Question

Tout d'abord, un petit arrière-plan: à mon travail, nous lions les rappels pour être invoqués plus tard, ce qui peut rendre la tenue de suivre le flux de contrôle dans les journaux assez difficile. Pour aider cela, nous utilisons un «contexte de journal», qui vous permet de suivre une demande au fur et à mesure du système. Vous pouvez copier le contexte actuel avec une fonction statique log_context::get_current et le restaurer avec la fonction statique log_context::set_current. Cela conduit à une mène à beaucoup de code répété chaque fois que nous publions des rappels aux files d'attente de travailleurs.

Je voudrais faire une fonction qui remplace std::bind qui sauvera le courant log_context et restaurez-le lorsqu'il est invoqué. Cependant, j'ai du mal à l'écrire.

En ce moment, la fonction ressemble à ceci:

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

Cela fonctionne, mais le problème est que l'utilisation vous oblige à transmettre le type de fonction sans raison réelle (à part que c'est ainsi que je découvre ce qu'il faut utiliser pour TFuncArgs):

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

Donc, pas tout à fait un remplacement sans rendez-vous. Ça ressemble à un devrait Connaissez ces informations chez Compile-Time, je ne peux tout simplement pas comprendre comment. Il est presque là. Comment puis-je éliminer la nécessité de passer dans le type de fonction?


Ma pensée initiale a été de diviser la liaison de la convertir en une fonction comme tel:

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

Le problème est que le casting (operator std::function<TReturn (TFuncArgs...)>() const) ne sera jamais appelé (donné int foo(int x, int y, int z)):

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

La raison en est que functionS Constructor essaie de saisir operator () du context_binder (même s'il n'en a pas).

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

Donc, ma question pour cette solution presque est: Y a-t-il un moyen d'obtenir g++ préférer mon opérateur de casting au lieu d'essayer d'utiliser functionConstructeur?

Était-ce utile?

La solution

La solution consiste à séparer la liaison de la conversion en un std::function utilisant 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)...);
}

L'expansion dans TArgs se produire lorsque quelqu'un essaie d'attribuer un context_binder à un std::function (dont le constructeur de non intégral essaie de saisir operator()).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top