C ++可变参数模板和评估顺序 [英] C++ variadic templates and evaluation order

查看:143
本文介绍了C ++可变参数模板和评估顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

lib.hxx:

template <typename C, typename R, typename ... Args>
R Lib::extract_call(lua_State* L, R(C::*method)(Args...))
{
  return static_cast<C*>(this)->*method(extract_data<Args>(L)...);
}

lib.cc:

template <>
std::string Lib::extract_data(lua_State* L)
{
  if (! lua_isstring(L, -1))
  {
    return "";
  }

  return lua_tostring(L, -1);
}
[...] // Other specializations following



在项目中嵌入lua,我目前正在寻找一种从lua调用方法的方法,并且从调度器中自动从lua栈提取参数。
从这些简单的模板可以很容易地生成从参数中提取的数据从lua堆栈提供的调用,没有任何输入错误。

I am embedding lua in a project, and I'm currently looking for a way to call methods from lua, and from a dispatcher extract arguments automatically from the lua stack. From those "simple" templates it is possible to easily generate the calls needed to extract data from the lua stack given in parameter without any typing error.

,我的问题是,当 extract_data< Args>(L)... 解包时,所有 extract_data 调用未指定(如标准中所述,用于优化目的),而真正重要的是从lua堆栈中提取数据的顺序。
另一方面,我不能在初始化器列表中重新组合所有这些调用,因为它们是不同类型的。

BUT, my problem is, when extract_data<Args>(L)... is unpacked, the order of evaluation for all extract_data calls are unspecified (as stated in standard, for optimization purposes), while it really matters in which order you extract data from the lua stack. On the other hand, I can't regroup all of these calls in an initializer list since they are of different type.

因此,如何确保 extract_data 调用以特定顺序,或至少保持自动的方式传递参数到我的成员指针函数?

Therefore, how can I ensure extract_data calls to be in a specific order, or at least keep an automated way to pass arguments to my member pointer function ?

EDIT :我忘记了电话需要处于恢复顺序,我认为这是任何语言机制都无法实现的。
因此,这里是我的解决方案,回到常规,非可变模板:

EDIT: I had forgotten that the calls need to be in revert order, which I don't think is achievable by any language mechanism. Therefore, here is my solution, back to regular, non variadic templates:

template <typename C, typename R, typename A1>
R Lib::extract_call(lua_State* L, R(C::*method)(A1))
{
  return (static_cast<C*>(this)->*method)(extract_data<A1>(L));
}

template <typename C, typename R, typename A1, typename A2>
R Lib::extract_call(lua_State* L, R(C::*method)(A1, A2))
{
  A2 b = extract_data<A2>(L);
  A1 a = extract_data<A1>(L);

  return (static_cast<C*>(this))->*method(a,b);
}

template <typename C, typename R,
          typename A1, typename A2, typename A3>
R Lib::extract_call(lua_State* L, R(C::*method)(A1, A2, A3))
{
  A3 c = extract_data<A3>(L);
  A2 b = extract_data<A2>(L);
  A1 a = extract_data<A1>(L);

  return (static_cast<C*>(this))->*method(a,b,c);
}
// And so on up to 8 arguments


推荐答案

现在我们将得到更复杂的模板。下面的代码我写的没有编译器,可能会有一些错误:

Now we will get more complicated templates. The following code I wrote without compiler, there might be some mistakes:

template <unsigned int n>
class tuple_extractor{
    template <typename T, typename ...ArgsOut, typename ...ArgsIn, typename ...ArgsPart>
    static void extractTuple(
            T* obj,
            void (T::*func)(ArgsOut...),
            const std::tuple<ArgsIn...>& inParams,
            ArgsPart... partParams){
        tuple_extractor<n-1>::extractTuple(obj, func, inParams, std::get<n-1>(inParams));
    }
};

template <>
class tuple_extractor<0>{
    template <typename T, typename ...ArgsOut, typename ...ArgsIn>
    static void extractTuple(
            T* obj,
            void (T::*func)(ArgsOut...),
            const std::tuple<ArgsIn...>& inParams,
            ArgsIn... partParams){
        obj->func(partParams...);
    }
};

template <typename C, typename R, typename ... Args>
R Lib::extract_call(lua_State* L, R(C::*method)(Args...))
{
    std::tuple<Args...> tmp{extract_data<Args>(L)...};
    tuple_extractor<sizeof...(Args)>::extractTuple(static_cast<C*>(this), method, tmp);
}

似乎GCC有错误影响大括号初始化的顺序。如果您使用受影响的版本,只需使用

It seems that GCC has a bug that influences ordering of the brace initialization. If you use a an affected version, just use

// For first-to-last order use:
template <typename T, typename ...Args>
inline std::tuple<T, Args...> getTuple(lua_State* L){
    return std::tuple_cat(std::make_tuple<T>(extract_data<T>(L)), getTuple<Args...>(L));
}

template <typename T>
inline std::tuple<T> getTuple(lua_State* L){
    return std::make_tuple<T>(extract_data<T>(L));
}

    template <typename C, typename R, typename ... Args>
R Lib::extract_call(lua_State* L, R(C::*method)(Args...))
{
    std::tuple<Args...> tmp = getTuple<Args...>(L);
    tuple_extractor<sizeof...(Args)>::extractTuple(static_cast<C*>(this), method, tmp);
}



首先我们创建一个包含所有参数的元组,然后我们将获得的元组提取到方法调用的参数中。

Firstly we create a tuple containing all the parameters, but in an ordered fashion, then we extract obtained tuple into parameters to a method call.

这是回答的问题。但是我同意@Mike - 如果你可以改变指针调用的方法的原型,你应该为一个元组添加一个重载,并将其作为一个参数传递。上面的代码原则上可以几乎完全内联,并导致很少的性能开销,但我不知道今天的编译器实际上会做什么。

This is answer to the question posted. However I agree with @Mike - if you can change prototype of methods called by the pointer, you should add a overload for a tuple and just pass it as one parameter. Above code could in principle be almost fully inlined and cause very little performance overhead, but I am not sure what today's compilers will actually do with it.

编辑:

可编辑版本这里

这篇关于C ++可变参数模板和评估顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