I would like to store the initialization values for elements in a tuple inside a separate tuple, so that I can use the same values as a preset for other tuples of the respective type.
It is very important to my program that the constructors of the tuple elements are called in left-to-right order (otherwise it would at best turn out very confusing).
Here is a simplified version of my program:
#include <tuple>
// Elements are the end points of a Widget hierarchy
struct Element
{
using initer_t = int;
Element( const initer_t pIniter )
:data{ pIniter }
{
printf("Creating %i\n", data);
}
const initer_t data;
};
// A Widget class stores any number of Elements and/or other Widget instances
template<typename... Elems>
struct Widget
{
using initer_t = std::tuple<typename Elems::initer_t...>;
Widget( const initer_t pIniter )
:elements{ pIniter }
{}
const std::tuple<Elems...> elements;
};
int main()
{
using Button = Widget<Element, Element>;
using ButtonList = Widget<Button, Button, Element>;
Button::initer_t basic_button_initer{ 0, 1 }; // presets for Buttons
Button::initer_t other_button_initer{ 2, 3 };
ButtonList::initer_t buttonlist_initer{ basic_button_initer, other_button_initer, 4 }; //a preset for a ButtonList
ButtonList buttonlist{ buttonlist_initer };
return 0;
}
So I am initializing the std::tuple<Elems...> elements
member of Widget<Elems...>
with a std::tuple<typename Elems::initer_t...>
in Widget<Elems...>
's constructor initialization list.
This is supposed to initialize each element of elements
with its corresponding initialization value of the type defined by initer_t
using the values in pIniter.
The initer_t
type is a type for each member of a Widget hierarchy(for example a Widget<typename...>
or an Element
), which is the type that the hierarchy member should be initialized with.
But the order in which this happens is right-to-left, while I need it in left-to-right.
The output of the program is
Creating 4
Creating 3
Creating 2
Creating 1
Creating 0
But I want to reverse this order.
How can I do this in this case?
For anyone interested in a solution, I came up with a way to control the initialization order and retain the constness of elements
:
#include <tuple>
template<typename... Elems>
struct construct
{
template<size_t... Ns, typename Head, typename... Rest>
static constexpr const std::tuple<Rest...>
drop_head_impl( const std::index_sequence<Ns...> ns,
const std::tuple<Head, Rest...> tup )
{
return std::tuple<Rest...>( std::get<Ns + 1u>( tup )... );
}
template<typename Head, typename... Rest>
static constexpr const std::tuple<Rest...>
drop_head( const std::tuple<Head, Rest...> tup )
{
return drop_head_impl( std::make_index_sequence<sizeof...(Rest)>(), tup );
}
template<typename Head>
static constexpr const std::tuple<Head>
func_impl( const std::tuple<typename Head::initer_t> initer )
{
return std::tuple<Head>( { std::get<0>( initer ) } );
}
template<typename Head, typename Next, typename... Rest>
static constexpr const std::tuple<Head, Next, Rest...>
func_impl( const std::tuple<typename Head::initer_t, typename Next::initer_t, typename Rest::initer_t...> initer )
{
std::tuple<Head> head( { std::get<0>( initer ) } );
return std::tuple_cat( head, func_impl<Next, Rest...>( drop_head(initer) ) );
}
static constexpr const std::tuple<Elems...>
func( const std::tuple<typename Elems::initer_t...> initer )
{
return func_impl<Elems...>( initer );
}
};
// Elements are the end points of a Widget hierarchy
struct Element
{
using initer_t = int;
Element( const initer_t pIniter )
:data{ pIniter }
{
printf( "Creating %i\n", data );
}
const initer_t data;
};
// A Widget class stores any number of Elements and/or other Widget instances
template<typename... Elems>
struct Widget
{
using initer_t = std::tuple<typename Elems::initer_t...>;
Widget( const initer_t pIniter )
:elements( construct<Elems...>::func( pIniter ) )
{}
const std::tuple<Elems...> elements;
};
int main()
{
using Button = Widget<Element, Element>;
using ButtonList = Widget<Button, Button, Element>;
Button::initer_t basic_button_initer{ 0, 1 }; // presets for Buttons
Button::initer_t other_button_initer{ 2, 3 };
ButtonList::initer_t buttonlist_initer{ basic_button_initer, other_button_initer, 4 }; //a preset for a ButtonList
ButtonList buttonlist{ buttonlist_initer };
return 0;
}
The construct
structure takes the tuple of initer_t
s (initer), constructs a tuple containing the first element of Elems...
using the first element of initer, then drops the first element of initer and passes the remaining tuple to itself, which causes a tuple with the next element of Elems...
to be constructed using the next element in initer. This recursion is stopped by an overload of func_impl
for a tuple with one element which simply constructs that element from its initer_t
in a tuple and returns it. This single-element tuple gets concatenated to the tuple with the previous element, the result gets returned to the higher level and is concatenated to the single-element tuple there and so on.