Create variant alternating value and array of value - c++

I want to have something like:
template <typename... Ts>
using make_variant_t = /* ??? */;
such that, for instance, make_variant_t<Foo, Bar> evaluates as the type
std::variant<Foo, std::vector<Foo>, Bar, std::vector<Bar>>
in that order. How, if it is possible, can this be achieved?

With Boost.Mp11 this is a short, one-liner (as always):
template <typename... Ts>
using make_variant_t = mp_append<variant<Ts, vector<Ts>>...>;
make_variant_t<int, char> will first produce two variants, variant<int, vector<int>> and variant<char, vector<char>>. Those each are "lists" in the Mp11 sense, and mp_append takes a bunch of lists and concatenates them together, producing variant<int, vector<int>, char, vector<char>> as desired. Demo.

Here you go:
namespace impl
{
template <typename R, typename ...P>
struct make_variant {};
template <typename ...R, typename P0, typename ...P>
struct make_variant<std::variant<R...>, P0, P...>
{
using type = typename make_variant<std::variant<R..., P0, std::vector<P0>>, P...>::type;
};
template <typename ...R>
struct make_variant<std::variant<R...>>
{
using type = std::variant<R...>;
};
}
template <typename ...P>
using make_variant_t = typename impl::make_variant<std::variant<>, P...>::type;
Now, make_variant_t<Foo, Bar> should expand to std::variant<Foo, std::vector<Foo>, Bar, std::vector<Bar>>.

Alternative solution with std::tuple under the hood:
namespace impl {
template<class> struct tuple_to_variant;
template<class... Ts>
struct tuple_to_variant<std::tuple<Ts...>> {
using type = std::variant<Ts...>;
};
}
template<class... Ts>
using make_variant_t = typename impl::tuple_to_variant<decltype(
std::tuple_cat(std::declval<std::tuple<Ts, std::vector<Ts>>>()...))>::type;

Related

Expand template type

Is there a way to convert each type of std::tuple into specific subtypes?
I have following code
struct Foo1
{
struct A{};
struct B{};
};
struct Foo2
{
struct A{};
struct B{};
};
using myTypes = std::tuple<Foo1, Foo2>;
Is there a way to convert myTypes into following type?
std::tuple<Foo1::A, Foo1::B, Foo2::A, Foo2::B>;
Order of types doesn't matter, but would be nice to have it like above.
If A/B name are fixed, you might do
template <typename... Ts>
using AB_Types = std::tuple<typename Ts::A..., typename Ts::B...>;
So AB_Types<Foo1, Foo2> is std::tuple<Foo1::A, Foo2::A, Foo1::B, Foo2::B>.
Having expected order would also be possible:
template <typename... Ts>
using AB_Types_ordered =
decltype(std::tuple_cat(std::tuple<typename Ts::A, typename Ts::B>{}...));
and if source is a tuple, just add extra layer
template <typename>
struct AB_impl;
template <typename... Ts>
struct AB_impl<std::tuple<Ts...>>
{
using type = AB_Types<Ts...>; // AB_Types_ordered<Ts...>
};
template <typename T>
using AB_Types_from_tuple = typename AB_impl<T>::type;
An alternative solution based on the Boost.Mp11 library:
template<class T>
using add_AB = std::tuple<typename T::A, typename T::B>;
template <typename Tuple>
using AB_Types_from_tuple =
boost::mp11::mp_flatten<boost::mp11::mp_transform<add_AB, Tuple>>;
static_assert(std::is_same_v<
AB_Types_from_tuple<myTypes>,
std::tuple<Foo1::A, Foo1::B, Foo2::A, Foo2::B>
>);

How to get an element of type list by index

