A basic extensible execution timer for C++

All of us need to time our projects and part of it. While recently getting re-acquainted with C++ through C++11, I too need to do it many times. I started with a using std::chrono facilities, but soon figured out that changing code to track different time of operations was getting tiring and boring. Some portions makes meaning only when expressed in seconds and some would never reach second limits and we are better off tracing them in microseconds. Then again, microseconds may go into 7-8 digits and we woul have tpough time comparing two values if log file has multiple outputs. I thus decided to revamp my timerclass and wrote its signature as follows. Its usage is shown in the blog here.

////////////////////////////////////////////////////////////////////////////////
class PrintTimer
{
public:
virtual std::wstring
	to_wstring(std::chrono::hours::rep value, std::chrono::hours dummy) = 0;
virtual std::wstring
	to_wstring(std::chrono::minutes::rep value, std::chrono::minutes dummy) = 0;
virtual std::wstring
	to_wstring(std::chrono::seconds::rep value, std::chrono::seconds dummy) = 0;
virtual std::wstring
	to_wstring(std::chrono::microseconds::rep value, std::chrono::microseconds dummy) = 0;
virtual std::wstring
	to_wstring(std::chrono::milliseconds::rep value, std::chrono::milliseconds dummy) = 0;
};

template<typename clock_type, typename dur>
class ExecutionTimer
{
private:

typename clock_type::time_point m_start;
typename clock_type::time_point m_end;
std::shared_ptr<PrintTimer>		m_printer;

public:

ExecutionTimer() : ExecutionTimer(nullptr) {};
ExecutionTimer(std::shared_ptr<PrintTimer> const& printer)
{
	m_start = clock_type::now();;
	m_end = clock_type::now();;
	m_printer = printer;
};

void set_start(){
	m_start = clock_type::now();
}
void set_start(typename clock_type::time_point start){
	m_start = start;
}
void set_end(){
	m_end = clock_type::now();
}
void set_end(typename clock_type::time_point end){
	m_end = end;
}
void set_printer(std::shared_ptr<PrintTimer> const& printer){
	m_printer = printer;
}

typename dur::rep elapsed()
{
	return std::chrono::duration_cast<dur>(m_end - m_start).count();
};
std::wstring to_wstring(){
	return to_wstring(m_printer);
}
std::wstring to_wstring(std::shared_ptr<PrintTimer> const& printer)
{
	if (printer){
		auto ret = printer->to_wstring(elapsed(), 
			std::chrono::duration_cast<dur>(std::chrono::seconds(1)));
		return ret;
	}
	else{
		return L"Printing facilities not available";
	}	
}
};

PrintTimer is the interface that will be used to display output in nice way when its big. You will see it soon. ExecutionTimer has to be declared and defined fully in a header file for it to be available everywhere as it is a template class for std::chrono facilities. The whole shebang here will not use C library functions for timer display which was the main agenda. Everything is done using STL methods. Purpose of having PrintTimer as interface is very simple, it allows these big functions to be written anywhere and in customizable way. If user does not need it, then can simply call elapsed from ExecutionTimer and get the abstract counter and then use some other display scheme.

dummy parameters at the end of each to_wstring method is needed to ensure function overloading since multiple ***::rep values coalesces to same kind.

My implementation for PrintTimer interface is as follows.

////////////////////////////////////////////////////////////////////////////////
class PrintExecutionTimer :public PrintTimer
{
private:
const std::wstring SS = std::wstring(L" ");

public:
virtual std::wstring
	to_wstring(std::chrono::hours::rep value, std::chrono::hours dummy);
virtual std::wstring
	to_wstring(std::chrono::minutes::rep value, std::chrono::minutes dummy);
virtual std::wstring
	to_wstring(std::chrono::seconds::rep value, std::chrono::seconds dummy);
virtual std::wstring
	to_wstring(std::chrono::microseconds::rep value, std::chrono::microseconds dummy);
virtual std::wstring
	to_wstring(std::chrono::milliseconds::rep value, std::chrono::milliseconds dummy);
};

