1//
2// immer: immutable data structures for C++
3// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4//
5// This software is distributed under the Boost Software License, Version 1.0.
6// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7//
8
9#pragma once
10
11#include <type_traits>
12
13#if __GNUC__ == 7 && __GNUC_MINOR__ == 1
14#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 1
15#define immer_offsetof(st, m) ((std::size_t) &(((st*)0)->m))
16#else
17#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 0
18#define immer_offsetof offsetof
19#endif
20
21namespace immer {
22namespace detail {
23
24//
25// Metafunction that returns a standard layout struct that combines
26// all the standard layout types in `Ts...`, while making sure that
27// empty base optimizations are used.
28//
29// To query a part of the type do `get<my_part>(x)`;
30//
31// This is useful when putting together a type that merges various
32// types coming from different policies. Some of them might be empty,
33// so we shall enable empty base optimizations. But if we just
34// inherit from all of them, we would break the "standard layout"
35// rules, preventing us from using `offseof(...)`. So metafunction
36// will generate the type by sometimes inheriting, sometimes adding as
37// member.
38//
39// Note that the types are added to the combined type from right to
40// left!
41//
42template <typename... Ts>
43struct combine_standard_layout;
44
45template <typename... Ts>
46using combine_standard_layout_t = typename combine_standard_layout<Ts...>::type;
47
48namespace csl {
49
50template <typename T>
51struct type_t {};
52
53template <typename U, typename T>
54U& get(T& x);
55
56template <typename U, typename T>
57const U& get(const T& x);
58
59template <typename T, typename Next=void>
60struct inherit
61{
62 struct type : T, Next
63 {
64 using Next::get_;
65
66 template <typename U>
67 friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
68 template <typename U>
69 friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
70
71 T& get_(type_t<T>) { return *this; }
72 const T& get_(type_t<T>) const { return *this; }
73 };
74};
75
76template <typename T>
77struct inherit<T, void>
78{
79 struct type : T
80 {
81 template <typename U>
82 friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
83 template <typename U>
84 friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
85
86 T& get_(type_t<T>) { return *this; }
87 const T& get_(type_t<T>) const { return *this; }
88 };
89};
90
91template <typename T, typename Next=void>
92struct member
93{
94 struct type : Next
95 {
96 T d;
97
98 using Next::get_;
99
100 template <typename U>
101 friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
102 template <typename U>
103 friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
104
105 T& get_(type_t<T>) { return d; }
106 const T& get_(type_t<T>) const { return d; }
107 };
108};
109
110template <typename T>
111struct member<T, void>
112{
113 struct type
114 {
115 T d;
116
117 template <typename U>
118 friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
119 template <typename U>
120 friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
121
122 T& get_(type_t<T>) { return d; }
123 const T& get_(type_t<T>) const { return d; }
124 };
125};
126
127template <typename T, typename Next>
128struct member_two
129{
130 struct type
131 {
132 Next n;
133 T d;
134
135 template <typename U>
136 friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
137 template <typename U>
138 friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
139
140 T& get_(type_t<T>) { return d; }
141 const T& get_(type_t<T>) const { return d; }
142
143 template <typename U>
144 auto get_(type_t<U> t) -> decltype(auto) { return n.get_(t); }
145 template <typename U>
146 auto get_(type_t<U> t) const -> decltype(auto) { return n.get_(t); }
147 };
148};
149
150template <typename... Ts>
151struct combine_standard_layout_aux;
152
153template <typename T>
154struct combine_standard_layout_aux<T>
155{
156 static_assert(std::is_standard_layout<T>::value, "");
157
158 using type = typename std::conditional_t<
159 std::is_empty<T>::value,
160 csl::inherit<T>,
161 csl::member<T>>::type;
162};
163
164template <typename T, typename... Ts>
165struct combine_standard_layout_aux<T, Ts...>
166{
167 static_assert(std::is_standard_layout<T>::value, "");
168
169 using this_t = T;
170 using next_t = typename combine_standard_layout_aux<Ts...>::type;
171
172 static constexpr auto empty_this = std::is_empty<this_t>::value;
173 static constexpr auto empty_next = std::is_empty<next_t>::value;
174
175 using type = typename std::conditional_t<
176 empty_this, inherit<this_t, next_t>,
177 std::conditional_t<
178 empty_next, member<this_t, next_t>,
179 member_two<this_t, next_t>>>::type;
180};
181
182} // namespace csl
183
184using csl::get;
185
186template <typename... Ts>
187struct combine_standard_layout
188{
189 using type = typename csl::combine_standard_layout_aux<Ts...>::type;
190#if !IMMER_BROKEN_STANDARD_LAYOUT_DETECTION
191 static_assert(std::is_standard_layout<type>::value, "");
192#endif
193};
194
195} // namespace detail
196} // namespace immer
197