fork(4) download
  1. #include <cstddef>
  2. #include <tuple>
  3. #include <type_traits>
  4.  
  5. template <typename T>
  6. struct identity { using type = T; };
  7.  
  8. template <typename T, std::size_t I>
  9. struct indexed {};
  10.  
  11. template <std::size_t I>
  12. using SizeT = std::integral_constant<std::size_t, I>;
  13.  
  14. template <template <typename...> class Template, typename... T>
  15. struct lazy_apply {
  16. using type = Template<typename T::type...>;
  17. };
  18.  
  19. template <typename T, typename Tuple>
  20. struct const_tuple;
  21. template <typename Head, typename... Tail>
  22. struct cons_tuple<Head, std::tuple<Tail...>>
  23. : identity<std::tuple<Head, Tail...>> {};
  24. template <typename T, typename Tuple>
  25. using ConsTuple = typename cons_tuple<T, Tuple>::type;
  26.  
  27. template <typename T, typename U>
  28. struct layout_first {
  29. #ifndef _LIBCPP_VERSION
  30. using first = T;
  31. using second = U;
  32. #else
  33. using first = U;
  34. using second = T;
  35. #endif
  36.  
  37. constexpr static bool value = std::is_empty<first>::value
  38. || (!std::is_empty<second>::value && alignof(first) > alignof(second));
  39. };
  40.  
  41. template <typename T, typename Tuple>
  42. struct insert_sorted
  43. : identity<std::tuple<T>> {};
  44. template <typename T, std::size_t I, typename TFirst, typename... TSorted, std::size_t IFirst, std::size_t... ISorted>
  45. struct insert_sorted<indexed<T, I>, std::tuple<indexed<TFirst, IFirst>, indexed<TSorted, ISorted>...>>
  46. : std::conditional<
  47. layout_first<T, TFirst>::value,
  48. identity<std::tuple<indexed<T, I>, indexed<TFirst, IFirst>, indexed<TSorted, ISorted>...>>,
  49. lazy_apply<ConsTuple, identity<indexed<TFirst, IFirst>>, insert_sorted<indexed<T, I>, std::tuple<indexed<TSorted, ISorted>...>>>
  50. >::type {};
  51.  
  52. template <typename T, typename Tuple>
  53. using InsertSorted = typename insert_sorted<T, Tuple>::type;
  54.  
  55. template <typename Tuple>
  56. struct split;
  57. template <typename... T, std::size_t... I>
  58. struct split<std::tuple<indexed<T, I>...>> {
  59. using types = std::tuple<T...>;
  60. using indices = std::tuple<SizeT<I>...>;
  61. }
  62. template <typename T>
  63. using ExtractTypes = typename split<T>::types;
  64. template <typename T>
  65. using ExtractIndices = typename split<T>::indices;
  66.  
  67. template <typename Acc, typename T>
  68. struct perfect_layout_impl {
  69. using tuples = ExtractTypes<Acc>;
  70. using map = ExtractIndices<Acc>;
  71. };
  72.  
  73. template <typename Acc, typename Head, typename... Tail>
  74. struct perfect_layout_impl<Acc, std::tuple<Head, Tail...>>
  75. : perfect_layout_impl<InsertSorted<Head, Acc>, std::tuple<Tail...>> {};
  76.  
  77. template <typename Acc, typename... T>
  78. struct with_indices_impl : identity<Acc> {};
  79. template <typename Acc, typename Head, typename... Tail>
  80. struct with_indices_impl<std::tuple<Acc..., indexed<Head, sizeof...(Acc)>>, Tail...> {};
  81. template <typename... T>
  82. struct with_indices : with_indices_impl<std::tuple<>, T...> {};
  83. template <typename... T>
  84. using WithIndices = typename with_indices<T...>::type;
  85.  
  86. template <typename... T>
  87. struct perfect_layout
  88. : perfect_layout_impl<std::tuple<>, WithIndices<T...>> {};
  89.  
  90.  
  91. template <typename... T>
  92. using PerfectLayoutTuple = typename perfect_layout<T...>::tuple;
  93. template <typename... T>
  94. using PerfectLayoutMap = typename perfect_layout<T...>::map;
  95.  
  96. template <std::size_t I, typename T>
  97. using TupleElement = typename std::tuple_element<I, T>::type;
  98.  
  99. template <std::size_t... Indices>
  100. struct indices {};
  101.  
  102. template <typename T>
  103. struct eval_indices;
  104. template <typename... S>
  105. struct eval_indices<std::tuple<S...>>
  106. : identity<indices<S::value...>> {};
  107.  
  108. template <typename T>
  109. using EvalIndices = typename eval_indices<T>::type;
  110.  
  111. template <typename... T, std::size_t... Indices>
  112. std::tuple<TupleElement<Indices, std::tuple<T...>>...> forward_for_my_tuple(indices<Indices...>, T&&...) {
  113. auto fwd = std::forward_as_tuple(std::forward<T>(t)...);
  114. return std::forward_as_tuple(std::forward<TupleElement<Indices, std::tuple<T...>>>(std::get<Indices>(fwd))...);
  115. }
  116.  
  117. template <typename... T>
  118. struct my_tuple {
  119. public:
  120. my_tuple() = default;
  121. explicit my_tuple(T const&... t)
  122. : inner(forward_for_my_tuple(EvalIndices<PerfectLayoutMap<T...>>(), t...) {}
  123.  
  124. template <typename... U>
  125. explicit my_tuple(U&&... u)
  126. : inner(forward_for_my_tuple(EvalIndices<PerfectLayoutMap<T...>>(), u...) {}
  127.  
  128. template <std::size_t I, typename... T1>
  129. friend TupleElement<I, std::tuple<T1...>>& get(my_tuple<T1...>& t) noexcept;
  130. template <std::size_t I, typename... T1>
  131. friend TupleElement<I, std::tuple<T1...>>&& get(my_tuple<T1...>&& t) noexcept;
  132. template <std::size_t I, typename... T1>
  133. friend TupleElement<I, std::tuple<T1...>> const& get(my_tuple<T1...> const& t) noexcept;
  134.  
  135. private:
  136. PerfectLayoutTuple<T...> inner;
  137. };
  138.  
  139. template <std::size_t I, typename... T1>
  140. TupleElement<I, std::tuple<T1...>>& get(my_tuple<T1...>& t) noexcept {
  141. return std::get<TupleElement<I, PerfectLayoutMap<T...>>::value>(t.inner);
  142. }
  143. template <std::size_t I, typename... T1>
  144. TupleElement<I, std::tuple<T1...>>&& get(my_tuple<T1...>&& t) noexcept {
  145. return std::get<TupleElement<I, PerfectLayoutMap<T...>>::value>(std::move(t.inner));
  146. }
  147. template <std::size_t I, typename... T1>
  148. TupleElement<I, std::tuple<T1...>> const& get(my_tuple<T1...> const& t) noexcept {
  149. return std::get<TupleElement<I, PerfectLayoutMap<T...>>::value>(t.inner);
  150. }
  151.  
  152. /****** TESTING PART ******/
  153.  
  154.  
  155. struct empty0 {};
  156. struct empty1 {};
  157. struct empty2 {};
  158.  
  159. template <std::size_t Size, std::size_t Align>
  160. using Layout = typename std::aligned_storage<Size, Align>::type;
  161.  
  162. using type1 = Layout<1,1>;
  163. using type2 = Layout<2,2>;
  164. using type4 = Layout<4,4>;
  165. using type8 = Layout<8,8>;
  166.  
  167. static_assert(sizeof(my_tuple<empty0, empty1, empty2, type8>) == sizeof(type8),
  168. "Empty base class optimization should be done on prefix empties");
  169. static_assert(sizeof(my_tuple<type8, empty0, empty1, empty2>) == sizeof(type8),
  170. "Empty base class optimization should be done on suffix empties");
  171. static_assert(sizeof(my_tuple<type8, empty0, empty1, empty2, type8>) == 2*sizeof(type8),
  172. "Empty base class optimization should be done on middle empties");
  173.  
  174.  
  175. static_assert(sizeof(my_tuple<empty0, empty1, empty2, type8, type4, type2, type1, type1>) == sizeof(type8) + sizeof(type4) + sizeof(type2) + 2*sizeof(type1),
  176. "Perfect layout should not be ruined");
  177. static_assert(sizeof(my_tuple<type1, type2, empty0, empty1, type1, type8, empty2, type4>) == sizeof(type8) + sizeof(type4) + sizeof(type2) + 2*sizeof(type1),
  178. "Perfect layout should be arranged");
  179.  
  180. #include <cassert>
  181.  
  182. int main() {
  183. my_tuple<int, empty0, empty1, double, empty2, int> t { 1, {}, {}, 0.5, {}, 2 };
  184. // reordering should be invisible to the client
  185. static_assert(std::is_same<decltype(get<0>(t)), int&>::value, "");
  186. static_assert(std::is_same<decltype(get<1>(t)), empty0&>::value, "");
  187. static_assert(std::is_same<decltype(get<2>(t)), empty1&>::value, "");
  188. static_assert(std::is_same<decltype(get<3>(t)), double&>::value, "");
  189. static_assert(std::is_same<decltype(get<4>(t)), empty2&>::value, "");
  190. static_assert(std::is_same<decltype(get<5>(t)), int&>::value, "");
  191. assert(get<0>(t) == 1);
  192. assert(get<3>(t) == 0.5);
  193. assert(get<5>(t) == 2);
  194. get<0>(t) = { 3 };
  195. get<3>(t) = { 2.5 };
  196. get<5>(t) = { 5 };
  197. assert(get<0>(t) == 3);
  198. assert(get<3>(t) == 2.5);
  199. assert(get<5>(t) == 4);
  200. }
  201.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty