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.
First, some helpers to facilitate index mapping. And because I'm lazy, I modified
typelist
slightly.Given a properly built
index_map
, converting from old index to new index is simple, leveraging template argument deduction and overload resolution: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.
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 this, partitioning a
typelist
ofindex_map_leaf
s is simplypartition_t<LeafList, project_type<F>>
.Finally, we transform the partitioned list, adding the new indices.
Bringing it all together,
With a little utility to convert the arguments of an arbitrary template to a type list:
We can do the partitioning only once, by constructing the reordered tuple type directly from the
index_map
.For example, given
The following assertions hold:
Demo.
P.S. Here's my version of
filter
:First, let's start with the basics. We need a way to turn a template template (
std::tuple
) into a metafunction class:And a typelist:
And something to go between them:
So that given some typelist, we can just have:
Now, we just need a partitioner on some criteria:
Where
concat
:filter
:And
not_
:Which, given
some_typelist
of types that you want to put in yourtuple
becomes: