参数打包在std :: tuple<>然后申请

时间:2013-02-26 10:14:33

标签: c++ c++11 lua tuples variadic-templates

我正在为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返回的引用的各种基本类型(例如doublestd::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更适合复制参数的容器(标准或其他)(即更少脂肪,更多修剪)。

1 个答案:

答案 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_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;
}

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