我正在为lua
编写绑定引擎。它通过实例化函数tamplates,将lua
提供的参数收集到std::tuple
中,然后将std::tuple
应用于指向成员函数的指针。这是一个例子:
template <member_meta_info const* mmi, class C, class R, class ...A>
inline typename std::enable_if<std::is_same<void, R>::value, int>::type
member_stub(lua_State* L)
{
assert(sizeof...(A) + 1 == lua_gettop(L));
static std::tuple<A...> args;
set_args<0, 2>(args, L);
lua_getfield(L, -1, "__instance");
assert(lua_islightuserdata(L, -1));
typedef R (C::*ptr_to_member_type)(A...);
apply_tuple(static_cast<C*>(lua_touserdata(L, -1)),
*static_cast<ptr_to_member_type*>(mmi->ptr_to_member), args);
lua_pushnil(L);
return 1;
}
mmi->ptr_to_member
只是一个void*
指针。 set_args
诡计被盗:
iterate over tuple:
template<std::size_t I = 0, std::size_t O = 1, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp)>::type
set_args(std::tuple<Tp...>&, lua_State*)
{
}
template<std::size_t I = 0, std::size_t O = 1, typename... Tp>
inline typename std::enable_if<I != sizeof...(Tp)>::type
set_args(std::tuple<Tp...>& t, lua_State* L)
{
set_arg(L, I + O, std::get<I>(t));
set_args<I + 1, O, Tp...>(t, I);
}
set_arg()
是一组重载函数,用于设置从int
返回的引用的各种基本类型(例如double
,std::get<>
,...),例如:
inline void set_arg(lua_State* L, std::size_t i, double& value)
{
assert(lua_isnumber(L, i));
value = lua_tonumber(L, i);
}
apply
技巧改编自:
How do I expand a tuple into variadic template function's arguments?
#ifndef APPLYTUPLE_HPP
# define APPLYTUPLE_HPP
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F&& f, T && t, A &&... a)
-> decltype(Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
))
{
return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
);
}
template<typename C, typename F, typename T, typename... A>
static inline auto apply(C && c, F && f, T && t, A &&... a)
-> decltype(Apply<N-1>::apply(::std::forward<C>(c),
::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
))
{
return Apply<N-1>::apply(::std::forward<C>(c), ::std::forward<F>(f),
::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)),
::std::forward<A>(a)...
);
}
template<typename C, typename T, typename... A>
static inline C* apply(T && t, A &&... a)
{
return Apply<N-1>::template apply<C>(::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T &&, A &&... a)
->decltype((*::std::forward<F>(f))(::std::forward<A>(a)...))
{
return (*::std::forward<F>(f))(::std::forward<A>(a)...);
}
template<typename C, typename F, typename T, typename... A>
static inline auto apply(C && c, F && f, T &&, A &&... a)
->decltype((::std::forward<C>(c)->*::std::forward<F>(f))(::std::forward<A>(a)...))
{
return (::std::forward<C>(c)->*::std::forward<F>(f))(::std::forward<A>(a)...);
}
template<typename C, typename T, typename... A>
static inline C* apply(T &&, A &&... a)
{
return new C(::std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply_tuple(F && f, T && t)
->decltype(Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)))
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}
template<typename C, typename F, typename T>
inline auto apply_tuple(C && c, F && f, T && t)
->decltype(Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<C>(c), ::std::forward<F>(f), ::std::forward<T>(t)))
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<C>(c), ::std::forward<F>(f), ::std::forward<T>(t));
}
template<typename C, typename T>
inline C* apply_tuple(T && t)
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::template apply<C>(::std::forward<T>(t));
}
#endif // APPLYTUPLE_HPP
它在指向成员函数上应用元组args
。现在我的问题。我感到不安的是,对于每个函数调用,我将lua
提供的所有参数复制到std::tuple
中,然后将其应用于指向成员的指针。当然,复制需要一些开销。有可能以某种方式省略复制吗?是否存在比std::tuple
更适合复制参数的容器(标准或其他)(即更少脂肪,更多修剪)。
答案 0 :(得分:2)
我认为使用std::tuple
应该没问题,但您可能想尝试删除static
并仅在一步中构建它(无递归)。它可以改进生成的代码,但我会留给你测量和/或分析它。
#include <cstddef>
// we need a compile-time helper to generate indices
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...( Ns ) > next;
};
template< std::size_t N >
struct make_indices
{
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices< 0 >
{
typedef indices<> type;
};
// instead of set_arg, provide get_arg:
template< typename T >
T get_arg( lua_State* L, std::size_t N )
{
static_assert( sizeof( T ) == 0, "T not specialized" );
}
// one specialization per type
template<>
inline double get_arg< double >( lua_State* L, std::size_t i )
{
assert(lua_isnumber(L, i));
return lua_tonumber(L, i);
}
// etc.
// now that we have the helpers, we use it to create an impl
// and forward the original call to it.
template< typename... Args, std::size_t... Ns >
void member_stub_impl( lua_State* L, const indices< Ns... >& )
{
// and here's the non-recursive direct initialization
// version of a std::tuple< Args... >, adjust the offset (1) as needed
const std::tuple< Args... > args( get_arg< Args >( L, Ns + 1 )... );
// now use it...
}
// the forwarder providing the indices to the impl
template< typename... Args >
void member_stub( lua_State* L )
{
typedef typename make_indices< sizeof...( Args ) >::type Indices;
return member_stub_impl< Args... >( L, Indices() );
}
编辑:现在我们已经看到了apply_tuple
,我认为您实际上可以摆脱apply_tuple
和std::tuple
本身。我上面的助手到位了,归结为:
template <member_meta_info const* mmi, class C, class R, class ...A, std::size_t ...Ns>
inline typename std::enable_if<std::is_same<void, R>::value, int>::type
member_stub_impl(lua_State* L, const indices<Ns...>& )
{
assert(sizeof...(A) + 1 == lua_gettop(L));
lua_getfield(L, -1, "__instance");
assert(lua_islightuserdata(L, -1));
typedef R (C::*ptr_to_member_type)(A...);
C* obj = static_cast<C*>(lua_touserdata(L, -1));
auto func = *static_cast<ptr_to_member_type*>(mmi->ptr_to_member);
// look ma, no std::tuple! ;)
obj->*func( get_arg< A >( L, Ns + 1 )... );
lua_pushnil(L);
return 1;
}
我无法测试它,所以可能会有一些拼写错误,但我希望它们很容易修复。如果您需要帮助,请告诉我。