//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 . */ #ifndef STOPWATCH_HPP #define STOPWATCH_HPP #include #include #include 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> dur = (endTime - startTime); timePassed = dur.count(); } else if(timeResolution == MINUTE){ std::chrono::duration> dur = (endTime - startTime); timePassed = dur.count(); } else if(timeResolution == SECOND){ std::chrono::duration dur = (endTime - startTime); timePassed = dur.count(); } else if(timeResolution == MILLISECOND){ std::chrono::duration dur = (endTime - startTime); timePassed = dur.count(); } else if(timeResolution == MICROSECOND){ std::chrono::duration dur = (endTime - startTime); timePassed = dur.count(); } else if(timeResolution == NANOSECOND){ std::chrono::duration 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 STOPWATCH_HPP