How can an element of a type list using L = type_list<T1, T2, ...> be retrieved by index, like std::tuple_element, preferrably in a non recursive way?
I want to avoid using tuples as type lists for use cases, that require instantiation for passing a list like f(L{}).
template<typename...> struct type_list {};
using L = typelist<int, char, float, double>;
using T = typeAt<2, L>; // possible use case
Not sure if an iteration using std::index_sequence and a test via std::is_same of the std::integral_constant version of the index is a good aproach.
I want to avoid using tuples as type lists for use cases, that require
instantiation for passing a list like f(L{})
If you don't want to instanciate std::tuple but you're ok with it in
unevaluated contexts, you may take advantage of std::tuple_element to
implement your typeAt trait:
template <std::size_t I, typename T>
struct typeAt;
template <std::size_t I, typename... Args>
struct typeAt<I, type_list<Args...>> : std::tuple_element<I, std::tuple<Args...>> {};
// ^ let library authors do the work for you
using L = type_list<int, char, float, double>;
using T = typename typeAt<2, L>::type;
static_assert(std::is_same<T, float>::value, "");
Using Boost.Mp11, this is a one-liner (as always):
template<typename...> struct type_list {};
using L = typelist<int, char, float, double>;
using T = mp_at_c<L, 2>; // <==
Note that this will compile significantly more efficiently than using tuple_element. On clang, the implementation even uses a compiler intrinsic.
You might re-implement a simplified non-recursive tuple-like version if you don't want to use std::tuple:
template <std::size_t I, typename T>
struct type_list_leaf
{
using type = T;
};
template <typename T> struct tag{ using type = T; };
template <typename Seq, typename...>
struct type_list_impl;
template <std::size_t... Is, typename... Ts>
struct type_list_impl<std::index_sequence<Is...>, Ts...> : type_list_leaf<Is, Ts>...
{
};
template <std::size_t I, typename T>
tag<T> type_list_element_tag(const type_list_leaf<I, T>&);
template <std::size_t I, typename Tuple>
using tuple_element = decltype(type_list_element_tag<I>(std::declval<Tuple>()));
template <std::size_t I, typename Tuple>
using tuple_element_t = typename tuple_element<I, Tuple>::type;
template <typename ... Ts>
using type_list = type_list_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>;
Demo

Parameterize of tuple with repeated type

