3

まず、少し背景があります。私の仕事では、後で呼び出されるようにコールバックをバインドします。これにより、ログを介した制御フローの追跡が非常に困難になる可能性があります。これを支援するために、「ログコンテキスト」を使用します。これにより、システムを通過するリクエストを追跡できます。静的関数を使用して現在のコンテキストをコピーし、静的関数log_context::get_currentを使用して復元できますlog_context::set_current。これにより、コールバックをワーカーキューに投稿するたびに、多くのコードが繰り返されることになります。

ドロップインの代わりとstd::bindなる関数を作成して、現在の関数を保存し、log_context呼び出されたときに復元したいと思います。しかし、私はそれを書くのに苦労しています。

現在、関数は次のようになっています。

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)’

したがって、このほぼ解決策に対する私の質問は、のコンストラクターを使用する代わりに、キャストアウト演算子を優先する方法はありますか?g++function

4

1 に答える 1

3

std::function解決策は、バインディングを usingへの変換から分離することoperator ()です。

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)...);
}

への展開は、誰かが aを anTArgsに割り当てようとしたときに発生します(非整数のコンストラクターが を取得しようとします)。context_binderstd::functionoperator()

于 2012-02-21T21:31:03.940 に答える