Files
CPPClasses/Stopwatch.hpp
2019-03-29 12:49:15 -04:00

198 lines
7.3 KiB
C++

//MyClasses/Stopwatch.hpp
//Matthew Ellison
// Created: 10-30-2018
//Modified: 02-07-2019
//This file defines a class that can be used as a simple timer for programs
/*
Copyright (C) 2019 Matthew Ellison
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP
#include <chrono>
#include <sstream>
#include <iomanip>
namespace mee{
class Stopwatch{
public:
//Create an error class
class stopBeforeStart{}; //Used in stop() to check if you are trying to stop the stopwatch before starting it
class timeBeforeStart{}; //Used in getTime() to check if you are trying to get a time before you start it
class invalidTimeResolution{}; //Used to detect invalid time resolution in the getStr function
private:
std::chrono::high_resolution_clock::time_point startTime; //The time the start function was called
std::chrono::high_resolution_clock::time_point endTime; //The time the stop function was called
bool hasStarted; //A flag to show that start() has been called
bool hasStopped; //A flag to show that stop() has been called
enum TIME_RESOLUTION {NANOSECOND, MICROSECOND, MILLISECOND, SECOND, MINUTE, HOUR, DEFAULT};
//Return the duration in the default time period for the high_resolution_clock
double getTime(TIME_RESOLUTION timeResolution){
double timePassed = 0; //Holds the amount of time that has passed
//If the timer hasn't been stopped then record the time right now. This will simulate looping at the stopwatch while it is still running
//I put this at the beginning to get the timestamp at close to the calling of the function as possible
if(!hasStopped){
endTime = std::chrono::high_resolution_clock::now();
}
//If the timer hasn't been started throw an exception
if(!hasStarted){
throw timeBeforeStart();
}
//Decide what resolution to make the duration
if(timeResolution == HOUR){
std::chrono::duration<double, std::ratio<3600LL>> dur = (endTime - startTime);
timePassed = dur.count();
}
else if(timeResolution == MINUTE){
std::chrono::duration<double, std::ratio<60LL>> dur = (endTime - startTime);
timePassed = dur.count();
}
else if(timeResolution == SECOND){
std::chrono::duration<double> dur = (endTime - startTime);
timePassed = dur.count();
}
else if(timeResolution == MILLISECOND){
std::chrono::duration<double, std::milli> dur = (endTime - startTime);
timePassed = dur.count();
}
else if(timeResolution == MICROSECOND){
std::chrono::duration<double, std::micro> dur = (endTime - startTime);
timePassed = dur.count();
}
else if(timeResolution == NANOSECOND){
std::chrono::duration<double, std::nano> dur = (endTime - startTime);
timePassed = dur.count();
}
else if(timeResolution == DEFAULT){
std::chrono::high_resolution_clock::duration dur = (endTime - startTime);
timePassed = dur.count();
}
return timePassed;
}
public:
Stopwatch(){
//Make sure the flags are set to false to show nothing has been called yet
hasStarted = hasStopped = false;
startTime = endTime = std::chrono::high_resolution_clock::time_point(); //Set the times with a blank time
}
~Stopwatch(){
}
//Set the start time and flag and make sure the stop flag is unset
void start(){
hasStarted = true; //Show that the stopwatch has been started
hasStopped = false; //Show that the stopwatch is still running. Security in case the Stopwatch is used in multiple places
//Put this last to ensure that the time recorded is as close to the return time as possible
startTime = std::chrono::high_resolution_clock::now();
}
//Set the stop time and flag
void stop(){
//Put this first to ensure the time recorded is as close to the call time as possible
std::chrono::high_resolution_clock::time_point tempTime = std::chrono::high_resolution_clock::now();
//Make sure the stopwatch has started before you say it has stopped
if(hasStarted){
endTime = tempTime; //Set the end time appropriately
hasStopped = true; //Show that the stop function has been called
}
//If the stopwatch hadn't been started throw an exception
else{
throw stopBeforeStart();
}
}
//Return the duration in nanoseconds
double getNano(){
return getTime(NANOSECOND);
}
//Return the duration in microseconds
double getMicro(){
return getTime(MICROSECOND);
}
//Return the duration in milliseconds
double getMilli(){
return getTime(MILLISECOND);
}
//Return the duration in seconds
double getSeconds(){
return getTime(SECOND);
}
//Return the duration in minutes
double getMinutes(){
return getTime(MINUTE);
}
//Return the duration in hours
double getHours(){
return getTime(HOUR);
}
//Return the duration in the default resolution of high_resolution_clock
double getTime(){
return getTime(DEFAULT);
}
//Returns a string with the time at best resolution
std::string getStr(){
//Setup the variables
double tempTime = getTime(NANOSECOND); //Holds the
std::stringstream timeStr;
//Decide what time resolution would be best. Looking for the format of XXX.XXX
int timeRes = NANOSECOND;
for(timeRes = MICROSECOND;(timeRes < DEFAULT) && (tempTime >= 1000);++timeRes){
tempTime = getTime(static_cast<TIME_RESOLUTION>(timeRes));
}
--timeRes; //Take this variable back down to the right place. It has to go one too far to trigger the loop stop
//Check if the resolution is seconds and if there are more than 120 seconds
if((timeRes == SECOND) && (tempTime >= 120)){
++timeRes;
tempTime = getTime(static_cast<TIME_RESOLUTION>(timeRes));
}
//Check if the resolution is minutes and if there are more than 120 minutes
else if((timeRes == MINUTE) && (tempTime >= 120)){
++timeRes;
tempTime = getTime(static_cast<TIME_RESOLUTION>(timeRes));
}
//Put the number in the string
timeStr << std::fixed << std::setprecision(3) << tempTime << ' ';
//From the timeRes variable decide what word should go on the end of the string
switch(timeRes){
case HOUR: timeStr << "hours"; break;
case MINUTE: timeStr << "minutes"; break;
case SECOND: timeStr << "seconds"; break;
case MILLISECOND: timeStr << "milliseconds"; break;
case MICROSECOND: timeStr << "microseconds"; break;
case NANOSECOND: timeStr << "nanoseconds"; break;
case DEFAULT: timeStr << "time"; break;
default: throw invalidTimeResolution(); //This should never be hit with this code, but it's good to have all the bases covered
}
//Return the string
return timeStr.str();
}
//This function resets all the variables so that it can be run again
void reset(){
hasStarted = hasStopped = false; //Set the flags as though nothing has happened
endTime = startTime = std::chrono::high_resolution_clock::time_point(); //Set the times with a blank time
}
}; //end class Stopwatch
} //end namespace mee
#endif //end STOPWATCH_HPP