Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "structured_binding_traits.h" based on ideas from Michael Park. #124

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions SG14/structured_binding_traits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#pragma once

#include <cstddef>
#include <type_traits>
#include <tuple>

#ifndef AGGREGATE_SIZE_FUDGE
#define AGGREGATE_SIZE_FUDGE 2
#endif

#if __cplusplus >= 201703L
#define INLINE_IN_CXX17 inline
#else
#define INLINE_IN_CXX17
#endif

namespace stdext {

#if __cplusplus >= 201703L
using std::is_aggregate;
using std::is_aggregate_v;
#elif __has_extension(is_aggregate)
template<class T> struct is_aggregate : std::integral_constant<bool, __is_aggregate(T)> {};
template<class T> INLINE_IN_CXX17 constexpr bool is_aggregate_v = is_aggregate<T>::value;
#else
template<class T> struct is_aggregate : std::integral_constant<bool, std::is_class<T>::value || std::is_array<T>::value> {};
template<class T> INLINE_IN_CXX17 constexpr bool is_aggregate_v = is_aggregate<T>::value;
#endif

namespace detail {
template<class T, class> struct is_tuple_like_impl : std::false_type {};
template<class T> struct is_tuple_like_impl<T, decltype(void(std::tuple_size<T>::value))> : std::true_type {};
} // namespace detail

template<class T> struct is_tuple_like : detail::is_tuple_like_impl<T, void> {};
template<class T> INLINE_IN_CXX17 constexpr bool is_tuple_like_v = is_tuple_like<T>::value;

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
namespace detail {
struct convertible_to_anything { template<class T> operator T() const noexcept; };
template<class T, class, class> struct is_n_constructible_impl : std::false_type {};
template<class T, size_t... Is> struct is_n_constructible_impl<T, std::index_sequence<Is...>, decltype(void(T{(void(Is), convertible_to_anything{})...}))> : std::true_type {};
} // namespace detail
#pragma GCC diagnostic pop

template<size_t N, class T> struct is_n_constructible : detail::is_n_constructible_impl<T, std::make_index_sequence<N>, void> {};
template<size_t N, class T> INLINE_IN_CXX17 constexpr bool is_n_constructible_v = is_n_constructible<N, T>::value;

namespace detail {
template<class T, size_t M, bool StopHere> struct aggregate_size_impl2 : aggregate_size_impl2<T, M-1, is_n_constructible_v<M-1, T>> {};
template<class T, size_t M> struct aggregate_size_impl2<T, M, true> : std::integral_constant<size_t, M> {};
template<class T> struct aggregate_size_impl2<T, 0, false> {};

template<class T, bool> struct aggregate_size_impl : aggregate_size_impl2<T, sizeof (T) + AGGREGATE_SIZE_FUDGE, is_n_constructible_v<sizeof (T) + AGGREGATE_SIZE_FUDGE, T>> {};
template<class T> struct aggregate_size_impl<T, false> {};
} // namespace detail

template<class T> struct aggregate_size : detail::aggregate_size_impl<T, is_aggregate<T>::value> {};
template<class T> INLINE_IN_CXX17 constexpr auto aggregate_size_v = aggregate_size<T>::value;

namespace detail {
template<class T, size_t M, class> struct is_structured_bindable_impl : std::false_type {};
template<class T, size_t M> struct is_structured_bindable_impl<T, M, std::enable_if_t<M != 0 && is_tuple_like<T>::value && std::tuple_size<T>::value == M>> : std::true_type {};
template<class T, size_t M> struct is_structured_bindable_impl<T, M, std::enable_if_t<M != 0 && !is_tuple_like<T>::value && is_aggregate<T>::value && aggregate_size<T>::value == M>> : std::true_type {};
} // namespace detail

template<size_t N, class T> struct is_structured_bindable : detail::is_structured_bindable_impl<T, N, void> {};
template<size_t N, class T> INLINE_IN_CXX17 constexpr bool is_structured_bindable_v = is_structured_bindable<N, T>::value;

} // namespace stdext
1 change: 1 addition & 0 deletions SG14_test/SG14_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace sg14_test
void plf_colony_test();
void ring_test();
void slot_map_test();
void structured_binding_traits_test();
void transcode_test();
void uninitialized_test();
void unstable_remove_test();
Expand Down
1 change: 1 addition & 0 deletions SG14_test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ int main(int, char *[])
sg14_test::plf_colony_test();
sg14_test::ring_test();
sg14_test::slot_map_test();
sg14_test::structured_binding_traits_test();
sg14_test::transcode_test();
sg14_test::uninitialized_test();
sg14_test::unstable_remove_test();
Expand Down
118 changes: 118 additions & 0 deletions SG14_test/structured_binding_traits_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include "SG14_test.h"
#include "structured_binding_traits.h"

#include <array>
#include <complex>
#include <memory>
#include <tuple>
#include <vector>

