C ++可变参数模板和评估顺序

时间:2013-11-13 00:28:52

标签: c++ templates variadic-templates

我有以下代码:

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堆栈中提取数据所需的调用,而不会出现任何输入错误。

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

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

编辑:我忘记了调用需要采用还原顺序,我认为任何语言机制都无法实现。 因此,这是我的解决方案,回到常规的非变量模板:

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

2 个答案:

答案 0 :(得分:2)

如果您可以将方法更改为单个std::tuple<Args...>,而不是多个Args...,那么您可以将extract_data放在大括号中:{/ p>

return static_cast<C*>(this)->*method({extract_data<Args>(L)...});

与函数参数不同,初始化子句的评估从左到右排序。

答案 1 :(得分:1)

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

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似乎有bug影响大括号初始化的顺序。如果您使用受影响的版本,请使用

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

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

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

编辑:

可编辑版本here