What is a stopwatch?
My definition of a stopwatch is different than your implementation of a stopwatch. A stopwatch tracks the amount of time that has elapsed while the stopwatch is in an active state. The basic functionality is broken down into the following parts:
start() - If not running, begin a new interval (update state and start time).
stop() - If running, accumulate the current interval into the total elapsed time.
elapsed() - If running, accumulate the current interval into the total elapsed time and begin a new interval.
reset() - Set the stopwatch to its inactive state and wipe the total elapsed time.
In your implementation, you track the absolute time since the stopwatch construction or reset(). Your start() essentially acts as a reset() when stopped, thus making your stopwatch non-resumable. Consider the following example:
stopwatch<> sw(true);
std::this_thread::sleep_for(1s);
sw.stop();
std::this_thread::sleep_for(1s);
sw.start();
std::this_thread::sleep_for(1s);
auto elapsed = sw.elapsed();
What should be the value of elapsed?
Prefer non-member, non-friend functions
Functions should only be class members if they are required to be members (constructors, inherited functions, etc) or they need access to the internals to your class. Your measure() function is a candidate for being a free function and the implementation is roughly the same.
template <typename Callable, typename... Args>
auto measure(Callable&& func, Args... args) {
cr::stopwatch<> sw{ true };
func(std::forward<Args>(args)...);
return sw.elapsed();
}
You can extend it to take any timer object (countdown timer, named timers, etc) if you wanted that functionality.
Use const consistently
Your implemented elapsed() function does not mutate the state of your stopwatch and can be marked as const.
Avoid latent usage errors by properly ordering headers
#include <iostream>
#include <thread>
#include "stopwatch.h"
While this is a small project and your includes are minimal, I would still recommend you order your includes. From John Lakos' "Large Scale C++ Software Design":
Latent usage errors can be avoided by ensuring that the .h file of a
component parses by itself – without externally-provided declarations
or definitions... Including the .h file as the very first line of the
.c file ensures that no critical piece of information intrinsic to the
physical interface of the component is missing from the .h file (or,
if there is, that you will find out about it as soon as you try to
compile the .c file).
To illustrate a clean and ordered header collection:
#include "stopwatch.h" // Prototype/Interface header
#include "my_graphics/graphics.hpp" // Other headers in project
#include <boost/foreach.hpp> // Non-standard headers
#include <boost/spirit/include/qi.hpp>
#include <iostream> // Standard headers
#include <vector>
#include <windows.h> // System headers
Avoid "magic" constants
int main() {
using namespace std::chrono_literals;
constexpr auto short_delay = 100ms;
constexpr auto long_delay = 250ms;
...
}
Use symbolic constants as they offer semantic meaning to a statement, clearing up any confusion.