I want to declare template:
template <size_t N, class Type> my_tuple
{
using type = ... //something here
};
So that this my_typle<3, std::string>::type,for example, will be the same as this std::tuple<std::string, std::string, std::string>
Please, show, how it can be done in one line, maybe, using std::index_sequence or something from boost or whatever? Or maybe it can not be done just simple?
UPD
Please, note I do not need an std::array, I need to parametrize some variable template with predefined list of types. I have used std::tuple as an example here.
This is fun. Here's a "pure" meta-programming approach to expand the sequence:
template<typename T, typename Seq>
struct expander;
template<typename T, std::size_t... Is>
struct expander<T, std::index_sequence<Is...>> {
template<typename E, std::size_t>
using elem = E;
using type = std::tuple<elem<T, Is>...>;
};
template <size_t N, class Type>
struct my_tuple
{
using type = typename expander<Type, std::make_index_sequence<N>>::type;
};
I say "pure" ironically. It's more akin to the classic meta-programming tricks, nothing else.
Use decltype with return type of function returning tuple<string,string,...repeated N times>:
template<typename T, size_t ... Indices>
auto GetType(std::index_sequence<Indices...>) {
return std::make_tuple( (Indices,T{})... );
}
template <size_t N, class Type> class my_tuple
{
using type = decltype(GetType<Type>(std::make_index_sequence<N>())); //something here
};
template<class T>
struct Dummy;
int main(){
Dummy<my_tuple<3,std::string>::type> d;
error on d gives you tuple<string,string,string>, so it is your desired type.
You can
template <typename...>
struct cat_tuple_type;
template <typename... T1, typename... T2>
struct cat_tuple_type<std::tuple<T1...>, std::tuple<T2...>>
{
using type = std::tuple<T1..., T2...>;
};
template <size_t N, class Type> struct my_tuple
{
static_assert(N>0);
using type = typename cat_tuple_type<std::tuple<Type>, typename my_tuple<N-1, Type>::type>::type;
};
template <class Type> struct my_tuple <1, Type>
{
using type = std::tuple<Type>;
};
Then my_typle<3, std::string>::type gives the type std::tuple<std::string, std::string, std::string>.

Recursively folding a parameter pack to resolve placeholder types

Notice: Followup to this question
After asking this question about parameter pack folding into pairs, I noticed that I need to retain the complete type of the previously folded type as the left pair type.
For example:
Fold<char, int, long, double> f;
must evaluate to
std::tuple<
std::pair<char
, int>,
std::pair<std::pair<char, int> /* <-- the previous resulting type */
, long>,
std::pair<std::pair<std::pair<char, int>, long> /* the previous type again */
, double>
> f;
Context to this problem
The reason why I need this, is because the types which have to be "folded" can be placeholder types. The placeholders "real" type can only be known when both having the fully expanded type to the left as well as the unexpanded type.
The leftmost type never contains placeholders.
Let me illustrate this with a quick example:
struct CopyTypeFromPreviousArgumentTag { };
template<typename T = CopyTypeFromPreviousArgumentTag>
struct Foo;
template<typename T...>
struct Bar {
/* Here fold will not use std::pair, but a resolver type that takes both A and B and gives back the resolved B */
Fold<T...> expanded;
};
Now Bar can be used like this:
Bar< Foo<int>
, Foo<>
, Foo<>
, Foo<double>
, Foo<>
> f;
and the internal type decltype(f::expanded) will be:
std::tuple< Foo<int>
, Foo<int>
, Foo<int>
, Foo<double>
, Foo<double>
>;
EDIT: The Bar class is actually not restricted to any class type it might hold. It can be a mixture of several types. So see the Foo class as a placeholder for some type Foo where a resolver type traits exists given the previous resolved type: ResolveType<PreviouslyResolvedType, CurrentType>::Type will give the resolved type correctly. Hence the std::pair idiom.
My current attempt
I tried to implement the recursion by building upon the answer from the linked question, but can't get it to work.
namespace Detail {
template<typename, typename...>
struct Fold;
template
< size_t... Indices
, typename... Types
> struct Fold<std::index_sequence<Indices...>, Types...> {
using Tuple = std::tuple<Types...>;
using Type = std::tuple<std::pair /* use std::pair just to match the first example */
//< std::tuple_element_t<Indices, Tuple>
< typename Fold
< std::tuple_element_t<Indices, Tuple>
, std::make_index_sequence<Indices>
, Types...>::Type; /* Tuple can't be expanded :( */
, std::tuple_element_t<Indices + 1, Tuple>
>::Type...>;
};
} /* namespace Detail */
template<typename... Types>
using Fold = typename Detail::Fold<std::make_index_sequence<sizeof...(Types) - 1>, Types...>::Type;
The linked question is a very convoluted way of doing this. If it was a runtime problem, it will obviously be solved with a one-pass algorithm, metaprogramming is no different.
struct copy_prev {};
template<typename T = copy_prev>
struct Foo {};
template<typename... Ts, typename T>
auto operator+(std::tuple<Ts...>, Foo<T>)
-> std::tuple<Ts..., Foo<T>>;
template<typename... Ts>
auto operator+(std::tuple<Ts...> t, Foo<copy_prev>)
-> std::tuple<Ts..., select_last_t<Ts...>>;
template<typename... Ts>
using fold_t = decltype((std::tuple<>{} + ... + std::declval<Ts>()));
Where select_last_t is implemented as
template<typename T>
struct tag
{
using type = T;
};
template<typename... Ts>
struct select_last
{
using type = typename decltype((tag<Ts>{}, ...))::type;
};
template<typename... Ts>
using select_last_t = typename select_last<Ts...>::type;
Live
Not sure to understand what do you want... but if your Bar struct accept only Foo types... that is: if can be written as follows
template <typename ...>
struct Bar;
template <typename ...Ts>
struct Bar<Foo<Ts>...>
{ /* something */ };
and in Bar you want a type so that from
Bar<Foo<int>, Foo<>, Foo<>, Foo<double>, Foo<>>
you want the internal type
std::tuple<Foo<int>, Foo<int>, Foo<int>, Foo<double>, Foo<double>>
where the unexpressed (defaulted) argument for Foo are substituted with the last expressed argument... I don't see an elegant solution.
The best I can imagine is the development of a type helper as follows (where, for the sake of brevity, I've renamed ctfpat the former CopyTypeFromPreviousArgumentTag)
template <typename...>
struct fooFolder;
// error case: the first type of Bar is ctfpat (non impemented;
// generate compile time error)
template <typename ... Ts>
struct fooFolder<std::tuple<>, ctfpat, ctfpat, Ts...>;
template <typename ... Tps, typename Tprev, typename T0, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, T0, Ts...>
: fooFolder<std::tuple<Tps..., Foo<T0>>, T0, Ts...>
{ };
template <typename ... Tps, typename Tprev, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, ctfpat, Ts...>
: fooFolder<std::tuple<Tps..., Foo<Tprev>>, Tprev, Ts...>
{ };
template <typename Tpl, typename Tprev>
struct fooFolder<Tpl, Tprev>
{ using type = Tpl; };
and Bar become
template <typename ...>
struct Bar;
template <typename ...Ts>
struct Bar<Foo<Ts>...>
{
using foldedType = typename fooFolder<std::tuple<>, ctfpat, Ts...>::type;
foldedType expanded;
};
The following is a full compiling example
#include <tuple>
#include <type_traits>
struct ctfpat // copy type from previous argument tag
{ };
template <typename T = ctfpat>
struct Foo
{ };
template <typename ...>
struct fooFolder;
// error case: the first type of Bar is ctfpat (non impemented;
// generate compile time error)
template <typename ... Ts>
struct fooFolder<std::tuple<>, ctfpat, ctfpat, Ts...>;
template <typename ... Tps, typename Tprev, typename T0, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, T0, Ts...>
: fooFolder<std::tuple<Tps..., Foo<T0>>, T0, Ts...>
{ };
template <typename ... Tps, typename Tprev, typename ... Ts>
struct fooFolder<std::tuple<Tps...>, Tprev, ctfpat, Ts...>
: fooFolder<std::tuple<Tps..., Foo<Tprev>>, Tprev, Ts...>
{ };
template <typename Tpl, typename Tprev>
struct fooFolder<Tpl, Tprev>
{ using type = Tpl; };
template <typename ...>
struct Bar;
template <typename ... Ts>
struct Bar<Foo<Ts>...>
{
using foldedType = typename fooFolder<std::tuple<>, ctfpat, Ts...>::type;
foldedType expanded;
};
int main()
{
using t1 = typename Bar<Foo<>, Foo<int>, Foo<>, Foo<double>,
Foo<>>::foldedType;
using t2 = std::tuple<Foo<int>, Foo<int>, Foo<int>, Foo<double>,
Foo<double>>;
static_assert( std::is_same<t1, t2>{}, "!" );
}
I start my solution by building something that knows how to create the nth type. It's just a pair of the previous created type, and the current source type, so:
template <std::size_t I, class T>
struct element {
using type = std::pair<typename element<I - 1, T>::type,
std::tuple_element_t<I + 1, T>>;
};
template <class T>
struct element<0, T> {
using type =
std::pair<std::tuple_element_t<0, T>, std::tuple_element_t<1, T>>;
};
All we really need to do now is put the input types into a tuple, grab an integer pack, and feed the tuple and unfold the integer pack through element, into a new tuple. No problem:
template <class I, class... Ts>
class fold_helper;
template <std::size_t... Is, class... Ts>
class fold_helper<std::index_sequence<Is...>, Ts...> {
using tup = std::tuple<Ts...>;
public:
using type = std::tuple<typename element<Is, tup>::type...>;
};
template <class... Ts>
using Fold = typename fold_helper<std::make_index_sequence<sizeof...(Ts)-1>,
Ts...>::type;
Finally, let's check this works:
int main() {
static_assert(
std::is_same<Fold<char, int, long, double>,
std::tuple<std::pair<char, int>, //
std::pair<std::pair<char, int>, long>,
std::pair<std::pair<std::pair<char, int>, long>,
double>>>::value,
"");
return 0;
};
This program compiled for me.

How can I arbitrarily sort a tuple's types?

One thing that really annoys me about C++ is that an empty struct/class takes up space.
So, I have this idea that std::tuple (or some variant, since it's (and the compiler's) implementation is highly implementation dependent) might be able to save the day, which it sort of does, but there are issues due to packing and alignment. Because of how compilers will align the items in the struct, having a empty next to a non-empty next to an empty next to a non-empty will be larger than 2 empties next to 2 non-empties.
Because of this, I need a way to reorder the types based on some criteria. Sorting the entire list based on size isn't necessary (and may in some cases be detrimental) so I need some generic way to reorder the tuple's type list but still access it as if the type list was in the original order.
I looked around a bit and haven't found anything like this and I'm at a loss. Ideas on how to accomplish this?
Example
struct A{};
struct B{};
// Need to be reordered based on some criteria.
std::tuple<A, int, B, float> x;
// In this case move all of the empty objects together like:
// std::tuple<A, B, int, float> x;
// but still have get<1>(x) return the `int` and get<2>(x) return `B`.
static_assert(std::is_same<decltype(get<0>()), A>::value, "0 should be type A");
static_assert(std::is_same<decltype(get<1>()), int>::value, "1 should be type int");
static_assert(std::is_same<decltype(get<2>()), B>::value, "2 should be type float");
static_assert(std::is_same<decltype(get<3>()), float>::value, "3 should be type B");
The reason this cannot be done by hand is that this could be part of a template and the elements in tuple may be empty or not, based on the parameters:
template <typename A, typename B, typename C, typename D>
class X
{
// Need to have this auto arranged given some criteria
// like size or move all of the empties together.
tuple<A, B, C, D> x;
public:
template<int i>
auto get() -> typename std::tuple_element<i, decltype(x)>
{
return get<i>(x);
}
};
// What are these types? Who knows. This could be buried in some
// template library somewhere.
X<T1, T2, T3, T4> x;
Building on what Barry did.
So from here I'd need a mapping meta-function to use the original
indices, how would I do that?
First, some helpers to facilitate index mapping. And because I'm lazy, I modified typelist slightly.
template <typename... Args>
struct typelist {
static constexpr std::size_t size = sizeof...(Args);
};
template<class T, std::size_t OldIndex, std::size_t NewIndex>
struct index_map_leaf {
using type = T;
static constexpr std::size_t old_index = OldIndex;
static constexpr std::size_t new_index = NewIndex;
};
template<class... Leaves>
struct index_map : Leaves... {};
Given a properly built index_map, converting from old index to new index is simple, leveraging template argument deduction and overload resolution:
template<std::size_t OldIndex, std::size_t NewIndex, class T>
index_map_leaf<T, OldIndex, NewIndex>
do_convert_index(index_map_leaf<T, OldIndex, NewIndex>);
template<std::size_t OldIndex, class IndexMap>
using converted_index_t = decltype(do_convert_index<OldIndex>(IndexMap()));
converted_index_t<OldIndex, IndexMap>::new_index is, well, the new index.
To build the index map, we do it in in three steps. We start by transforming the types into type-index pairs.
template<class... Ts, std::size_t... Is>
typelist<index_map_leaf<Ts, Is, 0>...>
do_build_old_indices(typelist<Ts...>, std::index_sequence<Is...>);
template<class TL>
using build_old_indices =
decltype(do_build_old_indices(TL(), std::make_index_sequence<TL::size>()));
Next, we partition the pairs. We need a metafunction that applies another metafunction to its arguments' nested typedef type rather than the arguments themselves.
// Given a metafunction, returns a metafunction that applies the metafunction to
// its arguments' nested typedef type.
template<class F>
struct project_type {
template<class... Args>
using apply = typename F::template apply<typename Args::type...>;
};
Given this, partitioning a typelist of index_map_leafs is simply partition_t<LeafList, project_type<F>>.
Finally, we transform the partitioned list, adding the new indices.
template<class... Ts, std::size_t... Is, std::size_t...Js>
typelist<index_map_leaf<Ts, Is, Js>...>
do_build_new_indices(typelist<index_map_leaf<Ts, Is, 0>...>,
std::index_sequence<Js...>);
template<class TL>
using build_new_indices =
decltype(do_build_new_indices(TL(), std::make_index_sequence<TL::size>()));
Bringing it all together,
template<class TL, class F>
using make_index_map =
apply_t<quote<index_map>, build_new_indices<partition_t<build_old_indices<TL>,
project_type<F>>>>;
With a little utility to convert the arguments of an arbitrary template to a type list:
template<template<class...> class T, class... Args>
typelist<Args...> do_as_typelist(typelist<T<Args...>>);
template<class T>
using as_typelist = decltype(do_as_typelist(typelist<T>()));
We can do the partitioning only once, by constructing the reordered tuple type directly from the index_map.
template<class Tuple, class F>
struct tuple_partitioner {
using map_type = make_index_map<as_typelist<Tuple>, F>;
using reordered_tuple_type = apply_t<project_type<quote<std::tuple>>,
as_typelist<map_type>>;
template<std::size_t OldIndex>
using new_index_for =
std::integral_constant<std::size_t,
converted_index_t<OldIndex, map_type>::new_index>;
};
For example, given
using original_tuple = std::tuple<int, double, long, float, short>;
using f = quote<std::is_integral>;
using partitioner = tuple_partitioner<original_tuple, f>;
The following assertions hold:
static_assert(partitioner::new_index_for<0>() == 0, "!");
static_assert(partitioner::new_index_for<1>() == 3, "!");
static_assert(partitioner::new_index_for<2>() == 1, "!");
static_assert(partitioner::new_index_for<3>() == 4, "!");
static_assert(partitioner::new_index_for<4>() == 2, "!");
static_assert(std::is_same<partitioner::reordered_tuple_type,
std::tuple<int, long, short, double, float>>{}, "!");
Demo.
P.S. Here's my version of filter:
template<typename A, typename F>
using filter_one = std::conditional_t<F::template apply<A>::value,
typelist<A>, typelist<>>;
template<typename F, typename... Args>
concat_t<filter_one<Args, F>...> do_filter(typelist<Args...>);
template <typename TL, typename F>
using filter_t = decltype(do_filter<F>(TL()));
First, let's start with the basics. We need a way to turn a template template (std::tuple) into a metafunction class:
template <template <typename...> class Cls>
struct quote {
template <typename... Args>
using apply = Cls<Args...>;
};
And a typelist:
template <typename... Args>
struct typelist { };
And something to go between them:
template <typename F, typename TL>
struct apply;
template <typename F, typename... Args>
struct apply<F, typelist<Args...>> {
using type = typename F::template apply<Args...>;
};
template <typename F, typename TL>
using apply_t = typename apply<F, TL>::type;
So that given some typelist, we can just have:
using my_tuple = apply_t<quote<std::tuple>, some_typelist>;
Now, we just need a partitioner on some criteria:
template <typename TL, typename F>
struct partition {
using type = concat_t<filter_t<TL, F>,
filter_t<TL, not_t<F>>
>;
};
Where concat:
template <typename... Args>
struct concat;
template <typename... Args>
using concat_t = typename concat<Args...>::type;
template <typename... A1, typename... A2, typename... Args>
struct concat<typelist<A1...>, typelist<A2...>, Args...> {
using type = concat_t<typelist<A1..., A2...>, Args...>;
};
template <typename TL>
struct concat<TL> {
using type = TL;
};
filter:
template <typename TL, typename F>
struct filter;
template <typename TL, typename F>
using filter_t = typename filter<TL, F>::type;
template <typename F>
struct filter<typelist<>, F> {
using type = typelist<>;
};
template <typename A, typename... Args, typename F>
struct filter<typelist<A, Args...>, F> {
using type = concat_t<
std::conditional_t<F::template apply<A>::value,
typelist<A>,
typelist<>>,
filter_t<typelist<Args...>, F>
>;
};
And not_:
template <typename F>
struct not_ {
template <typename Arg>
using apply = std::conditional_t<F::template apply<Args>::value,
std::false_type,
std::true_type>;
};
Which, given some_typelist of types that you want to put in your tuple becomes:
using my_tuple = apply_t<
quote<std::tuple>,
partition_t<
some_typelist,
some_criteria_metafunc_class
>>;