c++実装delegate(一般関数とメンバー関数をサポート)

4531 ワード

最近c#のものを書き始めましたが、delegateのものが面白いです.
例えば以下のC#コード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpConsole
{
    public delegate int Test(string str);
    public class DelegaeFunc
    {
        public static int func1(string str)
        {
            Console.WriteLine("Func1" + str);
            return 1;
        }
        public static int func2(string str)
        {
            Console.WriteLine("Func2" + str);
            return 2;
        }
    }  
    class Program
    {

        public static int func3(string str)
        {
            Console.WriteLine("Func3" + str);
            return 3;
        }
        static void Main(string[] args)
        {
            Test printHandler = new Test(func3);
            printHandler += DelegaeFunc.func2;
            printHandler += DelegaeFunc.func1;
            int res = printHandler("hanliangwei");
            Console.WriteLine(res);
            Console.ReadKey();

        }
    }
}

プログラムはfunc 3,func 2,func 1を順次実行し,最終的な戻り値はfunc 1の戻り値である.イベントバインドに似ているという意味です.しかし、単純にこの方法では非静的メンバー関数をバインドできないはずですが、winformにEventHandlerをカプセル化してもいいようですか?初学で使ったばかりで,よく覚えていない.c++のシミュレーション実装を以下に示す.
c++シミュレーションのバージョンでは、通常の関数とメンバー関数をサポートできます.主にテンプレートで戻り値とクラスを指定します.関数パラメータにはvoid*タイプが1つしかありません.複数のカスタムパラメータに対して、私はいくつかのクラスを定義する以外に、良い方法を考えていないようです.しかもwin 32 sdkの中にはそうしているものが多いのではないかと思います.void*は実はたくさんのことをすることができます.ふと2つのvoid*を使うべきだと思ったら、もっと柔軟かもしれません.
具体的な実装:通常の関数については簡単ですが、関数ポインタを定義してリストを定義し、各関数ポインタを保存すればいいのですが、クラスについてはメンバー関数アドレスと呼び出したオブジェクトを保存する必要があります.同じクラスなので、Delegateのクラステンプレートにはテンプレートパラメータが1つしかありません.戻り値です.メンバー関数のタイプについては、追加関数にテンプレート関数を作成してこのクラスのオブジェクトのタイプを抽出する必要があります.listはどのように定義しますか?クラスオブジェクトとメンバー関数ポインタを格納するクラスを支援する必要があります.そのため、この支援クラスには2つのテンプレートパラメータがあるはずです.class_typeとreturn_typeですが、Delegateクラスに格納されているリストにclass_が与えられないという問題があります.typeはどうするのか、補助クラスにベースクラスを追加する必要があります.リストにベースクラスのポインタを格納し、追加するときに派生クラスのオブジェクトを追加し、マルチステートを利用してエージェントのメンバー関数を実行します.
(この方法はsigslotというライブラリを少しずつ参考にして、c++が書いたqtを模した信号スロットライブラリです)
コードは次のとおりです
#include 
#include
using namespace std;
template
class ClassProxyBase
{
public:
	virtual Return_Type Execute(void * p) { return 0; }
};
template
class ClassProxy:public ClassProxyBase
{
public:
	typedef Return_Type(ClassType::*PMemFunc)(void *p);
	ClassProxy(ClassType* pObj, PMemFunc memFunc) :m_pObj(pObj), m_lpfnMemfunc(memFunc) {}
	
	Return_Type Execute(void * p)
	{
		return (m_pObj->*m_lpfnMemfunc)(p);
	}
private:
	ClassType *m_pObj;
	PMemFunc m_lpfnMemfunc;
};
template
class Delegate
{
public:
	Delegate() {}
	~Delegate()
	{
		for (auto & lpfn : m_listMemberFuncPointers)
		{
			delete lpfn;
		}
	}
	typedef Return_Type(*PFUNC)(void *);
	Return_Type operator()(void * p)
	{
		Return_Type returnValue;
		for (auto & lpfn : m_listFunctionPointers)
		{
			returnValue = (*lpfn)(p);
		}
		for (auto & lpfn : m_listMemberFuncPointers)
		{
			returnValue = lpfn->Execute(p);
		}
		return returnValue;
	}
	Return_Type operator =(PFUNC ptr)
	{
		if (ptr == NULL)
			return;
		m_listFunctionPointers.clear();
		m_listFunctionPointers.push_back(ptr);
	}
	void operator += (PFUNC ptr)
	{
		m_listFunctionPointers.push_back(ptr);
	}
private:
	list m_listFunctionPointers;
	//             
public:
	template
	void Add(ClassType * pObj, Return_Type(ClassType::*pMemfunc)(void *p))
	{
		ClassProxy *obj = new ClassProxy(pObj, pMemfunc);
		m_listMemberFuncPointers.push_back(obj);
	}
private:
	list*>m_listMemberFuncPointers;

};
class Test
{
public:
	int test(void * p)
	{ 
		cout << "class Test:" << *(int*)p << endl;
		return 30;
	}
};
int func1(void *p) 
{
	int * ptr = (int *)p;
	cout << "func1:" << *(int*)p< delegateObj;
	delegateObj += func1;
	delegateObj += func2;

	Test obj;
	delegateObj.Add(&obj,&Test::test);
	int t = 10;
	int res = delegateObj((void*)&t);
	cout << "res:" << res << endl;

	system("pause");
}

実行結果は、func 1:10 func 2:20 class Test:20 res:30