////////////////////////////////////////////////////////////////////////////////
std::wstring
PrintExecutionTimer::to_wstring(std::chrono::hours::rep value, std::chrono::hours dummy)
{
	using namespace std::chrono;
	auto ret = std::to_wstring(value) + SS + L"hours";
	return ret;
}
std::wstring
PrintExecutionTimer::to_wstring(std::chrono::minutes::rep value, std::chrono::minutes dummy)
{
	using namespace std::chrono;
	std::wstring ret{ L"" };
	auto cmp_unit = minutes(60).count();
	auto dummy_to_pass = duration_cast<hours>(dummy);

	if (value > cmp_unit){
		ret = to_wstring(hours::rep(value / cmp_unit), dummy_to_pass);
		ret += SS + std::to_wstring(value % cmp_unit) + SS + L"minutes";
	}
	else if (value == cmp_unit){
		ret = to_wstring(hours::rep(1), dummy_to_pass);
	}
	else{
		ret = std::to_wstring(value) + SS + L"minutes";
	}
	return ret;
}

std::wstring
PrintExecutionTimer::to_wstring(std::chrono::seconds::rep value, std::chrono::seconds dummy)
{
	using namespace std::chrono;
	std::wstring ret{ L"" };
	auto cmp_unit = seconds(60).count();
	auto dummy_to_pass = duration_cast<minutes>(dummy);

	if (value > cmp_unit){
		ret = to_wstring(minutes::rep(value / cmp_unit), dummy_to_pass);
		ret += SS + std::to_wstring(value % cmp_unit) + SS + L"seconds";
	}
	else if (value == cmp_unit){
		ret = to_wstring(minutes::rep(1), dummy_to_pass);
	}
	else{
		ret = std::to_wstring(value) + SS + L"seconds";
	}
	return ret;
}

std::wstring
PrintExecutionTimer::to_wstring(std::chrono::milliseconds::rep value, std::chrono::milliseconds dummy)
{
	using namespace std::chrono;
	std::wstring ret{ L"" };
	auto cmp_unit = milliseconds(1000).count();
	auto dummy_to_pass = duration_cast<seconds>(dummy);

	if (value > cmp_unit){
		ret = to_wstring(seconds::rep(value / cmp_unit), dummy_to_pass);
		ret += SS + std::to_wstring(value % cmp_unit) + SS + L"milliseconds";
	}
	else if (value == cmp_unit){
		ret = to_wstring(seconds::rep(1), dummy_to_pass);
	}
	else{
		ret = std::to_wstring(value) + SS + L"milliseconds";
	}
	return ret;
}

std::wstring
PrintExecutionTimer::to_wstring(std::chrono::microseconds::rep value, std::chrono::microseconds dummy)
{
	using namespace std::chrono;
	std::wstring ret{ L"" };
	auto cmp_unit = microseconds(1000).count();
	auto dummy_to_pass = duration_cast<milliseconds>(dummy);

	if (value > cmp_unit){
		ret = to_wstring(milliseconds::rep(value / cmp_unit), dummy_to_pass);
		ret += SS + std::to_wstring(value % cmp_unit) + SS + L"microseconds";
	}
	else if (value == cmp_unit){
		ret = to_wstring(milliseconds::rep(1), dummy_to_pass);
	}
	else{
		ret = std::to_wstring(value) + SS + L"microseconds";
	}
	return ret;
}

You may notice that they reference each other. If needed, you can decouple them from each other.

Below is a use case as to how to use it.

void timer_test()
{
    using namespace std::chrono;
	using namespace std;

	ExecutionTimer<high_resolution_clock, microseconds> exe_timer_micro;
	ExecutionTimer<high_resolution_clock, milliseconds> exe_timer_milli(new PrintExecutionTimer());
	exe_timer_micro.set_start();
	exe_timer_milli.set_start();

	for (size_t i = 0; i < (std::numeric_limits<unsigned int>::max()); i++)
	{
		if (0 == i % (32 * std::numeric_limits<unsigned short>::max())){
			wcout << L".";
			exe_timer_micro.set_end(high_resolution_clock::now());
			exe_timer_milli.set_end(high_resolution_clock::now());
		}
	}
	wcout << endl;
	wcout << to_wstring(exe_timer_micro.elapsed()) << L" microseconds" << endl;
	wcout << exe_timer_micro.to_wstring() << endl;

	wcout << to_wstring(exe_timer_milli.elapsed()) << L" milliseconds" << endl;
	wcout << exe_timer_milli.to_wstring() << endl;

    return;
}