Skip to content

Commit

Permalink
Add "structured_binding_traits.h" based on ideas from Michael Park.
Browse files Browse the repository at this point in the history
Michael says that something like this code is being used in
https://github.com/mpark/patterns

I took his code ( https://wandbox.org/permlink/tgjQkyHqzV9p2v3D ),
simplified it and lowered it to the C++14 subset accepted by Xcode.
  • Loading branch information
Quuxplusone committed Jan 30, 2018
1 parent b35c6f1 commit 32185b0
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 0 deletions.
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

0 comments on commit 32185b0

Please sign in to comment.