1

我正在为lua. 它通过实例化函数模板来工作,该模板将 a 提供的参数收集lua到 astd::tuple中,然后将std::tuplea 应用于指向成员函数的指针。这是一个例子:

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()是一组用于设置引用的各种原始类型(例如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技巧改编自: 如何将元组扩展为可变参数模板函数的参数?

#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提供的所有参数复制到 a 中std::tuple,然后将其应用到指向成员的指针上。当然,复制需要一些开销。是否可以以某种方式省略复制?是否存在比现有容器更适合复制参数的容器(标准或其他)std::tuple(即脂肪较少,修剪较多)。

4

1 回答 1

2

我认为使用 astd::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_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;
}

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

于 2013-02-26T18:20:25.767 回答