Files
CPPClasses/headers/mee/Stopwatch.hpp

211 lines
7.4 KiB
C++

//MyClasses/Stopwatch.hpp
//Matthew Ellison
// Created: 10-30-18
//Modified: 07-09-20
//This file defines a class that can be used as a simple timer for programs
/*
Copyright (C) 2020 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 MEE_STOPWATCH_HPP
#define MEE_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
return getStr(getTime(NANOSECOND)); //Holds the time that we are manipulating
}
//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
}
friend std::ostream& operator<<(std::ostream& out, Stopwatch& timer);
static std::string getStr(double nanoseconds){
//Setup the variables
double tempTime = nanoseconds; //Holds the time that we are manipulating
std::stringstream timeStr;
//Decide what time resolution would be best. Looking for the format of XXX.XXX
int timeRes = NANOSECOND;
for(timeRes = NANOSECOND;(timeRes < SECOND) && (tempTime >= 1000);++timeRes){
tempTime /= 1000;
}
//Check if the resolution is seconds and if there are more than 120 seconds
if((timeRes == SECOND) && (tempTime >= 120)){
++timeRes;
tempTime /= 60;
}
//Check if the resolution is minutes and if there are more than 120 minutes
if((timeRes == MINUTE) && (tempTime >= 120)){
++timeRes;
tempTime /= 60;
}
//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();
}
}; //end class Stopwatch
std::ostream& operator<<(std::ostream& out, Stopwatch& timer){
out << timer.getStr();
bool num = timer.hasStopped;
return out;
}
} //end namespace mee
#endif //end MEE_STOPWATCH_HPP