I have a templated structure ContainerInner
that has a tuple with vectors of types from its variadic template and a structure Container
that contains a tuple of ContainerInner
, specified in its template. The code looks like this:
template<typename... Args>
struct ContainerInner
{
std::tuple<std::vector<Args>...> m_elements;
template<typename T>
static constexpr bool containsOne()
{
return (std::is_same_v<T, Args> || ...);
}
template<typename... T>
static constexpr bool contains()
{
return (containsOne<T>() && ...);
}
template<typename T>
std::vector<T> &get()
{
return std::get<std::vector<T>>(m_elements);
}
};
template<typename... T>
struct ArgList {
template<typename... TTS>
using add = ArgList<T..., ContainerInner<TTS...>>;
};
template<typename ListArgs>
struct Container;
template<typename... Args>
struct Container<ArgList<Args...>>
{
std::tuple<Args...> m_containers;
// It also crashes with std::tuple<Args&...> return type
template<typename... T>
inline auto getTuples()
{
auto tpl = std::make_tuple<Args&...>(std::get<Args>(m_containers)...);
return tpl;
}
template<typename... T>
ContainerInner<T...> &get()
{
return std::get<ContainerInner<T...>>(m_containers);
}
};
using MyArgList = ArgList<>
::add<char, int>
::add<char, float>
::add<char, int, float>;
int main(int argc, char* args[])
{
Container <MyArgList> cnt;
auto arr = cnt.getTuples<float>();
std::get<ContainedType<char, int>>(arr).get<int>().push_back(3);
std::cout << typeid(arr).name() << std::endl;
std::cout << cnt.get<char, int>().get<int>().size() << std::endl;
}
I want to change getTuples
method so that it would return a tuple with references to all ContainerInner
from m_containers
that contain specified types, which is a condition handled by static contains
method. I thought I could use a concept like this:
template<typename InnerCnt, typename T>
concept ContainedType =
requires(T a, InnerCnt b) {
b.contains<T>();
};
to recursively iterate over variadic template and use std::tuple_cat
to build final array, but didnt figure out how to do it. How can I solve this problem?
EDIT. I solved it, but probably not in the optimal way. I added Query
wrapper over tuple with overloaded operator+
and used it for unfolding:
template<typename... Args>
struct Query
{
std::tuple<Args...> m_tpl;
template<typename... TT>
constexpr inline auto operator+(const Query<TT...> &rhs_)
{
return Query<Args..., TT...>(std::tuple_cat(m_tpl, rhs_.m_tpl));
}
template<typename... TT>
constexpr inline auto &get()
{
return std::get<ContainerInner<TT...>&>(m_tpl);
}
};
template<typename LST, typename CONTAINER_INNER>
constexpr inline auto getQueryElem(CONTAINER_INNER &t_)
{
return Query<>();
}
template<typename LST, typename CONTAINER_INNER> requires Contained<CONTAINER_INNER, LST>
constexpr inline auto getQueryElem(CONTAINER_INNER &t_)
{
return Query(std::tuple<CONTAINER_INNER&>(t_));
}
// Inside Container
template<typename... T>
constexpr inline auto getTuples()
{
return (getQueryElem<Typelist<T...>>(std::get<Args>(m_containers)) + ...);
}
int main(int argc, char* args[]) {
Container <MyArgList> cnt;
auto arr = cnt.getTuples<int>();
std::cout << typeid(arr).name() << std::endl;
cnt.get<char, int>().get<int>().push_back(5);
arr.get<char, int>().get<int>().push_back(-99);
std::cout << cnt.get<char, int>().get<int>() << std::endl;
std::cout << arr.get<char, int>().get<int>() << std::endl;
}
It seems to work, but i'm not sure how well does compiler optimize this, it might actually create temporary Queries during unfolding in runtime
Issues from getTuples()
:
std::make_tuple
is intended to deduced its parameter, you want std::tuple
directly:auto getTuples()
{
auto tpl = std::tuple<Args&...>(std::get<Args>(m_containers)...);
return tpl;
}
or use std::tie
:
auto getTuples()
{
return std::tie(std::get<Args>(m_containers)...);
}
From your edit, it seems you want to filter (at compile time) some elements of the tuple, so using std::tuple_cat
with function which return empty tuple
or tuple
with the element might help:
template <typename... Ts, typename... Us>
auto filter(ContainerInner<Us...>& c)
{
if constexpr (ContainerInner<Us...>::template contains<Ts...>()) {
return std::tie(c);
} else {
return std::tuple<>{};
}
}
And then
template <typename... Ts>
constexpr auto getTuples()
{
return std::tuple_cat(filter<Ts...>(std::get<Args>(m_containers))...);
})