[英]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_constant
在constexpr
上下文中轉換為標量。 他們是錯誤的和/或過時的。 對於這些,您必須執行decltype(Is)::value
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.