繁体   English   中英

如何在sdbus回调中使用C ++ 14可变参数模板

[英]How to use c++14 variadic templates with sdbus callbacks

定义d-bus方法时, sd-bus需要一个回调函数。 当我在做C ++ 14时,我想对类对象on_msg_method_here()函数进行这些调用。 我想要实现的是这样的(在伪C ++中):

int callback_dbus_method_foo( message* msg, void* userdata, ... )
{
     MyClass* cls = (MyClass*)userdata;
     Type0 var0;
     if ( message_process( msg, signature[0], &var0 ) != 0 )
        //.. error here
     Type1 var1;
     if ( message_process( msg, signature[1], &var1 ) != 0 )
        //.. error here
     //... and these continue from 0 to N
     TypeN varN;
     if ( message_process( msg, signature[N], &varN ) != 0 )
        //.. error here
     int dbus_ret = cls->on_msg_method_foo( var1, var2, ..., varN )          
     handle_dbus_ret( msg, dbus_ret // ... );
     return 0;
}

int MyClass::register_callbacks( ... )
{

  // Well really we have something really different, this is to demonstrate

  // pass 'this' as userdata* to callback function
  dbus_register_callback( "method_foo", 
         &callback_dbus_method_foo, this )
}

现在我知道我可以使用C-宏来执行此操作,但是如何使用C ++ 14 varidic宏来正确执行此操作?

据我了解,调用某些类对象某些方法的麻烦可以用std :: bind处理(并通过userdata指针传递),变量声明和message_process可以用可变模板剥离来完成,但是如何获得那些声明的变量(在伪代码示例中为var0,var1,..)是否已正确扩展到调用? 简而言之,如何做到这一点:

MyClass::register_callbacks()
{
   Mystic fun_to_call = std::bind( &MyClass::on_dbus_method_foo, this ); 
   dbus_register_callback( "method_foo", 
            super_magic_template<int,double,bool>, &fun_to_call );
}

为了获得一个优雅而通用的解决方案,我将做几件事。

我们需要一种收集变量(var0,var1,...,varN)并将其传递给函数的方法。 为此,我首先要提供一个包装器,以给定索引i来查询此类变量。 我不确定您的示例中有什么signature ,但是我确定您可以解决此问题。

template <class T>
T get_var(message* msg, unsigned i) {
  T var;
  if ( message_process( msg, signature[i], &var ) != 0)
    throw std::runtime_error("Oups"); // In this context, it's easier to deal with errors with exception.
  return var;
}

然后,我们可以通过解压缩可变参数模板参数以及用于索引的关联index_sequence来收集所有变量。 就像是

template <class... Vars, class F>
void callback_wrapper(F& fcn, message* msg) {
  callback_wrapper_impl(fcn, msg, std::make_index_sequence<sizeof...(Vars)>());
}
template <class... Vars, class F, size_t... i>
void callback_wrapper_impl(F& fcn, message* msg, std::index_sequence<i...>) {
  fcn(get_var<Vars>(msg, i)...);
}

使用std :: bind会带来另一个困难,它会返回类似函数的对象fun_to_call 我们不能将其作为不包含任何数据的函数指针传递给dbus_register_callback ,也不能将其作为用户数据传递给fun_to_call ,因为fun_to_call是局部变量,因此它的生命周期太短。

而不是仅依赖于super_magic_template回调,我将对dbus_register_callback进行包装,以提供一个更简单的接口,我们将其modern_dbus_register_callback 我看到的最直接的解决方案是使用动态存储持续时间,但会牺牲内存分配和额外的间接级别-这类似于std :: function中使用的类型擦除。 请注意,如果sizeof(fun_to_call) < sizeof(void*)可以通过按值作为用户数据传递fun_to_call来优化此方法-这是小数值优化。 我相信使用不带捕获的lambda会很有用,因为它们是可转换为函数指针的模板,并且避免了许多模板样板。 可能需要一些额外的工作来处理错误,同时避免内存泄漏。

template <class... Vars, class F>
void modern_dbus_register_callback(const char* name, F& fcn) {
  std::unique_ptr<F> fcn_ptr = std::make_unique<F>(fcn);
  dbus_register_callback(name, [](message* msg, void* userdata){
    std::unique_ptr<F> fcn_ptr(static_cast<F*>(userdata));
    callback_wrapper<Vars...>(*fcn_ptr, msg);
  }, fcn_ptr.release());
}

然后可以用作

modern_dbus_register_callback<int,double,bool>("method_foo", fun_to_call);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM