I'm not sure what the best way is when dealing with users calling start()
multiple times or stop()
before calling start()
, I decided to silently return. I lean towards not using exceptions for these situations. Is there a good argument to be made for exceptions? Is there a different or better mechanism?
Looking for:
- Suggestions on missing operations (Provide
bool has_started() noexcept;
?). - Handling special cases (calling start on a stopwatch that has already started, etc.)
- General suggestions: Better idioms, code clarity, etc.
#ifndef CR_STOPWATCH_H
#define CR_STOPWATCH_H
#include <chrono>
//#include <stdexcept>
namespace cr
{
/*
DECLARATION
*/
template
<
typename TimeUnit = std::chrono::milliseconds,
typename Clock = std::chrono::high_resolution_clock
>
class stopwatch
{
public:
explicit stopwatch( bool const = false ) noexcept;
void start() noexcept;
void stop() noexcept;
TimeUnit elapsed() noexcept;
void reset() noexcept;
template <typename F, typename... FArgs>
inline static TimeUnit measure( F&&, FArgs&&... );
private:
bool stopped_;
std::chrono::time_point<Clock> stop_;
bool started_;
std::chrono::time_point<Clock> start_;
};
/*
IMPLEMENTATION
*/
template <typename TimeUnit, typename Clock>
inline cr::stopwatch<TimeUnit, Clock>::stopwatch( bool const start_stopwatch = false ) noexcept :
stopped_{ false },
stop_{ TimeUnit{ 0 } },
started_{ start_stopwatch },
start_{ start_stopwatch ? Clock::now() : stop_ }
{
}
template <typename TimeUnit, typename Clock>
inline void cr::stopwatch<TimeUnit, Clock>::start() noexcept
{
if ( started_ )
{
return;
//throw std::logic_error( "stopwatch: already called start()" );
}
start_ = Clock::now();
started_ = true;
}
template <typename TimeUnit, typename Clock>
inline void cr::stopwatch<TimeUnit, Clock>::stop() noexcept
{
if ( !started_ )
{
return;
//throw std::logic_error( "stopwatch: called stop() before start()" );
}
stop_ = Clock::now();
stopped_ = true;
}
template <typename TimeUnit, typename Clock>
inline TimeUnit cr::stopwatch<TimeUnit, Clock>::elapsed() noexcept
{
if ( !started_ )
{
return TimeUnit{ 0 };
}
if ( stopped_ )
{
return std::chrono::duration_cast<TimeUnit>( stop_ - start_ );
}
return std::chrono::duration_cast<TimeUnit>( Clock::now() - start_ );
}
template <typename TimeUnit, typename Clock>
inline void
cr::stopwatch<TimeUnit, Clock>::reset() noexcept
{
started_ = false;
stop_ = start_;
stopped_ = false;
}
template <typename TimeUnit, typename Clock>
template <typename F, typename... FArgs>
inline TimeUnit cr::stopwatch<TimeUnit, Clock>::measure( F&& f, FArgs&&... f_args )
{
auto start_time = Clock::now();
std::forward<F>( f )( std::forward<FArgs>( f_args )... );
auto stop_time = Clock::now();
return std::chrono::duration_cast<TimeUnit>( stop_time - start_time );
}
}
#endif
Sample tests:
#include <iostream>
#include <thread>
#include "stopwatch.h"
void f()
{
std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
}
int main()
{
using namespace std::chrono_literals;
cr::stopwatch<std::chrono::microseconds> sw{ true };
std::this_thread::sleep_for( 100ms );
std::cout << sw.elapsed().count() << '\n';
std::this_thread::sleep_for( 100ms );
sw.stop();
std::cout << sw.elapsed().count() << '\n';
std::cout << cr::stopwatch<>::measure( f ).count() << '\n';
}
TimeUnit
doesn't need to be a class template parameter. It could as well be a template parameter only onmeasure
andelapsed
. Orelapsed
could return a duration rather than a count. \$\endgroup\$ – Kerrek SB Dec 20 '15 at 13:51stop_ - start_
, rather than the thing you return now (the cast + count). \$\endgroup\$ – Kerrek SB Dec 20 '15 at 19:18