2015年8月30日日曜日

C++ で C# の delegate っぽいものも作ってみた

こないだ event 風のものを作ってみたけど、もう一ひねりしたら delegate っぽいものも作れた。

template<typename T>
class Delegate;

template<typename R, typename... A>
class Delegate<R(A...)> {
private:
    std::function<R(A...)> mFunc;
public:
    Delegate(std::function<R(A...)> func) : mFunc(func) {}
    template<typename C, typename T>
    Delegate(C* pObj, T func) {
        mFunc = [=](A... args) { std::mem_fn(func)(pObj, args...); };
    }
public:
    void operator()(A... args) {
        mFunc(std::forward<A>(args)...); // 修正
    }
};

event と組み合わせるとこんな感じで使える。

class EventManager {
public:
    typedef Delegate<void(const std::string&)> TestEventHndlr;
    Event<TestEventHndlr> TestEvent;
    void InvokeTestEvent() {
        TestEvent("This is TestEvent. > ");
    }
};

class EventHandler {
public:
    void Hndlr(const std::string& msg) {
        std::cout << msg << "I'm a member function." << std::endl;
    }
};

void print_msg(const std::string& msg) {
    std::cout << msg << "I'm a static function." << std::endl;
}

int main()
{
    EventManager manager;
    EventHandler hndlr;
    manager.TestEvent += EventManager::TestEventHndlr([](const std::string& msg) {
        std::cout << msg << "I'm a lambda function." << std::endl;
    });
    manager.TestEvent += EventManager::TestEventHndlr(print_msg);
    manager.TestEvent += EventManager::TestEventHndlr(&hndlr, &EventHandler::Hndlr);
    manager.InvokeTestEvent();
    return 0;
}

※追記
unique_ptr を使おうとしたらハマったので Delegate を一部修正。
Event の方は複数のハンドラを登録できるようにしているのでもともと unique_ptr は適さない。
どうしても使いたければハンドラを一つしか登録できないイベントクラスを別途作る必要があるが、それよりはイベントハンドラ側にメモリ管理をさせないようにデータ構造を見直した方が健全かなぁ。。。

0 件のコメント:

コメントを投稿