I'm writing a binding engine for lua
. It works by instantiating function tamplates, that gather arguments provided by lua
into a std::tuple
and then apply the std::tuple
on a pointer to member function. Here's an example:
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
is simply a void*
pointer. The set_args
trick was stolen from:
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()
are a set of overloaded functions for various primitive types (such as int
, double
, ...) that set the reference, returned from std::get<>
, for example:
inline void set_arg(lua_State* L, std::size_t i, double& value)
{
assert(lua_isnumber(L, i));
value = lua_tonumber(L, i);
}
The apply
trick was adapted from:
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
it applies the tuple args
on the pointed to member function. Now for my question. I'm disturbed that for each function call I copy all the arguments lua
provides into a std::tuple
and then apply it the on the pointer to member. Certainly the copying entails some overhead. Is it possible to omit the copying somehow? Does there exist a container (standard or otherwise) that is more suitable for the copying of arguments than std::tuple
is (i.e. is less fat, more trimmed).