C++ 処理時間計測classを作ってみる

#はじめに
個人的にたまに使うのでまとめとく
#処理時間の計測
`std::chrono`を使ったシンプルな計測は以下のようにすることができます

```cpp:main.cpp
#include<iostream>
#include<chrono>

int main()
{

	std::chrono::system_clock::time_point start,end;
	start = std::chrono::system_clock::now();

	/*
	処理
	*/
	end = std::chrono::system_clock::now();

	auto elapsed = std::chrono::duration_cast< std::chrono::milliseconds >(end - start).count();
	std::cout << elapsed <<"ms"<< std::endl;
	return 0;
}
``` 

`std::chrono::milliseconds`の所を変えれば時間単位を変えれます

| 型 | 単位 |
|:-:|:-:|
|  nanoseconds |  ナノ秒 |
|  microseconds |  マイクロ秒 |
| milliseconds  |  ミリ秒 |
|  seconds | 秒  |
|  minutes |  分 |
|  hours |  時 |

#classにしてみる
先ほどの処理を毎回書くのは大変なのでclassにしてあげる

```cpp:ProcessingTime.hpp


	class  ProcessingTime
	{

	public:
		ProcessingTime(const std::string& name = "Process", bool start = true) :
			m_name(name),
			m_isActive(start)
		{
			if (start)
			{
				this->restart();
			}
		}
		~ProcessingTime()
		{
			this->stop();
		}

		///<summary>
		///計測のリスタート
		///</summary>
		void restart()&
		{
			m_start = std::chrono::system_clock::now();
			m_isActive = true;
		}
		///<summary>
		///計測を終了し出力
		///</summary>
		void stop()&
		{
			if (!m_isActive)
				return;
			const auto end = std::chrono::system_clock::now();
	        const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds >(end - m_start).count();
	        std::cout << elapsed <<"ms"<< std::endl;

			m_isActive = false;
		}
	private:

		std::string m_name;
		std::chrono::system_clock::time_point m_start;
		bool m_isActive;

	};

```

**コンストラクタで計測開始**、**デストラクタで計測終了**を呼べるようにしてます

使い方は計測したい箇所をスコープで括って最初にProcessingTimeのインスタンスを生成するだけ

```cpp:main.cpp
#include"ProcessingTime.hpp"


int main()
{
	{
		ProcessingTime pt("Test");

		/*

		処理

		*/
	
	}
	return 0
}


```

出力

```
Test : 0 ms
```

#templateで時間単位を変更可能にしてみる

`std::chrono::milliseconds`以外の時間単位でも出力できるようにしてみます

```cpp:ProcessingTime.hpp

	template<class T>
	struct is_duration :std::false_type
	{};
	template<class Rep, class Period>
	struct is_duration<std::chrono::duration<Rep, Period>> :std::true_type
	{};

	template<class Duration>
	class ProcessingTime
	{
		static_assert(is_duration<Duration>::value, "tempate parameter requires std::chrono::duration");

	public:

         //省略

		void stop()&
		{
			if (!m_isActive)
				return;
			const auto end = std::chrono::system_clock::now();
			const auto elapsed = std::chrono::duration_cast<Duration>(end - m_start).count();
			std::cout << m_name << " : " << elapsed << " " << this->getUnit() << std::endl;

			m_isActive = false;
		}
	private:

		std::string getUnit()const
		{
			if constexpr(std::is_same_v<Duration, std::chrono::nanoseconds>)return "ns";
			if constexpr(std::is_same_v<Duration, std::chrono::microseconds>)return "us";
			if constexpr(std::is_same_v<Duration, std::chrono::milliseconds>)return "ms";
			if constexpr(std::is_same_v<Duration, std::chrono::seconds>)return "s";
			if constexpr(std::is_same_v<Duration, std::chrono::minutes>)return "min";
			if constexpr(std::is_same_v<Duration, std::chrono::hours>)return "h";

			return " ";
		}
		std::string m_name;
		std::chrono::system_clock::time_point m_start;
		bool m_isActive;

	};
```

メタ関数`is_duration`を作って`std::chrono::duration`以外の型はアサートするようにしてみました

また、それぞれの型の時間単位をC++17から導入された`if constexpr`でコンパイル時に返り値を確定できるようにしてみました


#少し修正
クラステンプレートの場合、template引数のデフォルトを用意してもProcessingTime<>ってかかなくてはいけないのが気に入らなかったので関数を通して生成するようにしてみる

```cpp:ProcessingTime.hpp
namespace detail
{
	template<class T>
	struct is_duration :std::false_type
	{};
	template<class Rep, class Period>
	struct is_duration<std::chrono::duration<Rep, Period>> :std::true_type
	{};

	///<summary>
	///処理計測実装
	///</summary>
	template<class Duration>
	class ProcessingTime_impl
	{
		static_assert(is_duration<Duration>::value, "tempate parameter requires std::chrono::duration");

	public:
		ProcessingTime_impl(const std::string& name = "Process", bool start = true) :
			m_name(name),
			m_isActive(start)
		{
			if (start)
			{
				this->restart();
			}
		}
		~ProcessingTime_impl()
		{
			this->stop();
		}

		///<summary>
		///計測のリスタート
		///</summary>
		void restart()&
		{
			m_start = std::chrono::system_clock::now();
			m_isActive = true;
		}
		///<summary>
		///計測を終了し出力
		///</summary>
		void stop()&
		{
			if (!m_isActive)
				return;
			const auto end = std::chrono::system_clock::now();
			const auto elapsed = std::chrono::duration_cast<Duration>(end - m_start).count();
			std::cout << m_name << " : " << elapsed << " " << this->getUnit() << std::endl;

			m_isActive = false;
		}
	private:

		std::string getUnit()const
		{
			if constexpr(std::is_same_v<Duration, std::chrono::nanoseconds>)return "ns";
			if constexpr(std::is_same_v<Duration, std::chrono::microseconds>)return "us";
			if constexpr(std::is_same_v<Duration, std::chrono::milliseconds>)return "ms";
			if constexpr(std::is_same_v<Duration, std::chrono::seconds>)return "s";
			if constexpr(std::is_same_v<Duration, std::chrono::minutes>)return "min";
			if constexpr(std::is_same_v<Duration, std::chrono::hours>)return "h";

			return " ";
		}
		std::string m_name;
		std::chrono::system_clock::time_point m_start;
		bool m_isActive;

	};

}

///<summary>
///処理時間の計測
///</summary>
///<param name="name">
///処理名
///</param>
///<param name="start">
///すぐに計測を開始するか? true=>開始
///</param>
template<
	class Duration = std::chrono::milliseconds,
	std::enable_if_t<detail::is_duration<Duration>::value>* = nullptr
>
[[nodiscard]] detail::ProcessingTime_impl<Duration> ProcessingTime(const std::string& name = "Process", bool start = true)
{
	return detail::ProcessingTime_impl<Duration>(name, start);
}

```

せっかくなので`[[nodiscard]]`属性で帰り値を破棄した場合に警告をだすようにしてみたが msvcはpod型しか警告がでないのかな？？:thinking::thinking:(でなかった)

使い方は最終的に以下のようになりました。

```cpp:main.cpp
#include"ProcessingTime.h"

int main()
{
	{
		auto p = ProcessingTime<std::chrono::nanoseconds>("nanosec");

		/*
		処理
		*/

	}
	{
		auto p = ProcessingTime("millisec");

		/*
		処理
		*/

	}
	return 0;
}
```