C ++: получить аргументы, полученные в результате std :: bind - PullRequest
3 голосов
/ 21 февраля 2012

Во-первых, немного предыстории: в моей работе мы связываем обратные вызовы, которые будут вызываться позже, что может затруднить попытки отслеживания потока управления через журналы.Чтобы помочь этому, мы используем «контекст журнала», который позволяет вам следовать запросу, когда он проходит через систему.Вы можете скопировать текущий контекст с помощью статической функции 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?

1 Ответ

3 голосов
/ 22 февраля 2012

Решение состоит в том, чтобы отделить привязку от преобразования в std::function, используя 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)...);
}

Расширение в TArgs происходит, когда кто-то пытается присвоить context_binder для std::function (чей конструктор для нецелого пытается получить operator()).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...