【问题标题】:Boost bind placeholder argument equal to the number of Variadic Template argumentsBoost 绑定占位符参数等于可变参数模板参数的数量
【发布时间】:2011-12-24 22:37:16
【问题描述】:

我想知道是否可以在 boost::bind 调用中使用传递给可变参数模板的参数数量作为占位符。

类似这样的:

template <typename ... Args>

boost::bind(&function, this, anArg, _1));         //If Args count equals 1
boost::bind(&function, this, anArg, _1, _2));     //If Args count equals 2
boost::bind(&function, this, anArg, _1, _2, _3)); //If Args count equals 3

这可能吗?

谢谢

【问题讨论】:

  • 这里有一个很好的 make_indice 实用程序实现:http://preney.ca 但我很难弄清楚如何将它与 boost::arg 一起使用
  • 该链接读起来很棒,apply( Func, std::tuple ) 函数可能有一天会派上用场。
  • 我看到typename... Args。你在用 C++11 吗?

标签: c++ templates boost bind variadic-templates


【解决方案1】:

肯定有一种方法可以实现部分专业化。 你的变量不知道参数的数量吗?您必须使用编译时递归,在此期间您可以使用 boost::mpl 堆叠您的参数(或使用简单的整数常量增量对它们进行计数)。 然后在您最后一次非可变递归调用(使用 0 arg)中,您在容器上调用 mpl::size(或者如果您选择这种方式,则只使用积分计数器)来调用 Callable 像其他答案一样,它包含所有参数, 在类型列表的开头加上一个完整的模板参数。这就是你的专长。您为每个数量的参数创建一个调用者,该调用者将根据其专用数量的参数调用正确的绑定。 (Callable 结构(部分)根据参数整数模板参数的数量进行专门化。即使 Call 函数采用最大参数数量,它也只包装正确的 boost::bind 调用,例如 bind(.., _1,_2) 用于 Callable) 它并不可怕,但我确认我过去在 C++03 中使用过这种方法。

【讨论】:

    【解决方案2】:

    也许你应该更详细地解释你想要做什么。如果您只是在寻找一种解决方案来处理因参数类型而异的三种不同签名,您可以这样做:

    template<typename signature>
    struct callable;
    
    template<typename P0, typename P1, typename P2>
    struct callable<void (P0, P1, P2)>
    {
        void bind()
        {
            boost::bind(&callable::operator(), this, _1, _2, _3);
        }
    
        void operator()(P0, P1, P2) {}
    };
    

    【讨论】:

      【解决方案3】:

      这不是针对特定问题的答案,而是针对您可能试图解决的问题的一个很好的解决方法。

      我在实现通用委托机制时遇到了同样的问题。我的解决方案是在绑定调用之上使用一个包装器,专门针对变体。虽然它不能解决问题,但它确实将冗余代码最小化为仅绑定调用,最重要的是为我提供了一个基于可变参数的委托系统,我可以在任何地方使用。

      template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE>
      std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(), CALLBACK_TARGET_CLASS* callbackTarget)
      {
          return std::bind(memberFunction, callbackTarget);
      }
      
      template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE, typename P0>
      std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(P0), CALLBACK_TARGET_CLASS* callbackTarget)
      {
          return std::bind(memberFunction, callbackTarget, std::placeholders::_1);
      }
      
      template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE, typename P0, typename P1>
      std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(P0, P1), CALLBACK_TARGET_CLASS* callbackTarget)
      {
          return std::bind(memberFunction, callbackTarget, std::placeholders::_1, std::placeholders::_2);
      }
      
      
      
      template<typename RETURNTYPE, typename... ARGS>
      struct Delegate
      {
          std::function<RETURN_TYPE (ARGS...)> callbackFunction;
      
          template<class CALLBACK_TARGET_CLASS>
          void Bind(CALLBACK_TARGET_CLASS* callbackTarget, RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(ARGS...))
          {
              callbackFunction = BindFunction<CALLBACK_TARGET_CLASS, RETURN_TYPE, ARGS...>(memberFunction, callbackTarget); 
          }
      
          void Callback(ARGS... params)
          {
              callbackFunction(params...);
          }
      };
      

      使用结束我们看起来像这样..

      class Foo
      {
      public:
          void Bar(int x);
      }
      
      Foo foo;
      Delegate<void, int> myDelegate;
      
      myDelegate.Bind(&foo, &Foo::Bar);
      
      myDelegate.Callback(3);
      

      【讨论】:

        【解决方案4】:

        使用可变参数模板不能直接使用 _1、_2、...。您需要改用扩展宏。

        但是,您可以将这些占位符包装在模板工厂中,以使用模板参数 1 获取 _1,为 2 获取 _2 等...

        gcc / msvc 等实现已经将占位符定义为模板结构(分别为 std::_Placeholder 和 std::_Ph),因此您可以通过这种方式定义您的工厂:

        struct ph_factory {
            template<size_t holder>
            static std::_Placeholder<holder> make_ph() {
                return std::_Placeholder<holder>();
            }
        };
        

        这个定义,你可以用你想要的所有占位符扩展一个参数包:

        struct tester {
        
            template<size_t ... holders>
            void test(int val) {
                auto callable = std::bind(&tester::call, this, val, ph_factory::make_ph<holders>()...);
                callable('a', 42, 'c');
            }
        
            void call(int v1, char c1, int v2, char c2) {
                cout << "calling :" << v1 << " " << c1 << " " << v2 << " " << c2 << endl;
            }
        };
        

        所以下面的代码会输出“calling:10 c 42 a”

        int main() {
            tester t;
            t.test<3,2,1>(10);
        }
        

        使用 make_indice 之类的技巧将使您有可能实现最初的目标。

        【讨论】:

        • 永远不要依赖_Names,它们完全是实现私有的,你甚至没有理由去想它们。有更好的方法,一种是将所有占位符填充到 tupleget&lt;I&gt; 中,同时进行包扩展。另一个是您注册自己的占位符(标准为此提供了 is_placeholder 特征,您可以对其进行专门化。
        猜你喜欢
        • 1970-01-01
        • 2017-08-04
        • 2016-12-01
        • 1970-01-01
        • 2020-06-07
        • 1970-01-01
        • 2013-09-22
        • 2013-05-17
        相关资源
        最近更新 更多