I believe that I have a relatively simple solution with a variation pattern.
Implementation will require several helpers, so I will introduce it back so you can get it first.
template <typename... Args> class OptimizedLayout { public: template <size_t I> auto at() -> decltype(std::get<Index<I>::value>(_storage)) { return std::get<Index<I>::value>(_storage); } template <size_t I> auto at() const -> decltype(std::get<Index<I>::value>(_storage)) { return std::get<Index<I>::value>(_storage); } private: using Indexed = /**/; // pairs of sorted Args (by decreasing size) // and their original index in the argument list using Storage = /*std::tuple<Indexed.first ...>*/; template <size_t I> using Index = /*index of element of Indexed whose .second is I*/; Storage _storage; }; // class OptimizedLayout
The main advantage here is that changing the layout of the elements only affects how Indexed is determined, so you can easily improve the algorithm. At the moment, I just provided the equivalent of your template.
Disclaimer: the following code is not verified, it may not even compile, not to mention getting the correct result.
I. Creating indexes.
An explanation can be found in the living room , we can reuse it to create a package of pairs (type, index). To sort it, we will use the MPL algorithm, so itβs easier to create a package as an MPL vector.
template <std::size_t... Is> struct indices {}; template <std::size_t N, std::size_t... Is> struct build_indices : build_indices<N-1, N-1, Is...> {}; template <std::size_t... Is> struct build_indices<0, Is...> { using type = indices<Is...>; }; template <typename Tuple, typename Indices> struct IndexedImpl; template <typename... T, size_t... I> struct IndexedImpl< std::tuple<T...>, indices<I...> > { using type = boost::mpl::vector< std::pair<T, I>... >; }; template <typename Tuple> using Indexed = IndexedImpl<Tuple, typename build_indices<std::tuple_size<Tuple>::value>::type>;
II. Sorting
For sorting, we will use the MPL sorting algorithm, which works with types.
struct GreaterSize { template <typename T, typename U> struct apply { using type = boost::mpl::bool_<sizeof(T) > sizeof(U)>; }; }; template <typename T> struct TupleInserter { using state = T; template <typename Seq, typename E> struct apply; }; template <typename T> template <typename... Args, typename E> struct TupleInserter<T>::apply<std::tuple<Args...>, E> { using type = std::tuple<Args..., E>; }; template <typename Tuple> using SortedSequence = boost::mpl::sort< typename Indexed<Tuple>::type, GreaterSize, TupleInserter >;
III. Storage Class Calculation
Now we only need to calculate the storage class, which is performed by extracting the first element of each pair. Interestingly, pattern matching can really help here.
template <typename T> struct TupleFirstExtractor; template <typename... T, size_t... I> struct TupleFirstExtractor<std::tuple<std::pair<T, I>...>> { using type = std::tuple<T...>; };
IV. Index Solver Calculation
template <typename Tuple, size_t Needle, size_t Acc> struct IndexFinderImpl; template <typename H, size_t h, typename... Tail, size_t Needle, size_t Acc> struct IndexFinderImpl<std::tuple<std::pair<H,h>, Tail...>, Needle, Acc>: IndexFinderImpl<std::tuple<Tail...>, Needle, Acc+1> {}; template <typename H, typename... Tail, size_t Needle, size_t Acc> struct IndexFinderImpl<std::tuple<std::pair<H, Needle>, Tail...>, Needle, Acc>: std::integral_constant<size_t, Acc> {};
V. Putting it all together
And now we connect everything:
template <typename... Args> class OptimizedLayout { public: template <size_t I> auto at() -> decltype(std::get<Index<I>::value>(_storage)) { return std::get<Index<I>::value>(_storage); } template <size_t I> auto at() const -> decltype(std::get<Index<I>::value>(_storage)) { return std::get<Index<I>::value>(_storage); } private: using Indexed = typename SortedSequence<std::tuple<Args...>>::type; using Storage = typename TupleFirstExtractor<Indexed>::type; template <size_t I> using Index = IndexFinderImpl<Indexed, I, 0>; Storage _storage; }; // class OptimizedLayout
Tip. I recommend using a specialized namespace to store all helpers. Although they can be defined in a template, they are easier to define since they are not dependent on Args... however you will need to isolate them to avoid collisions with other parts of your program.