我有以下模板特化,它将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?
答案 0 :(得分:2)
您应该能够创建一个通用仿函数,该仿函数采用fp
,args
和lua
,并调用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 = void
时FunctionPointer<F>::Apply
返回虚拟void_type
。例如,使用R = void
,std::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
中的类型信息。这是处理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
编码和保存这些信息应该是可能的。你需要那个。
使用这种技术,你应该能够重构并显着减少你需要的专业化数量。