三次元日誌(hugo)

msgpack-rpcの関数登録の都合上、lambda関数から引数の型を得たい。 下記のような書き方をしたい。

template<typename R, typename A1, typename A2>
void register_func(const std::stiring &func_name
    , R(*handler)(A1, A2)) 
{
    // 関数登録 
}

int main() { 
    // errorになる 
    register_func("add", [](int a, int b)->int{ return a+b; });

    return 0;
} 

上記の書き方では普通の関数ポインタを受けることはできるのだが、 std::functionとlambdaは受けられない。

std::functionを受けるには下記のようにすることでできた。

template<typename R, typename A1, typename A2> 
void register_func(const std::string &func_name
    , std::function<R(A1, A2) handler) 
{}

残り、lambdaを受けられる記述方法を知りたいのだがどうしたらよいものか。 下記のような手はうまくいかなかった・・・

template<typename F, typename R, typename A1, typename A2> 
void add_handler(F handler, const std::string &method) 
{     
    std::function<R(A1, A2)> f(handler);     
    //add_handler 
}

こちらのサイトから http://d.hatena.ne.jp/osyo-manga/20121205/1354674180 decltypeを使ったらなんかできそうな感じがしたので粘っていたのだが、

目的そのものの記事を発見した。 http://stackoverflow.com/questions/6512019/can-we-get-the-type-of-a-lambda-argument

微妙にそのままではコンパイルが通らなかったので少し工夫したらうまくいった。

// ret template Ret helper0(Ret (F::*)(Rest...));
template<typename F, typename Ret, typename... Rest> 
Ret helper0(Ret (F::*)(Rest...) const);

// 1 template A1 helper1(Ret (F::*)(A1, Rest...));
template<typename  F, typename  Ret, typename A1, typename... Rest> 
A1 helper1(Ret (F::*)(A1, Rest...) const);

// 2 template A2 helper2(Ret (F::*)(A1, A2, Rest...));
template<typename F, typename Ret, typename A1, typename A2, typename... Rest> 
A2 helper2(Ret (F::*)(A1, A2, Rest...) const);

template<typename  F> 
void add_handler(F handler, const std::string &method) 
{
    typedef decltype(handler) functor; 
    typedef decltype(helper0(&functor::operator())) R; 
    typedef decltype(helper1(&functor::operator())) A1; 
    typedef decltype(helper2(&functor::operator())) A2;

    // register function...
}

F handlerで関数ポインタ、lambda、std::function等全部受けられるようになったらしくoverloadが不要になった。 decltypeなんかすごいな。