Pregunta

Primero, un poco de historia:En mi trabajo vinculamos las devoluciones de llamada para que se invoquen más tarde, lo que puede dificultar bastante el intento de seguir el flujo de control a través de los registros.Para ayudar con esto, utilizamos un "contexto de registro", que le permite seguir una solicitud a medida que pasa por el sistema.Puede copiar el contexto actual con una función estática log_context::get_current y restaurarlo con la función estática log_context::set_current.Esto genera una gran cantidad de código repetido cada vez que publicamos devoluciones de llamada en las colas de trabajadores.

Me gustaría crear una función que sea un reemplazo directo para std::bind que salvará la corriente log_context y restaurarlo cuando se invoque.Sin embargo, tengo algunos problemas para escribirlo.

En este momento, la función se ve así:

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

Funciona, pero el problema es que el uso requiere que pases el tipo de función sin ningún motivo real (aparte de que así es como descubro para qué usar). TFuncArgs):

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

Por lo tanto, no es un reemplazo directo.parece uno debería Conozco esta información en tiempo de compilación, simplemente no puedo entender cómo.Es casi llegamos. ¿Cómo puedo eliminar la necesidad de pasar el tipo de función?


Mi idea inicial fue dividir el enlace para convertirlo en una función como esta:

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

El problema es que el elenco (operator std::function<TReturn (TFuncArgs...)>() const) nunca será llamado (dado int foo(int x, int y, int z)):

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

La razón es que functionEl constructor está intentando agarrar operator () desde el context_binder (aunque no lo tenga).

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

Entonces mi pregunta para esta casi solución es: ¿Hay alguna manera de conseguir g++ preferir a mi operador de expulsión en lugar de intentar usar function¿El constructor?

¿Fue útil?

Solución

La solución es separar la unión de la conversión a un std::function usando 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)...);
}

La expansión hacia TArgs sucede cuando alguien intenta asignar un context_binder a una std::function (cuyo constructor para no integral intenta agarrar operator()).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top