struct key_16_8_t {
uint16_t index;
uint8_t generation;
};
struct key_11_5_t {
uint16_t index : 11;
uint8_t generation : 5;
};
struct unique_view {
std::unique_ptr<char> p;
int z;
};
struct tuple_ish {
tuple_ish(int x, int y) : x(x), y(y) {}
int x, y;
};
namespace std { template<> class tuple_size<tuple_ish> : public std::integral_constant<size_t, 2> {}; }

template<template<class> class Pred, class T, bool B>
int static_assert_predicate()
{
static_assert(Pred<T>::value == B, "");
return 0;
}

template<template<class> class Pred, class... Ts>
void static_assert_true()
{
int dummy[] = {
static_assert_predicate<Pred, Ts, true>() ...
};
(void)dummy;
}

template<template<class> class Pred, class... Ts>
void static_assert_false()
{
int dummy[] = {
static_assert_predicate<Pred, Ts, false>() ...
};
(void)dummy;
}

template<class T> using is_0_constructible = stdext::is_n_constructible<0, T>;
template<class T> using is_1_constructible = stdext::is_n_constructible<1, T>;
template<class T> using is_2_constructible = stdext::is_n_constructible<2, T>;
template<class T> using is_3_constructible = stdext::is_n_constructible<3, T>;

template<class T> using is_aggregate_size_0 = std::integral_constant<bool, stdext::aggregate_size<T>::value == 0>;
template<class T> using is_aggregate_size_1 = std::integral_constant<bool, stdext::aggregate_size<T>::value == 1>;
template<class T> using is_aggregate_size_2 = std::integral_constant<bool, stdext::aggregate_size<T>::value == 2>;

template<class T> using is_bindable_0 = stdext::is_structured_bindable<0, T>;
template<class T> using is_bindable_1 = stdext::is_structured_bindable<1, T>;
template<class T> using is_bindable_2 = stdext::is_structured_bindable<2, T>;

void sg14_test::structured_binding_traits_test()
{
using A0 = int[0];
using A1 = int[1];
using A2 = int[2];
using F = int(int);
using I = int;
using IP = int*;
using K2 = key_16_8_t;
using KB2 = key_11_5_t;
using KU2 = unique_view;
using SA0 = std::array<int, 0>;
using SA1 = std::array<int, 1>;
using SA2 = std::array<int, 2>;
using SC2 = std::complex<double>;
using SP = std::pair<int, int>;
using ST0 = std::tuple<>;
using ST1 = std::tuple<int>;
using ST2 = std::tuple<int, double>;
using SV = std::vector<int>;
using T2 = tuple_ish;

static_assert_true<stdext::is_tuple_like, SA0, SA1, SA2, SP, ST0, ST1, ST2, T2>();
static_assert_false<stdext::is_tuple_like, K2, KB2, A0, A1, A2, F, I, IP, SC2, SV>();

static_assert_true<is_0_constructible, K2, KB2, KU2, A0, A1, A2, I, IP, SA0, SA1, SA2, SC2, SP, ST0, ST1, ST2, SV>();
static_assert_false<is_0_constructible, F, T2>();

static_assert_true<is_1_constructible, A1, A2, I, IP, K2, KB2, KU2, SA0, SA1, SA2, SC2, SP, ST1, ST2, SV, T2>();
static_assert_false<is_1_constructible, A0, F>();

static_assert_true<is_2_constructible, A2, K2, KB2, KU2, SA2, SC2, SP, ST2, T2>();
static_assert_false<is_2_constructible, A0, A1, SA0, SA1, F, I, IP>();

static_assert_true<is_3_constructible>();
static_assert_false<is_3_constructible, A0, A1, A2, F, I, IP, K2, KB2, KU2, SA0, SA1, SA2, SC2, SP, T2>();

static_assert_true<is_aggregate_size_1, SA1>();
static_assert_true<is_aggregate_size_2, K2, KB2, SA2>();

static_assert_false<is_bindable_0, A0, SA0, ST0>();
static_assert_true<is_bindable_1, A1, SA1, ST1>();
static_assert_false<is_bindable_1, A2, K2, KB2, KU2, SA2, ST2, T2>();
static_assert_true<is_bindable_2, A2, K2, KB2, KU2, SA2, ST2, T2>();
static_assert_false<is_bindable_2, A1, SA1, SC2, ST1>();
}

#ifdef TEST_MAIN
int main()
{
sg14_test::structured_binding_traits_test();
}
#endif
1 change: 1 addition & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(SOURCE_FILES
${SG14_TEST_SOURCE_DIRECTORY}/plf_colony_test.cpp
${SG14_TEST_SOURCE_DIRECTORY}/ring_test.cpp
${SG14_TEST_SOURCE_DIRECTORY}/slot_map_test.cpp
${SG14_TEST_SOURCE_DIRECTORY}/structured_binding_traits_test.cpp
${SG14_TEST_SOURCE_DIRECTORY}/transcode_test.cpp
${SG14_TEST_SOURCE_DIRECTORY}/uninitialized_test.cpp
${SG14_TEST_SOURCE_DIRECTORY}/unstable_remove_test.cpp
Expand Down