イベントハンドラを登録したスレッドでデリゲートしてねっていうクラス


せっかく下にお勧めしたので、ネタ的に作成してみた。

ヘッダーにwindows.hを記述したくなかったのと

テンプレート名のSignatureに依存したくなかったので

TypeErasureするためにbindしまくってみた。invoke_helperはpimplっぽく?してみたつもりで

呼び出しの引数は参照つけてるけど・・その辺はどうしたらいいのか謎。->追記へ

あとは、ネーミングセンス爆発しろみたいになった。ちょっと読みにくいけど許してください。

//synchronized_event_delegator.h

#pragma once

#include <utility>
#include <vector>

#include <boost/function.hpp>
#include <boost/bind.hpp>

namespace umyu
{
	struct invoke_helper
	{
		typedef boost::function<void ()> callback_handler_type;

		static void* get_thread_id();
		static void invoke(void* thread_id,callback_handler_type* handler);
	};

	template <typename Signature>
	struct synchronized_event_delegator
	{
		typedef Signature delegate_function_type;
		typedef boost::function<delegate_function_type> delegate_handler_type;

		synchronized_event_delegator& operator+=(const synchronized_event_delegator& delegator)
		{
			std::copy(delegator.handlers_.begin()
				,delegator.handlers_.end(),std::back_inserter(handlers_));
			return *this;
		}

		synchronized_event_delegator& operator+=(const delegate_handler_type& handler)
		{
			handlers_.push_back(std::make_pair(invoke_helper::get_thread_id(),handler));
			return *this;
		}

		template <typename Sig>
		synchronized_event_delegator& operator+=(const boost::function<Sig>& handler)
		{
			handlers_.push_back(std::make_pair(invoke_helper::get_thread_id(),handler));
			return *this;
		}

		void operator()() const
		{
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr=handlers_.begin();
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr_end=handlers_.end();
			for(itr;itr!=itr_end;++itr)
				invoke_helper::invoke(itr->first
					,new invoke_helper::callback_handler_type(itr->second));
		}

		template <typename Sender>
		void operator()(Sender& sender) const
		{
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr=handlers_.begin();
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr_end=handlers_.end();
			for(itr;itr!=itr_end;++itr)
				invoke_helper::invoke(itr->first
					,new invoke_helper::callback_handler_type(
					boost::bind(itr->second,boost::ref(sender))));
		}

		template <typename Sender,typename Arg1>
		void operator()(Sender& sender,Arg1& arg1) const
		{
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr=handlers_.begin();
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr_end=handlers_.end();
			for(itr;itr!=itr_end;++itr)
				invoke_helper::invoke(itr->first
					,new invoke_helper::callback_handler_type(
					boost::bind(itr->second,boost::ref(sender),boost::ref(arg1))));
		}

		void reset()
		{
			handlers_.clear();
		}

		std::vector<std::pair<void*,delegate_handler_type> > handlers_;
	};
}
//synchronized_event_delegator.cpp

#include "synchronized_event_delegator.h"

#define _WIN32_WINNT 0x0501
#include <windows.h>

namespace umyu
{
	VOID APIENTRY invoke_helper_static_handler(ULONG_PTR handler)
	{
		invoke_helper::callback_handler_type* invoke_handler=(invoke_helper::callback_handler_type*)handler;
		(*invoke_handler)();
		delete invoke_handler;
	}
	void* invoke_helper::get_thread_id()
	{
		HANDLE regist_thread;
		DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),GetCurrentProcess()
			,&regist_thread,0,FALSE,DUPLICATE_SAME_ACCESS);
		return (void*)regist_thread;
	}
	void invoke_helper::invoke(void* thread_id,callback_handler_type* handler)
	{
		QueueUserAPC(&invoke_helper_static_handler,(HANDLE)thread_id,(ULONG_PTR)handler);
	}

}

メインスレッドはSleepEx(0,TRUE)でプーリングを忘れずに。


追記
上記だと、呼び出し方によってスライシングしたり値でコピーされたりしますね。
以下のように参照もboost::refもはずして、呼び出す側でboost:refするように変更しました。

		template <typename Sender,typename Arg1>
		void operator()(Sender sender,Arg1 arg1) const
		{
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr=handlers_.begin();
			std::vector<std::pair<void*,delegate_handler_type> >::const_iterator itr_end=handlers_.end();
			for(itr;itr!=itr_end;++itr)
				invoke_helper::invoke(itr->first
					,new invoke_helper::callback_handler_type(
					boost::bind(itr->second,sender,arg1)));
		}

		...
		synchronized_event_delegator<void (file&,unsigned long)> on_close;
		...
		on_close(boost::ref(*this),error_code);