如何提高Lua包装函数的简洁性?

时间:2015-01-14 07:17:27

标签: c++ lua variadic-templates

我有以下模板特化,它将C ++函数包装到Lua:

  template<class ...Args>
  struct Wrapper<void (*)(Args...)> {

    using F = void (*)(Args...);

    static int f (lua_State *L)
    {
      Lua lua(L);

      // Grab the function pointer.
      F f = (F) lua_touserdata(L, lua_upvalueindex(1));

      // Build a tuple of arguments.
      auto args = lua.CheckArgs<1, Args...>();

      // Apply the function to the tuple.
      FunctionPointer<F> fp(f);
      fp.Apply(args);

      return 0;

    }
  };

  template<class R, class ...Args>
  struct Wrapper<R (*)(Args...)> {

    using F = R (*)(Args...);

    static int f (lua_State *L)
    {
      Lua lua(L);

      // Grab the function pointer.
      F f = (F) lua_touserdata(L, lua_upvalueindex(1));

      // Build a tuple of arguments.
      auto args = lua.CheckArgs<1, Args...>();

      // Apply the function to the tuple.
      FunctionPointer<F> fp(f);
      lua.Push( fp.Apply(args) );

      return 1;

    }
  };

请注意它们之间的差异很小。在第一次专门化中,FunctionPointer<F>::Apply返回void。在第二个中,它的结果被推到了Lua堆栈上。

我可以将这两个专业组合成一个吗?

我意识到这可能看起来很迂腐,但我不得不在我的代码中的其他地方写了很多这些包装器,因为函数类型的变化是包装器(自由函数,或PMF,const或不是)。我总共有14个这样的专业。

这里还有两个,它们的区别仅在于PMF是否为常数:

  template <typename Self, typename ...Args>
  struct MethodWrapper<void (Self::*)(Args...) >
  {
    using F = void (Self::*)(Args...);

    static int f (lua_State *L)
    {
      Lua lua(L);

      F f = *(F *)lua_touserdata(L, lua_upvalueindex(1));
      Self* self = lua.CheckPtr<Self>(1);

      auto args = lua.CheckArgs<2, Args...>();

      FunctionPointer<F> fp(f);
      try {
        fp.Apply(self, args);
      } catch(std::exception& e) {
        luaL_error(L, e.what());
      }

      return 0;
    }
  };

  template <typename R, typename Self, typename ...Args>
  struct MethodWrapper<R (Self::*)(Args...) const >
  {
    // exactly the same as above
  };

我可以避免这种剪切和粘贴吗?(不使用宏)

相关,但遭受相同数量的所需专业化:How to use variadic templates to make a generic Lua function wrapper?

2 个答案:

答案 0 :(得分:2)

您应该能够创建一个通用仿函数,该仿函数采用fpargslua,并调用lua.Push(),并对{{1}进行部分特化是R只调用函数并忽略(void)结果。然后你会像这样调用它:

void

答案 1 :(得分:1)

绝对有可能消除所有重复的模板专业化。事实上,对于一次性分支案例,就像在您的免费功能struct Wrapper中一样,您甚至不需要编写专门化来隐藏它 - 只需使用std::is_void来自{{ 1}}:

type_traits

编译器将根据实例化的方式优化其中一个分支。

虽然返回类型为template<typename R, typename ...Args> struct Wrapper { using F = R (*)(Args...); static int f (lua_State *L, F f) { // ... FunctionPointer<F> fp {f}; if (std::is_void<R>::value) { fp.Apply(args); return 0; } else { lua.Push( fp.Apply(args) ); return 1; } } }; ,但是在实例化过程中仍会对falsey分支进行类型检查,从而导致身体形成不良。

像其他答案一样使用模板专业化是一个显而易见的解决方案。还有一种替代解决方法:R = voidFunctionPointer<F>::Apply返回虚拟void_type。例如,使用R = voidstd::conditional可以修改为:

FunctionPointer
带有外部依赖类型的

IDEone Demo已被删除。

对于template <typename F> class FunctionPointer { template <typename R, typename ...Args> static R func_return( R(*)(Args...) ) { return {}; } using R_ = decltype( func_return( (F)nullptr ) ); struct void_type {}; public: F f; using R = typename std::conditional<std::is_void<R_>::value, void_type, R_>::type; template <typename ...Args> R Apply(std::tuple<Args...> &args) { // ... return {}; } }; ,我会识别不同的特征&#39;它需要从成员指针中提取所有这些并将其隐藏在一些特征类后面。我们称之为MethodWrapper

PMF_traits

template <typename T, typename ...Args> struct PMF_traits { private: using T_traits = decltype( PMF_trait_helper( (T)nullptr ) ); public: using class_type = typename T_traits::class_type; using return_type = typename T_traits::return_type; static const bool const_member = T_traits::const_member; using type = T; }; 本身只是一个空函数,可帮助推导出PMF_trait_helper中的类型信息。这是处理PMFconst non-const的位置。该信息使用PMF捕获并传回PMF_trait_detail

PMF_traits

使用该设置template <typename R, typename Class, bool Is_Const> struct PMF_trait_detail { using class_type = Class; using return_type = R; static const bool const_member = Is_Const; }; template <typename R, typename Class, typename ...Args> PMF_trait_detail<R, Class, false> PMF_trait_helper( R (Class::*)(Args...) ) { return PMF_trait_detail<R, Class, false> (); } template <typename R, typename Class, typename ...Args> PMF_trait_detail<R, Class, false> PMF_trait_helper( R (Class::*)(Args...) const) { return PMF_trait_detail<R, Class, true> (); } 不再需要单独处理MethodWrapper const个案

non-const

注意我没有捕获template <typename PMF, typename ...Args> struct MethodWrapper { typedef typename PMF_traits<PMF>::class_type Self; int f (lua_State *L) { // ... FunctionPointer<PMF> fp { (PMF) lua_touserdata(L, lua_upvalueindex(1)) }; Self *self = lua.CheckPtr<Self>(1); // ... try { // Almost like 'Wrapper' above // handle void and non-void case etc. if (std::is_void< typename PMF_traits<PMF>::return_type >::value) { fp.Apply(self, args); return 0; } else { // ... } } catch(std::exception& e) { return luaL_error(L, e.what()); } } }; 中的可变参数只是为了保持模板复杂性和语法错误,但是如果使用PMF_traits编码和保存这些信息应该是可能的。你需要那个。

使用这种技术,你应该能够重构并显着减少你需要的专业化数量。