2012-02-21 8 views
3

まず、小さな背景:後で呼び出されるコールバックをバインドします。これにより、ログを通じた制御フローに従うことが難しくなります。これを助けるために、我々はシステムを通過するときに要求に従うことを可能にする「ログコンテキスト」を使用します。現在のコンテキストを静的関数log_context::get_currentでコピーし、静的関数log_context::set_currentで復元することができます。これにより、コールバックをワーカー・キューにポストするたびに、繰り返しコードが多数発生します。C++:std :: bindの引数を取得します。

私は現在log_contextを保存し、呼び出されたときにそれを復元するstd::bindのドロップイン置換である関数を作りたいと思います。しかし、私はそれを書くのにいくつかの問題を抱えています。

今、関数は次のようになります。それは作品

template <typename TResult, 
      typename... TFuncArgs, 
      typename Func, 
      typename... TProvidedArgs 
     > 
std::function<TResult (TFuncArgs...)> 
bind_with_context(const std::function<TResult (TFuncArgs...)>& throwaway, 
        Func func, 
        TProvidedArgs&&... args 
       ) 
{ 
    log_context cxt = log_context::get_current(); 
    auto bound = std::bind(func, std::forward<TProvidedArgs>(args)...); 
    auto lambda = [cxt, bound] (TFuncArgs... args) -> TResult 
        { 
         log_context::set_current(cxt); 
         return bound(args...); 
        }; 
    return lambda; 
} 

が、問題は、使用量がありません本当の理由のために関数型を渡す必要がありますですが(それはさておき、私は何をして見つける方法です)TFuncArgsのために使用します。

bind_with_context(func_type(), &some_class::some_func, ptr, _1, "Bob", _2); 

だから、ないかなりのドロップイン置換。コンパイル時にがこの情報を知っているべきであると思われます。私は方法を理解できません。ほぼです。です。 関数の型を渡す必要性をどのように排除できますか?


私の最初の考えはそうのような関数に変換からの結合を分割することでした:

template <typename Func> 
struct context_binder 
{ 
public: 
    context_binder(const Func& func) : 
      func(func) 
    { } 

    // Use the casting operator to figure out what we're looking for: 
    template <typename TReturn, typename... TFuncArgs> 
    operator std::function<TReturn (TFuncArgs...)>() const 
    { 
     log_context cxt = log_context::get_current(); 
     auto lambda = [func, cxt] (TFuncArgs... args) -> TReturn 
         { 
          log_context::set_current(cxt); 
          return func(std::forward<TFuncArgs>(args)...); 
         }; 
     return lambda; 
    } 

private: 
    Func func; 
}; 

template <typename F, typename... TArgs> 
auto bind_with_context(F f, TArgs&&... args) 
     -> context_binder<decltype(std::bind(f, std::forward<TArgs>(args)...))> 
{ 
    return std::bind(f, std::forward<TArgs>(args)...); 
} 

問題は、キャスト(operator std::function<TReturn (TFuncArgs...)>() const)は(int foo(int x, int y, int z)与えられた)と呼ばれることはありませんということです。

std::function<int (int)> f = bind_with_context(&foo, 4, 5, _1); 

理由はfunctionのコンストラクタからoperator()をつかむしようとしていることですcontext_binder(それがない場合でも)。

In file included from scratch.cpp:1:0: 
/usr/local/include/gcc-4.6.2/functional: In static member function ‘static _Res std::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_invoke(const std::_Any_data&, _ArgTypes ...) [with _Res = int, _Functor = context_binder<std::_Bind<int (*(int, int, std::_Placeholder<1>))(int, int, int)> >, _ArgTypes = {int}]’: 
/usr/local/include/gcc-4.6.2/functional:2148:6: instantiated from ‘std::function<_Res(_ArgTypes ...)>::function(_Functor, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type) [with _Functor = context_binder<std::_Bind<int (*(int, int, std::_Placeholder<1>))(int, int, int)> >, _Res = int, _ArgTypes = {int}, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type = std::function<int(int)>::_Useless]’ 
scratch.cpp:53:85: instantiated from here 
/usr/local/include/gcc-4.6.2/functional:1764:40: error: no match for call to ‘(context_binder<std::_Bind<int (*(int, int, std::_Placeholder<1>))(int, int, int)> >) (int)’ 

だから、これはほぼ解決のために、私の質問は:代わりfunctionのコンストラクタを使用しようとしているの私のキャストアウト演算子を好むg++を取得する方法はありますか?

template <typename Func> 
struct context_binder 
{ 
private: 
    Func  func; 
    log_context cxt; 

public: 
    context_binder(const Func& func) : 
      func(func), 
      cxt(log_context::get_current()) 
    { } 

    template <typename... TArgs> 
    auto operator()(TArgs&&... args) const 
      -> decltype(func(std::forward<TArgs>(args)...)) 
    { 
     log_context::set_current(cxt); 
     return func(std::forward<TArgs>(args)...); 
    } 
}; 

template <typename F, typename... TArgs> 
auto bind_with_context(F f, TArgs&&... args) 
     -> context_binder<decltype(std::bind(f, std::forward<TArgs>(args)...))> 
{ 
    return std::bind(f, std::forward<TArgs>(args)...); 
} 

誰かが割り当てしようとしたときTArgsに膨張が起こるcontext_binder非整数ためstd::function(そのコンストラクタに:

+2

'const std :: function &throwaway、Func func'を' TResult(Class :: *)(TFuncArgs ...) 'に置き換えてみませんか?結果のタイプは推論可能です。 – Lol4t0

+0

私はそれについて考えました。あなたは、 'class'に' const'と 'volatile'修飾子をつけて非メンバ関数にバインドできます。 9つの過負荷を持つことはひどいことではありません。 –

+1

あなたのコンパイラが[* this']のためのセマンティクスをサポートするならば、それは9以上の過負荷です(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm) 。 – ildjarn

答えて

3

溶液をoperator()を用いstd::functionへの変換から結合を分離することですつかむことを試みるoperator())。

+0

私はこの質問を開いたままにします。なぜなら、この回答は世界で最も美しいものではないからです。 –

+0

私のコンパイラ(g ++ 4.6.2)で動作します。 –

+0

あなたのソリューションは本当に適切です。現在、可変個数の引数をインラインで取り込むファンクタを定義する方法はありません。ラムダ式や 'std :: bind'はそれを解決できません。特に、' std :: bind'を呼び出すと 'アリティとネストされたバインド式は特別な意味を持ちます。 –

関連する問題