boost::asioのタイマーをIObservableでラップする。あとstd::chrono

C++/CLIをやってみる気になった。C++/CLIで公開されたクラスはWPFとかで使う予定。
そうなるとプロパティとかイベントを公開することになるのでいずれIPropertyChangedなどを実装する必要がある。
ならば最初からIObservableになっているとよいのではないか。
ということでやってみた。

<https://github.com/ousttrue/AsioCliSample>

```
boost::asio::steady_timer
 ↓
std::chrono::duration
 ↓
IObservable<TimeSpan>
```
という機能のC++/CLIによるクラスライブラリを作成した。

# プロジェクトを作る
VS2013で新規のC++/CLIのクラスライブラリプロジェクトAsioを作成。 
  [Templates] - [Visual C++] - [CRL] - [Class Library]
を選択した。

もうひとつこのライブラリの動作確認用のC#のコンソールプロジェクトSampleを作成。
  [Templates] - [Visual C#] - [Windows Desktop] - [Console Application]
で選択。

* ReferencesにAsioプロジェクトへの参照を追加
* NugetでReactive Extensions - Main Libraryを追加

プロジェクト設定の
* [x]Enable native code debugging
を有効にした。

## C++/CLIに必要なもうひとつのデバッグ設定
VisualStudioの[TOOLS] - [OPTIONS]から、[Debuggin] - [General]
* [ ]Require source files to exactly match the original version
のチェックを外した。
これをしないとbreakpointが●から○になってC++/CLIのデバッグができなかった。

# Sampleプロジェクト
ReactiveExtensionでIObservable<TimeSpan>を使う。

```c
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;


namespace Sample
{
    class Program
    {
        static void Main(string[] args)
        {
            {
                var service = new Asio.Service();
                var timer = new Asio.Timer(service);

                timer
                    .Take(3)
                    .Subscribe(elapsed =>
                {
                    Console.WriteLine("Expired: " + elapsed);
                }
                , () =>
                {
                    Console.WriteLine("Completed");
                    timer.Stop();
                }
                );

                timer.Start(TimeSpan.FromMilliseconds(330));
                service.Run();
                Console.WriteLine("Service ended");
            }

            // thread
            {
                var service = new Asio.Service();
                var timer = new Asio.Timer(service);

                timer
                    .Subscribe(elapsed =>
                    {
                        Console.WriteLine("Expired: " + elapsed);
                    }
                , ex =>
                    {
                        Console.WriteLine(ex);
                    }
                , () =>
                    {
                        Console.WriteLine("Completed");
                    }
                );

                Task.Run(() =>
                {
                    System.Threading.Thread.Sleep(1000);
                    timer.Stop();
                });

                timer.Start(TimeSpan.FromMilliseconds(330));
                service.Run();
                Console.WriteLine("Service ended");
            }
        }
    }
}
```

```
Expired: 00:00:00.3300000
Expired: 00:00:00.6600000
Expired: 00:00:00.9900000
Completed
Service ended
Expired: 00:00:00.3300000
Expired: 00:00:00.6600000
Expired: 00:00:00.9900000
System.Exception: スレッドの終了またはアプリケーションの要求によって、I/O 処理は中止されました。
Service ended
```

# Asioプロジェクト
## ref class Asio.Service
単純にboost::asio::io_serviceをラップ。

```cpp
	public ref class Service
	{
	public:
		Service()
			: ptr(new boost::asio::io_service)
		{}

		~Service()
		{
			this->!Service();
		}

		!Service()
		{
			delete ptr;
		}

		void Run()
		{
			ptr->run();
		}

	internal:
		boost::asio::io_service *ptr;
	};
```
C++の型を単純にラップする場合は上記のようにコンストラクタでnewして、デストラクタでファイナライズ、ファイナライザでdeleteするスタイルになるらしい。

* [Dispose Finalize パターンにおけるC#とC++/CLIでの違い](http://from.hatenablog.jp/entry/2014/09/13/034758)

internalがミソでこのクラスを使う側からは不可視で、C++/CLIの実装側からはpublicになる。
 
## ref class Asio.Timer
IObservableを実装する。
IObservableのインタフェースはSubscribeだけだが、
Unscriber, Unsubscribe, Expired(OnNextを呼ぶ), RaiseError, Completedは実質的に必須ぽい。
この辺よく知らずにReactiveExtensionsを使ってましたよ。

```c++

	template<class CLOCK> class TimerImpl;
	public ref class Timer : IObservable<TimeSpan>
	{
		List<IObserver<TimeSpan>^> ^m_observers;
		Queue<IObserver<TimeSpan>^> ^m_subscribeQueue;
		Queue<IObserver<TimeSpan>^> ^m_unsubscribeQueue;
		ref class Unsbscriber
		{
			Timer^ m_timer;
			IObserver<TimeSpan>^ m_observer;

		public:
			Unsbscriber(Timer^ timer, IObserver<TimeSpan> ^observer)
				: m_timer(timer), m_observer(observer)
			{
			}

			~Unsbscriber()
			{
				this->!Unsbscriber();
			}

			!Unsbscriber()
			{
				m_timer->Unsubscribe(m_observer);
			}
		};

	public:
		Timer(Service ^service);
		~Timer();
		!Timer();
		void Start(TimeSpan interval);
		void Stop();
		virtual IDisposable^ Subscribe(IObserver<TimeSpan> ^observer);

	internal:
		TimerImpl<std::chrono::steady_clock> *ptr;
		void Unsubscribe(IObserver<TimeSpan> ^observer);
		void RaiseError(String ^message);
		void Next(TimeSpan elapsed);
		void Completed();

	private:
		void UpdateObservers();
	};
}
```

timerのコールバックが別スレッドから飛んでくるため、m_observersに対するforeachとUnsubscribeによる削除がバッティングしてクラッシュする事態が発生。m_observablesはasioのタイマースレッドのみが操作するようにして、
m_subscribeQueue, m_unsubscribeQueueをロックしながら使うことで同期することにした。

## class TimerImpl(C++/CLIではなくC++の作法)
C++で記述するC++とC++/CLIが相互にやりとりするクラス(ややこしい)。C++なのでref classに対する参照[^]は使えないが、

```cpp
  msclr::gcroot<Timer^>
```

というtemplateで例外的にマネージドクラスへの参照をC++に持ち込めるらしい。
C#のstructを表すval class(今回のTimeSpan)はそのまま値渡しらしい。
C++的にはconstリファレンスにしたいところだがそのあたりはわからなかった。

```cpp
	template<class CLOCK>
	class TimerImpl
	{
		boost::asio::basic_waitable_timer<CLOCK> m_timer;
		typename CLOCK::time_point m_start;
		volatile bool m_stop;

	public:
		TimerImpl(boost::asio::io_service &service)
		: m_timer(service), m_stop(true)
		{
		}

		~TimerImpl()
		{
			Stop();
		}

		void Stop()
		{
			m_stop = true;
			m_timer.cancel();
		}

		void Start(msclr::gcroot<Timer^> refClass, TimeSpan span)
		{
			m_stop = false;

			double totalMilliSeconds = span.TotalMilliseconds;
			auto interval = std::chrono::milliseconds((int)totalMilliSeconds);
			m_start = CLOCK::now();
			m_timer.expires_from_now(interval);
			Start(refClass, interval);
		}

		void Start(msclr::gcroot<Timer^> refClass, const std::chrono::microseconds &interval)
		{
			auto self = this;
			auto onTimer = [self, refClass, interval](const boost::system::error_code& ec)
			{
				if (ec) {
					// error;
					refClass->RaiseError(gcnew String(ec.message().c_str()));
					return;
				}

				auto expired = self->m_timer.expires_at();
				auto elapsed = expired - self->m_start;
#if 0
				auto tick = std::chrono::duration_cast <
					std::chrono::duration < int64_t, std::ratio<INTMAX_C(1), INTMAX_C(10000000)> >
				> (elapsed);
				refClass->Expired(TimeSpan(tick.count()));
#else
				auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
				refClass->Expired(TimeSpan(0, 0, 0, 0, milliseconds.count()));
#endif

				// next
				if (!self->m_stop){
					auto from = self->m_timer.expires_at();
					self->m_timer.expires_at(from + interval);

					self->Start(refClass, interval);
				}
			};
			m_timer.async_wait(onTimer);
		}
	};
```

# std::chronoに関するメモ
[C++11の時間ライブラリは美しさを追求したあまり、かえって使いにくくなっているのではないか](http://cpplover.blogspot.jp/2012/05/c11_24.html)にあるように、たしかにchronoちょっと分かりにくい。
だが使っていく気なので整理しておく。

## duration
[Boost.Chrono User's Guide](http://www.boost.org/doc/libs/1_57_0/doc/html/chrono/users_guide.html#chrono.users_guide.tutorial.duration)

chronoの分かりにくさはduration。
[duration](http://ja.cppreference.com/w/cpp/chrono/duration)は時の長さを表現するテンプレート。
それのtemplateとして6つの便利typedefがある。

```cpp
template<
    class Rep, 
    class Period = std::ratio<1> 
> class duration;

// convenience typedefs
typedef duration<boost::int_least64_t, nano> nanoseconds;    // at least 64 bits needed
typedef duration<boost::int_least64_t, micro> microseconds;  // at least 55 bits needed
typedef duration<boost::int_least64_t, milli> milliseconds;  // at least 45 bits needed
typedef duration<boost::int_least64_t> seconds;              // at least 35 bits needed
typedef duration<boost::int_least32_t, ratio< 60> > minutes; // at least 29 bits needed
typedef duration<boost::int_least32_t, ratio<3600> > hours;  // at least 23 bits needed
```
こいつらはtemplate引数が違っても加減算等演算ができるぞと。

templateの第１引数は数値を格納する型で、第２引数は有理数[ratio](http://ja.cppreference.com/w/cpp/numeric/ratio/ratio)。

```cpp
template< 
    std::intmax_t Num, // 分子
    std::intmax_t Denom = 1 // 分母
> class ratio;

nano	std::ratio<1, 1000000000>
micro	std::ratio<1, 1000000>
milli	std::ratio<1, 1000>
```

これ30FPSの1000/33とかに使うとよさげだな。
異なる単位の時を相互に演算したり変換したりできるための型それがdurationらしい。

## time_point
あと時間を表すdurationに対して時点をあらわす[time_point](http://www.boost.org/doc/libs/1_57_0/doc/html/chrono/users_guide.html#chrono.users_guide.tutorial.time_point)がある。
基準時間(epoctimeとか?)からの絶対時間を表す概念か。durationは相対的な時間というか。
そのままでは使いづらいのでtime_tに変換するとかが要る。

boost.asioのtimerのexpires_at()で得られるのはtime_pointとなっている。
time_point - time_pointからdurationを得られるので下記のようにした。

```cpp
auto timer=boost::asio::steady_timer(io_service);
auto start = timer::clock_type::now(); // 基準時間(time_point)

// 経過時間をdurationで得る
auto duration=timer.expires_at() - start;
```

## C#のTimeSpanとstd::chronoを変換する例。
durationのコンストラクタが数値をセットし、count()で数値を取得できる。

### chrono -> TimeSpan

```c++
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
auto ts=TimeSpan(0, 0, 0, 0, milliseconds.count());
```

### TimeSpan -> chrono

```cpp
double totalMilliSeconds = span.TotalMilliseconds;
auto interval = std::chrono::milliseconds((int)totalMilliSeconds);
```

# Boost.Asioのchrono版のタイマー
<http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/tutorial/tuttimer4.html>
を参考に書いてみた。

等間隔にtimerを発行するには

```cpp
timer.expires_at(timeer.expires_at()+interval)
```
とする。

初回だけ

```cpp
timer.expires_from_now(interval)
```
とする。