Subversion Repositories Games.Descent

Rev

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
        }