Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | #pragma once |
||
8 | |||
9 | #include <cstddef> |
||
10 | #include <type_traits> |
||
11 | |||
12 | #ifndef DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION |
||
13 | #ifdef NDEBUG |
||
14 | #define DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION 0 |
||
15 | #else |
||
16 | #define DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION 1 |
||
17 | #endif |
||
18 | #endif |
||
19 | |||
20 | #if DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION |
||
21 | template <typename T> |
||
22 | struct strong_typedef; |
||
23 | #endif |
||
24 | |||
25 | /* Use a C++11 user-defined literal to convert a string literal into a |
||
26 | * type, so that it can be used as a template type parameter. |
||
27 | */ |
||
28 | template <typename T, T... v> |
||
29 | struct literal_as_type {}; |
||
30 | |||
31 | template <typename T, T... v> |
||
32 | constexpr literal_as_type<T, v...> operator""_literal_as_type(); |
||
33 | |||
34 | /* Given two types representing literals, return a type representing the |
||
35 | * concatenation of those literals. This function is never defined, and |
||
36 | * can only be used in unevaluated contexts. |
||
37 | */ |
||
38 | template <typename T, T... a, T... b> |
||
39 | constexpr literal_as_type<T, a..., b...> concatenate(literal_as_type<T, a...> &&, literal_as_type<T, b...> &&); |
||
40 | |||
41 | /* valptridx_specialized_types is never defined, but is specialized to |
||
42 | * define a typedef for a type-specific class suitable for use as a base |
||
43 | * of valptridx<T>. |
||
44 | */ |
||
45 | template <typename managed_type> |
||
46 | struct valptridx_specialized_types; |
||
47 | |||
48 | namespace valptridx_detail { |
||
49 | |||
50 | /* This type is never defined, but explicit specializations of it are |
||
51 | * defined to provide a mapping from literal_as_type<char, 'X'> to |
||
52 | * report_error_style::X, for each member X of report_error_style. |
||
53 | */ |
||
54 | template <typename> |
||
55 | struct literal_type_to_policy; |
||
56 | |||
57 | /* Given a C identifier, stringize it, then pass the string to |
||
58 | * operator""_literal_as_type() to produce a specialization of |
||
59 | * literal_as_type<...>, which can be used as a template type argument. |
||
60 | */ |
||
61 | #define DXX_VALPTRIDX_LITERAL_TO_TYPE2(A) #A##_literal_as_type |
||
62 | #define DXX_VALPTRIDX_LITERAL_TO_TYPE(A) decltype(DXX_VALPTRIDX_LITERAL_TO_TYPE2(A)) |
||
63 | |||
64 | /* Generate all the template parameters to one instantiation of |
||
65 | * error_style_dispatch. A macro is used due to the repeated occurrence |
||
66 | * of various boilerplate identifiers. |
||
67 | */ |
||
68 | #define DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_PARAMETERS(MC,TYPE) \ |
||
69 | DXX_VALPTRIDX_LITERAL_TO_TYPE(TYPE), \ |
||
70 | DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_##MC##_), DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_##MC##_##TYPE), \ |
||
71 | DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_), DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_##TYPE), \ |
||
72 | DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_##MC##_default), \ |
||
73 | DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_default) \ |
||
74 | |||
75 | class untyped_utilities |
||
76 | { |
||
77 | public: |
||
78 | /* Given a C identifier as PREFIX, and a C type identifier as TYPE, |
||
79 | * generate `concatenate(literal_as_type<PREFIX>, |
||
80 | * literal_as_type<TYPE>)` and `literal_as_type<PREFIX##TYPE>`. |
||
81 | * Compare them for type equality. If `PREFIX##TYPE` matches an |
||
82 | * active macro, it will be expanded to the value of the macro, but |
||
83 | * the concatenation of literal_as_type<PREFIX> and |
||
84 | * literal_as_type<TYPE> will not be expanded. This allows the |
||
85 | * template to detect whether `PREFIX##TYPE` is a defined macro |
||
86 | * (unless `PREFIX##TYPE` is defined as a macro that expands to its |
||
87 | * own name), and choose a branch of `std::conditional` accordingly. |
||
88 | * The true branch is chosen if `PREFIX##TYPE` is _not_ a macro, and |
||
89 | * is implemented by expanding to the third argument. The false |
||
90 | * branch is chosen if `PREFIX##TYPE` is a macro, and is implemented |
||
91 | * as a reference to `literal_type_to_policy`. |
||
92 | */ |
||
93 | #define DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH(PREFIX,TYPE,BRANCH_TRUE,...) \ |
||
94 | typename std::conditional< \ |
||
95 | std::is_same< \ |
||
96 | decltype(concatenate( \ |
||
97 | std::declval<PREFIX>(), std::declval<TYPE>() \ |
||
98 | )), PREFIX##TYPE>::value, \ |
||
99 | BRANCH_TRUE, ## __VA_ARGS__, literal_type_to_policy<PREFIX##TYPE> \ |
||
100 | >::type |
||
101 | |||
102 | /* Given specializations of `literal_as_type`, find the most |
||
103 | * specific error-reporting style and evaluate to |
||
104 | * `literal_type_to_policy` specialized on that style. Consumers |
||
105 | * can then access `error_style_dispatch<...>::value` to obtain the |
||
106 | * chosen error-reporting style as an enum member of |
||
107 | * report_error_style. |
||
108 | */ |
||
109 | template <typename managed_type, |
||
110 | typename style_qualified_, typename style_qualified_managed_type, |
||
111 | typename style_default_, typename style_default_managed_type, |
||
112 | typename style_qualified_default, |
||
113 | typename style_default> |
||
114 | using error_style_dispatch = |
||
115 | DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH(style_qualified_, managed_type, |
||
116 | DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH(style_default_, managed_type, |
||
117 | typename std::conditional< |
||
118 | std::is_same< |
||
119 | decltype( |
||
120 | concatenate( |
||
121 | std::declval<style_qualified_>(), |
||
122 | "default"_literal_as_type |
||
123 | ) |
||
124 | ), |
||
125 | style_qualified_default |
||
126 | >::value, |
||
127 | literal_type_to_policy<style_default>, |
||
128 | literal_type_to_policy<style_qualified_default> |
||
129 | >::type |
||
130 | ) |
||
131 | ); |
||
132 | #undef DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH |
||
133 | /* The style can be selected on a per-const-qualified-type basis at |
||
134 | * compile-time by defining a DXX_VALPTRIDX_REPORT_ERROR_STYLE_* |
||
135 | * macro. See the banner comment by the definition of |
||
136 | * `DXX_VALPTRIDX_REPORT_ERROR_STYLE_default` for details. |
||
137 | */ |
||
138 | enum class report_error_style |
||
139 | { |
||
140 | /* Do not report the error at all. This produces the smallest |
||
141 | * program, but the program will exhibit undefined behavior if |
||
142 | * an error condition occurs. The program may crash |
||
143 | * immediately, crash at a later stage when some other function |
||
144 | * becomes confused by the invalid data propagated by the |
||
145 | * undefined behavior, continue to run but behave incorrectly, |
||
146 | * or seem to work normally. |
||
147 | * |
||
148 | * This mode is not recommended for use in normal environments, |
||
149 | * but is included because it is the only mode which can |
||
150 | * completely optimize out the error detection code. |
||
151 | * |
||
152 | * Debugging may be very difficult, since the variables are not |
||
153 | * preserved and the undefined behavior may not halt the program |
||
154 | * until much later, if at all. |
||
155 | */ |
||
156 | undefined, |
||
157 | /* Report the error by executing an architecture-specific |
||
158 | * trapping instruction. No context is explicitly preserved. |
||
159 | * This produces the second smallest program. This is the |
||
160 | * smallest choice that prevents undefined behavior. |
||
161 | * |
||
162 | * This is recommended for production in constrained |
||
163 | * environments and for environments in which no debugging will |
||
164 | * be attempted. |
||
165 | * |
||
166 | * Debugging will require reading the surrounding code to find |
||
167 | * relevant variables. The variables might have been |
||
168 | * overwritten by the time of the trap. |
||
169 | */ |
||
170 | trap_terse, |
||
171 | /* Report the error by executing an architecture-specific |
||
172 | * trapping instruction. Context data is stored into |
||
173 | * registers/memory immediately before the trap. |
||
174 | * This produces a larger program than `trap_terse`, but smaller |
||
175 | * than `exception`. |
||
176 | * |
||
177 | * This is recommended for constrained environments where |
||
178 | * debugging is expected. |
||
179 | * |
||
180 | * Debugging should be easier because the relevant variables |
||
181 | * were kept alive and explicitly stored into registers/memory |
||
182 | * immediately before the trap. Although the compiler may |
||
183 | * generate code that will overwrite those locations before the |
||
184 | * trap executes, it has no reason to do so, and likely will not |
||
185 | * do so. |
||
186 | */ |
||
187 | trap_verbose, |
||
188 | /* Report the error by throwing an exception. This produces the |
||
189 | * largest program, but produces the most informative output in |
||
190 | * case of an error. |
||
191 | * |
||
192 | * This is recommended for environments where debugging is |
||
193 | * expected and the space overhead is acceptable. |
||
194 | * |
||
195 | * Debugging should be easier because the relevant variables are |
||
196 | * formatted into a string, which is passed to the exception |
||
197 | * constructor. The variables should also be visible in the |
||
198 | * call to the function which throws the exception. |
||
199 | */ |
||
200 | exception, |
||
201 | }; |
||
202 | |||
203 | #if DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION |
||
204 | template <typename T> |
||
205 | using wrapper = strong_typedef<T>; |
||
206 | #else |
||
207 | template <typename T> |
||
208 | using wrapper = T; |
||
209 | #endif |
||
210 | |||
211 | protected: |
||
212 | /* These classes contain a static method `report` called when an |
||
213 | * error occurs. The report method implements the error reporting |
||
214 | * (if any) for this type of error. See `report_error_style` |
||
215 | * members for a description. |
||
216 | * |
||
217 | * Reporting as undefined and reporting as terse use a single helper |
||
218 | * for all error classes, since those methods do not depend on any |
||
219 | * error-type specific context data. |
||
220 | * |
||
221 | * Reporting as a trap with context is broken out on a per-error |
||
222 | * basis, but all types share the same implementation. |
||
223 | * |
||
224 | * Reporting as an exception is broken out and is not shared, so |
||
225 | * that the exception type reports the managed_type that failed the |
||
226 | * test. See `valptridx<T>` for the exception classes. |
||
227 | */ |
||
228 | class report_error_undefined; |
||
229 | class report_error_trap_terse; |
||
230 | class index_mismatch_trap_verbose; |
||
231 | class index_range_trap_verbose; |
||
232 | class null_pointer_trap_verbose; |
||
233 | |||
234 | /* This is a template switch to map each error reporting style to |
||
235 | * its corresponding class. Modes `undefined` and `trap_terse` map |
||
236 | * to one type each. Modes `trap_verbose` and `exception` map to |
||
237 | * varied types, so must be supplied as template arguments. |
||
238 | * |
||
239 | * Type `array_managed_type` and styles `report_const_error_mode`, |
||
240 | * `report_mutable_error_mode` are only used to to choose |
||
241 | * `report_error_mode`, which should never be specified by the |
||
242 | * caller. Style `report_error_mode` is then used as the key of the |
||
243 | * switch. |
||
244 | * |
||
245 | * This switch yields a type that is one of the four error report |
||
246 | * classes, or `void` if the input is invalid. |
||
247 | */ |
||
248 | template < |
||
249 | typename array_managed_type, |
||
250 | report_error_style report_const_error_mode, |
||
251 | report_error_style report_mutable_error_mode, |
||
252 | typename report_error_trap_verbose, |
||
253 | typename report_error_exception, |
||
254 | report_error_style report_error_mode = std::is_const<array_managed_type>::value ? report_const_error_mode : report_mutable_error_mode |
||
255 | > |
||
256 | using dispatch_mc_report_error_type = typename std::conditional< |
||
257 | report_error_mode == report_error_style::undefined, |
||
258 | report_error_undefined, |
||
259 | typename std::conditional< |
||
260 | report_error_mode == report_error_style::trap_terse, |
||
261 | report_error_trap_terse, |
||
262 | typename std::conditional< |
||
263 | report_error_mode == report_error_style::trap_verbose, |
||
264 | report_error_trap_verbose, |
||
265 | typename std::conditional< |
||
266 | report_error_mode == report_error_style::exception, |
||
267 | report_error_exception, |
||
268 | /* Error, no match - use void to force a |
||
269 | * compilation error when the caller tries to |
||
270 | * access a static method of the resulting type. |
||
271 | */ |
||
272 | void |
||
273 | >::type |
||
274 | >::type |
||
275 | >::type |
||
276 | >::type; |
||
277 | |||
278 | class allow_end_construction; |
||
279 | class allow_none_construction; |
||
280 | class assume_nothrow_index; |
||
281 | class rebind_policy; |
||
282 | }; |
||
283 | |||
284 | /* Map the four reporting styles from their `literal_as_type` |
||
285 | * representation to their `report_error_style` enumerated value. |
||
286 | */ |
||
287 | template <> |
||
288 | struct literal_type_to_policy<decltype("undefined"_literal_as_type)> : std::integral_constant<untyped_utilities::report_error_style, untyped_utilities::report_error_style::undefined> |
||
289 | { |
||
290 | }; |
||
291 | |||
292 | template <> |
||
293 | struct literal_type_to_policy<decltype("trap_terse"_literal_as_type)> : std::integral_constant<untyped_utilities::report_error_style, untyped_utilities::report_error_style::trap_terse> |
||
294 | { |
||
295 | }; |
||
296 | |||
297 | template <> |
||
298 | struct literal_type_to_policy<decltype("trap_verbose"_literal_as_type)> : std::integral_constant<untyped_utilities::report_error_style, untyped_utilities::report_error_style::trap_verbose> |
||
299 | { |
||
300 | }; |
||
301 | |||
302 | template <> |
||
303 | struct literal_type_to_policy<decltype("exception"_literal_as_type)> : std::integral_constant<untyped_utilities::report_error_style, untyped_utilities::report_error_style::exception> |
||
304 | { |
||
305 | }; |
||
306 | |||
307 | template < |
||
308 | typename INTEGRAL_TYPE, |
||
309 | std::size_t array_size_value, |
||
310 | untyped_utilities::report_error_style report_const_error_value, |
||
311 | untyped_utilities::report_error_style report_mutable_error_value |
||
312 | > |
||
313 | class specialized_type_parameters : public untyped_utilities |
||
314 | { |
||
315 | public: |
||
316 | using integral_type = INTEGRAL_TYPE; |
||
317 | static constexpr std::integral_constant<std::size_t, array_size_value> array_size{}; |
||
318 | using report_error_uses_exception = std::integral_constant<bool, |
||
319 | report_const_error_value == untyped_utilities::report_error_style::exception || |
||
320 | report_mutable_error_value == untyped_utilities::report_error_style::exception |
||
321 | >; |
||
322 | template <typename array_managed_type, typename report_error_exception> |
||
323 | using dispatch_index_mismatch_error = typename untyped_utilities::template dispatch_mc_report_error_type< |
||
324 | array_managed_type, |
||
325 | report_const_error_value, |
||
326 | report_mutable_error_value, |
||
327 | typename untyped_utilities::index_mismatch_trap_verbose, |
||
328 | report_error_exception |
||
329 | >; |
||
330 | template <typename array_managed_type, typename report_error_exception> |
||
331 | using dispatch_index_range_error = typename untyped_utilities::template dispatch_mc_report_error_type< |
||
332 | array_managed_type, |
||
333 | report_const_error_value, |
||
334 | report_mutable_error_value, |
||
335 | typename untyped_utilities::index_range_trap_verbose, |
||
336 | report_error_exception |
||
337 | >; |
||
338 | template <typename array_managed_type, typename report_error_exception> |
||
339 | using dispatch_null_pointer_error = typename untyped_utilities::template dispatch_mc_report_error_type< |
||
340 | array_managed_type, |
||
341 | report_const_error_value, |
||
342 | report_mutable_error_value, |
||
343 | typename untyped_utilities::null_pointer_trap_verbose, |
||
344 | report_error_exception |
||
345 | >; |
||
346 | }; |
||
347 | |||
348 | } |
||
349 | |||
350 | /* If not otherwise defined, set the default reporting style for all |
||
351 | * valptridx errors. The macro value must be equal to the suffix of one |
||
352 | * of the four members of `report_error_style`. |
||
353 | * |
||
354 | * For finer control, valptridx inspects four values and picks the first |
||
355 | * defined value. Undefined styles are ignored and later values are |
||
356 | * searched. Invalid values produce a compile-time error. |
||
357 | * |
||
358 | * For const inputs, the four values are: |
||
359 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_<TYPE> |
||
360 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_<TYPE> |
||
361 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default |
||
362 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default |
||
363 | * |
||
364 | * For mutable inputs, the four values are: |
||
365 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_<TYPE> |
||
366 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_<TYPE> |
||
367 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default |
||
368 | * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default |
||
369 | * |
||
370 | * In all cases, <TYPE> is the second argument passed to |
||
371 | * DXX_VALPTRIDX_DECLARE_SUBTYPE. This is normally the |
||
372 | * non-namespace-qualified name of the type, such as `segment`, |
||
373 | * `object`, or `wall`. Namespace qualification is not permitted since |
||
374 | * colon is not valid in a macro name. |
||
375 | * |
||
376 | * For example, to make all const lookups use `trap_terse`, mutable |
||
377 | * segment use `trap_verbose`, and all others retain default, define two |
||
378 | * macros: |
||
379 | * |
||
380 | * #define DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default trap_terse |
||
381 | * #define DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_segment trap_verbose |
||
382 | */ |
||
383 | #ifndef DXX_VALPTRIDX_REPORT_ERROR_STYLE_default |
||
384 | #define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default exception |
||
385 | #endif |
||
386 | |||
387 | #define DXX_VALPTRIDX_DECLARE_SUBTYPE(NS_TYPE,MANAGED_TYPE,INTEGRAL_TYPE,ARRAY_SIZE_VALUE) \ |
||
388 | template <> \ |
||
389 | struct valptridx_specialized_types<NS_TYPE MANAGED_TYPE> { \ |
||
390 | using type = valptridx_detail::specialized_type_parameters< \ |
||
391 | INTEGRAL_TYPE, \ |
||
392 | ARRAY_SIZE_VALUE, \ |
||
393 | valptridx_detail::untyped_utilities::error_style_dispatch<DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_PARAMETERS(const, MANAGED_TYPE)>::value, \ |
||
394 | valptridx_detail::untyped_utilities::error_style_dispatch<DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_PARAMETERS(mutable, MANAGED_TYPE)>::value \ |
||
395 | >; \ |
||
396 | } |