参数打包在 std::tuple<> 中,然后应用

argument packing in std::tuple<> and then applying

本文关键字:gt 然后 应用 lt std tuple 参数      更新时间:2023-10-16

我正在为lua编写绑定引擎。它通过实例化函数模板来工作,该模板将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的把戏是从:遍历元组:

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()是一组重载函数,用于设置从std::get<>返回的引用的各种基本类型(如int, double,…),例如:

inline void set_arg(lua_State* L, std::size_t i, double& value)
{
  assert(lua_isnumber(L, i));
  value = lua_tonumber(L, i);
}

apply技巧改编自:如何将元组扩展为可变模板函数的参数?

#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更适合复制参数的容器(标准的或其他的)?

我认为使用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() );
}

EDIT:现在我们已经看到了apply_tuple,我认为你实际上可以摆脱apply_tuplestd::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;
}

我无法测试它,所以那里可能有一些错字,但我希望它们很容易修复。如果你需要帮助,请告诉我。