3

我有以下代码:

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
4

2 回答 2

2

如果您可以将方法更改为采用单个std::tuple<Args...>而不是多个Args...,那么您可以放置extract_data​​在大括号初始化器中:

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

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

于 2013-11-13T00:47:54.207 回答
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 有一个错误会影响大括号初始化的顺序。如果您使用受影响的版本,只需使用

// 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 - 如果您可以更改指针调用的方法的原型,则应该为元组添加重载并将其作为一个参数传递。上面的代码原则上可以几乎完全内联并且导致非常小的性能开销,但我不确定今天的编译器实际上会用它做什么。

编辑:

此处可编译版本:

于 2013-11-13T01:37:55.993 回答