繁体   English   中英

向量元组并向后推

[英]Tuple of vectors and push back

我有一个向量元组,我想将初始化列表中的每个值push_back()放入“向量元组”中的相应向量中。 我想在代码中使用create()函数。

template<typename...Fields>
class ComponentManager
{
  using Index = int;
public:
  /**
   * Provides a handle to a component
   **/
  struct ComponentHandle {
    static constexpr Index Nil = -1;
    bool nil() { return index == Nil; }
    const Index index;
  };

  ComponentHandle lookup(Entity e) { 
    return ComponentHandle{get(m_map,e,-1)}; 
  }

  template<int i>
  auto get(ComponentHandle handle) {
    return std::get<i>(m_field)[handle.index];
  }

  ComponentHandle create(Entity e, Fields ...fields) {
    m_entity.push_back(e);
    // m_fields.push_back ... ???
  }

private:
  std::vector<Entity>                m_entity;
  std::tuple<std::vector<Fields>...> m_field;
  std::map<Entity,Index>             m_map;
};

例:

Entity e1, e2;
ComponentManager<int,float,int> cm{};
cm.create(e1, 42, 1337.0, 99);
cm.create(e2, 84, 2674.0, 198);
// Resulting contents of cm.m_field tuple
// { 
//    vector<int>   [ 42, 84 ], 
//    vector<float> [ 1337.0, 2674.0 ]
//    vector<int>   [ 99, 198 ]
// }

可能并不容易明白,但是在C ++中解压缩元组的方法是使用std::apply() 使用C ++ 17,这很容易:

void create(Entity e, Fields ...fields) {
    m_entity.push_back(e);
    std::apply([&](auto&... vs) {
       (vs.push_back(fields), ...);
    }, m_field);
}

对于C ++ 14,我建议无论如何都要自己实现apply() (这是一个短函数 ),然后您需要使用Expander技巧而不是使用fold-expressions:

void create(Entity e, Fields ...fields) {
    m_entity.push_back(e);
    not_std::apply([&](auto&... vs) {
       using swallow = int[];
       (void)swallow{0, 
           (vs.push_back(fields), 0)...
           };
    }, m_field);
}

在C ++ 11中,大多数情况都适用,除了我们不能使用泛型lambda,并且实现std::apply比链接引用更冗长(但并不复杂)。 值得庆幸的是,除了使代码更短之外,我们实际上不需要做其他事情-我们知道所有向量类型:

void create(Entity e, Fields ...fields) {
    m_entity.push_back(e);
    not_std::apply([&](std::vector<Fields>&... vs) {
       using swallow = int[];
       (void)swallow{0, 
           (vs.push_back(fields), 0)...
           };
    }, m_field);
}
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
    (void) std::initializer_list<int>{(f(std::forward<Args>(args)), 0)...};
}

ComponentHandle create(Entity e, Fields ...fields) 
{
    for_each_argument([&](auto field)
                  { 
                      using field_type = std::vector<std::decay_t<decltype(field)>>;
                      std::get<field_type>(m_field).push_back(field); 
                  },
                  fields...);
}

魔盒示例


我没有在问题中提到要求字段必须是同一类型,因此例如在元组中可以有多个向量。

template <typename TVec, typename TFieldTuple, std::size_t... TIdxs>
void expander(TVec& vec, TFieldTuple ft, std::index_sequence<TIdxs...>)
{
    for_each_argument([&](auto idx)
    {
        std::get<idx>(vec).push_back(std::get<idx>(ft));
    }, std::integral_constant<std::size_t, TIdxs>{}...);
}

const auto create = [](auto& vec, auto ...fields) 
{
    expander(vec, 
             std::make_tuple(fields...), 
             std::make_index_sequence<sizeof...(fields)>());
};

魔盒示例

实现的C ++ 14解决方案从C ++ 17向下apply一半。

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};

template<class=void,std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&&f)->decltype(auto) {
    return decltype(f)(f)( index<Is>... );
  };
}
template<std::size_t N>
auto index_over( index_t<N> ={} ) {
  return index_over( std::make_index_sequence<N>{} );
}


template<class F>
auto for_each_arg( F&& f ) {
  return [f = std::forward<F>(f)](auto&&...args)->decltype(auto) {
    using discard=int[];
    (void)discard{0,(void(
      f(decltype(args)(args))
    ),0)...};
  };
}

这些是有用的,但不是必需的:

template<class F, class Tuple>
decltype(auto) apply( F&& f, Tuple&& tuple ) {
  auto count = index< std::tuple_size< std::decay_t<Tuple> >{} >;
  return index_over( count )( [&](auto...Is) {
    using std::get;
    return std::forward<F>(f)( get<decltype(Is)::value>( std::forward<Tuple>(tuple) )... );
  } );
}
template<class Tuple>
auto for_each_tuple_element(Tuple&& tuple) {
  return [&](auto&& f)->decltype(auto){
    return apply(
      for_each_arg( decltype(f)(f) ),
      std::forward<Tuple>(tuple)
    );
  };
}

测试代码:

int main() {
    std::tuple< std::vector<int>, std::vector<char> > tup;
    for_each_tuple_element( tup )( [](auto&& v) {
      v.push_back(3);
    });
    std::cout << std::get<0>(tup).size() << "," << std::get<1>(tup).size() << "\n";
}

现场例子

然后,我们可以将其应用于您的问题。

ComponentHandle create(Entity e, Fields ...fields) {
  m_entity.push_back(e);
  auto indexer = index_over<sizeof...(fields>();
  auto fields_tuple = std::forward_as_tuple( std::forward<Fields>(fields)... );
  indexer( for_each_arg([&](auto Is){
    std::get<Is>(m_fields).push_back(
      std::get<Is>(decltype(fields_tuple)(fields_tuple))
    );
  } );
}

index_over需要一个编译时N并返回一个lambda。 该lambda接受可调用对象,并通过index_t<N-1>index_t<0>进行index_t<N-1>

for_each_arg接受一个callable,并返回一个lambda,该lambda接受任意数量的参数。 它依次使用这些参数中的每个参数调用callable。

我们将Fields...fields缝合在一起,在其上建立index_over ,使我们获得一组编译时的索引。 然后,我们将这些字段存储在r和l值引用的tuple中。

我们在单个索引Is上写一个操作。 然后,将其传递给for_each_arg ,并将返回值传递给index_over ,并为每个索引获取要调用的单个索引处理lambda。

一些编译器不允许非constexpr std::integral_constantconstexpr上下文中转换为标量。 他们是错误的和/或过时的。 对于这些,您必须执行decltype(Is)::value

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM