Skip to content

Instantly share code, notes, and snippets.

@talybin
Last active December 20, 2019 22:29
Show Gist options
  • Select an option

  • Save talybin/0ecb564de2d1eed9fb29893e99aa5595 to your computer and use it in GitHub Desktop.

Select an option

Save talybin/0ecb564de2d1eed9fb29893e99aa5595 to your computer and use it in GitHub Desktop.
Sequence order tuple implementation (e.g order not inverted)
#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';
}
@talybin

talybin commented Nov 1, 2019

Copy link
Copy Markdown
Author
tuple size: 14
 2a  0 c3 f5 48 40 dd 24  6 81 95 43 f2 3f
struct size: 14
 2a  0 c3 f5 48 40 dd 24  6 81 95 43 f2 3f
using get by index:
  0: 42
  1: 3.14
  2: 1.1415
using get by type:
  0: 42
  1: 3.14
  2: 1.1415
structured binding:
 a1: 42
 a2: 3.14
 a3: 1.1415

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment