Last active
December 20, 2019 22:29
-
-
Save talybin/0ecb564de2d1eed9fb29893e99aa5595 to your computer and use it in GitHub Desktop.
Sequence order tuple implementation (e.g order not inverted)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <iomanip> | |
| #include <tuple> | |
| // TODO fix alignment to match struct alignment | |
| // Empty list | |
| template <class...> | |
| struct seq_tuple | |
| { }; | |
| // Sequence of types | |
| #pragma pack(push, 2) | |
| template <class First, class... Rest> | |
| struct seq_tuple<First, Rest...> : seq_tuple<First> | |
| { | |
| constexpr seq_tuple() = default; | |
| template <class Arg1, class... Args, | |
| class = std::enable_if_t<std::is_constructible_v<First, Arg1>> > | |
| constexpr seq_tuple(Arg1&& first, Args&&... rest) | |
| : seq_tuple<First>(std::forward<Arg1>(first)) | |
| , rest(std::forward<Args>(rest)...) | |
| { } | |
| seq_tuple<Rest...> rest; | |
| }; | |
| #pragma pack(pop) | |
| // Value holder | |
| template <class T> | |
| struct alignas(2) seq_tuple<T> | |
| { | |
| constexpr seq_tuple() = default; | |
| template <class Arg, | |
| class = std::enable_if_t<std::is_constructible_v<T, Arg>> > | |
| constexpr seq_tuple(Arg&& value) | |
| : value_(std::forward<Arg>(value)) | |
| { } | |
| const T& get() const & noexcept { return value_; } | |
| T& get() & noexcept { return value_; } | |
| const T&& get() const && noexcept { return std::move(value_); } | |
| T&& get() && noexcept { return std::move(value_); } | |
| private: | |
| T value_; | |
| }; | |
| // Type identifier | |
| template <class T> | |
| struct is_seq_tuple : std::false_type { }; | |
| template <class... T> | |
| struct is_seq_tuple<seq_tuple<T...>> : std::true_type { }; | |
| template <class T> | |
| inline constexpr bool is_seq_tuple_v = is_seq_tuple<T>::value; | |
| // Returns the first apparence of T in Ts. | |
| // Returns sizeof...(Types) if T is not in Ts. | |
| template <class T, class... Ts> | |
| struct index_of : std::integral_constant<size_t, 0> { }; | |
| template <class T, class... Ts> | |
| inline constexpr size_t index_of_v = index_of<T, Ts...>::value; | |
| template <class T, template <class...> class L, class... Ts> | |
| struct index_of<T, L<Ts...>> : index_of<T, Ts...> { }; | |
| template <class T, class First, class... Rest> | |
| struct index_of<T, First, Rest...> | |
| : std::integral_constant<size_t, | |
| std::is_same_v<T, First> ? 0 : index_of_v<T, Rest...> + 1> | |
| { }; | |
| // Get by index methods | |
| template <class T, | |
| std::enable_if_t<is_seq_tuple_v<std::decay_t<T>>, int> = 0> | |
| constexpr decltype(auto) get(std::in_place_index_t<0>, T&& t) { | |
| return std::forward<T>(t).get(); | |
| } | |
| template <size_t I, class T, | |
| std::enable_if_t<is_seq_tuple_v<std::decay_t<T>>, int> = 0> | |
| constexpr decltype(auto) get(std::in_place_index_t<I>, T&& t) { | |
| return get(std::in_place_index<I - 1>, std::forward<T>(t).rest); | |
| } | |
| template <size_t I, class T, | |
| std::enable_if_t<is_seq_tuple_v<std::decay_t<T>>, int> = 0> | |
| constexpr decltype(auto) get(T&& t) { | |
| return get(std::in_place_index<I>, std::forward<T>(t)); | |
| } | |
| // Get by type method | |
| template <class U, class T, | |
| std::enable_if_t<is_seq_tuple_v<std::decay_t<T>>, int> = 0> | |
| constexpr decltype(auto) get(T&& t) { | |
| return get<index_of_v<U, std::decay_t<T>>>(std::forward<T>(t)); | |
| } | |
| // Structured binding declarations | |
| template <class... T> | |
| struct std::tuple_size<seq_tuple<T...>> | |
| : std::integral_constant<size_t, sizeof...(T)> | |
| { }; | |
| template <size_t I, class... T> | |
| struct std::tuple_element<I, seq_tuple<T...>> | |
| : std::tuple_element<I, std::tuple<T...>> | |
| { }; | |
| static_assert(std::is_same_v< | |
| decltype(get<0>(std::declval<seq_tuple<int, float>&>())), int&>); | |
| static_assert(std::is_same_v< | |
| decltype(get<0>(std::declval<seq_tuple<int, float>>())), int&&>); | |
| template <class T> | |
| void print(const T& buf) | |
| { | |
| auto ptr = reinterpret_cast<const unsigned char*>(&buf); | |
| std::cout << std::hex; | |
| for (size_t i = 0; i < sizeof(buf); ++i) | |
| std::cout << std::setw(3) << int(ptr[i]); | |
| std::cout << std::dec << '\n'; | |
| } | |
| int main() | |
| { | |
| seq_tuple<short, float, double> x; | |
| seq_tuple<short, float, double> t(42, 3.14f, 1.1415); | |
| std::cout << "tuple size: " << sizeof(t) << "\n"; | |
| print(t); | |
| #pragma pack(push, 2) | |
| struct test_s { | |
| #if 0 | |
| alignas(8) short x = 42; | |
| alignas(8) float y = 3.14f; | |
| alignas(8) double z = 1.1415; | |
| #else | |
| short x = 42; | |
| float y = 3.14f; | |
| double z = 1.1415; | |
| #endif | |
| }; | |
| #pragma pack(pop) | |
| test_s s; | |
| std::cout << "struct size: " << sizeof(s) << "\n"; | |
| print(s); | |
| std::cout << "using get by index:\n"; | |
| std::cout << " 0: " << get<0>(t) << '\n'; | |
| std::cout << " 1: " << get<1>(t) << '\n'; | |
| std::cout << " 2: " << get<2>(t) << '\n'; | |
| std::cout << "using get by type:\n"; | |
| std::cout << " 0: " << get<short>(t) << '\n'; | |
| std::cout << " 1: " << get<float>(t) << '\n'; | |
| std::cout << " 2: " << get<double>(t) << '\n'; | |
| std::cout << "structured binding:\n"; | |
| auto& [a1, a2, a3] = t; | |
| std::cout << " a1: " << a1 << '\n'; | |
| std::cout << " a2: " << a2 << '\n'; | |
| std::cout << " a3: " << a3 << '\n'; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.