如何提高 Lua 包装器函数的简洁性
How can I improve the conciseness of my Lua wrapper functions?
我有以下模板专用化,将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,常量与否)有所不同。我总共有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
};
我可以避免这种剪切和粘贴吗? (虽然不使用宏)
相关,但遭受相同数量的必需专业化:如何使用可变参数模板制作通用 Lua 函数包装器?
你应该能够创建一个泛型函子,它接受fp
、args
和lua
,并调用lua.Push()
,在R
被void
时具有部分特化,它只是调用函数并忽略(void)结果。 然后,您将像这样调用它:
ApplyAndPushIfNotVoid<R>()(lua, fp, args);
绝对可以消除所有重复的模板专用化。事实上,对于一次性分支的情况,比如在你的自由函数struct Wrapper
中,你甚至不需要编写一个专门的来隐藏它——只需使用 type_traits
中的std::is_void
:
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;
}
}
};
编译器将根据其实例化方式优化其中一个分支。
但是,当返回类型R = void
时,假分支在实例化过程中仍然会进行类型检查,从而导致正文格式不正确。
像另一个答案一样使用模板专业化是一个明显的解决方案。还有另一种解决方法:FunctionPointer<F>::Apply
R = void
时返回虚拟void_type
。例如使用 std::conditional
,可以修改FunctionPointer
,使其工作如下:
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 {};
}
};
IDEone 演示,其中外部依赖类型已存根。
对于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
本身只是一个空函数,用于帮助从PMF
中推断和提取类型信息。这是处理const
和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
中的可变参数,只是为了降低模板的复杂性和语法措辞,但如果需要,也应该可以使用std::tuple
编码和保存此信息。
使用此技术,您应该能够重构并显著减少所需的专业化数量。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如何提高 Lua 包装器函数的简洁性