【问题标题】:C++: Get the arguments resulting from std::bindC++:获取从 std::bind 产生的参数
【发布时间】:2012-02-21 17:42:43
【问题描述】:

首先,有一点背景知识:在我的工作中,我们绑定了稍后调用的回调,这会使尝试通过日志跟踪控制流变得非常困难。为了帮助实现这一点,我们使用“日志上下文”,让您可以在请求通过系统时对其进行跟踪。您可以使用静态函数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&lt;TReturn (TFuncArgs...)&gt;() const)永远不会被调用(给定int foo(int x, int y, int z)):

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

原因是functions 构造函数试图从context_binder 中获取operator ()(即使它没有)。

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 的构造函数?

【问题讨论】:

  • 为什么不将const std::function&lt;TResult (TFuncArgs...)&gt;&amp; throwaway, Func func 替换为TResult (Class::*)(TFuncArgs...)。那么结果类型将是可推导出的。
  • 我想过...你可以绑定到非成员函数,在Class 上使用constvolatile 限定符。有 9 个重载并不可怕。
  • 如果你的编译器支持move semantics for *this,那么重载就远不止9个。
  • @ildjarn:会是TResult (Class::*)(TFuncArgs...) &amp;&amp;吗?老实说,一旦您进入宏观领域,只需添加 entry(&amp;&amp;) entry(const &amp;&amp;) 等即可。
  • &amp;&amp;&amp;,以及constvolatile 的所有排列。这是很多重载。

标签: c++ c++11 variadic-templates std-function stdbind


【解决方案1】:

解决方案是使用operator () 将绑定与转换为std::function 分开:

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

当有人试图将context_binder 分配给std::function(其非整数构造函数试图获取operator())时,就会扩展到TArgs

【讨论】:

  • 我将保留这个问题,因为这个答案不是世界上最漂亮的东西。
  • 它适用于我的编译器 (g++ 4.6.2)。
  • 您的解决方案确实足够了。目前没有办法定义一个内联可变数量参数的仿函数——没有多少 lambda 表达式或std::bind 可以解决这个问题,特别是考虑到对std::bind 的调用“修复”了arity 和嵌套的绑定表达式特殊含义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
  • 1970-01-01
  • 2017-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多