diff --git a/common/include/cpp-valptridx.h b/common/include/cpp-valptridx.h index 91e82b6fe..0fdae9f75 100644 --- a/common/include/cpp-valptridx.h +++ b/common/include/cpp-valptridx.h @@ -22,6 +22,22 @@ template struct strong_typedef; #endif +/* Use a C++11 user-defined literal to convert a string literal into a + * type, so that it can be used as a template type parameter. + */ +template +struct literal_as_type {}; + +template +constexpr literal_as_type operator""_literal_as_type(); + +/* Given two types representing literals, return a type representing the + * concatenation of those literals. This function is never defined, and + * can only be used in unevaluated contexts. + */ +template +constexpr literal_as_type concatenate(literal_as_type &&, literal_as_type &&); + /* valptridx_specialized_types is never defined, but is specialized to * define a typedef for a type-specific class suitable for use as a base * of valptridx. @@ -29,9 +45,91 @@ struct strong_typedef; template struct valptridx_specialized_types; -class valptridx_untyped_utilities +namespace valptridx_detail { + +/* This type is never defined, but explicit specializations of it are + * defined to provide a mapping from literal_as_type to + * report_error_style::X, for each member X of report_error_style. + */ +template +struct literal_type_to_policy; + +/* Given a C identifier, stringize it, then pass the string to + * operator""_literal_as_type() to produce a specialization of + * literal_as_type<...>, which can be used as a template type argument. + */ +#define DXX_VALPTRIDX_LITERAL_TO_TYPE2(A) #A##_literal_as_type +#define DXX_VALPTRIDX_LITERAL_TO_TYPE(A) decltype(DXX_VALPTRIDX_LITERAL_TO_TYPE2(A)) + +/* Generate all the template parameters to one instantiation of + * error_style_dispatch. A macro is used due to the repeated occurrence + * of various boilerplate identifiers. + */ +#define DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_PARAMETERS(MC,TYPE) \ + DXX_VALPTRIDX_LITERAL_TO_TYPE(TYPE), \ + DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_##MC##_), DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_##MC##_##TYPE), \ + DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_), DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_##TYPE), \ + DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_##MC##_default), \ + DXX_VALPTRIDX_LITERAL_TO_TYPE(DXX_VALPTRIDX_REPORT_ERROR_STYLE_default) \ + +class untyped_utilities { public: + /* Given a C identifier as PREFIX, and a C type identifier as TYPE, + * generate `concatenate(literal_as_type, + * literal_as_type)` and `literal_as_type`. + * Compare them for type equality. If `PREFIX##TYPE` matches an + * active macro, it will be expanded to the value of the macro, but + * the concatenation of literal_as_type and + * literal_as_type will not be expanded. This allows the + * template to detect whether `PREFIX##TYPE` is a defined macro + * (unless `PREFIX##TYPE` is defined as a macro that expands to its + * own name), and choose a branch of `std::conditional` accordingly. + * The true branch is chosen if `PREFIX##TYPE` is _not_ a macro, and + * is implemented by expanding to the third argument. The false + * branch is chosen if `PREFIX##TYPE` is a macro, and is implemented + * as a reference to `literal_type_to_policy`. + */ +#define DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH(PREFIX,TYPE,BRANCH_TRUE,...) \ + typename std::conditional< \ + std::is_same< \ + decltype(concatenate( \ + std::declval(), std::declval() \ + )), PREFIX##TYPE>::value, \ + BRANCH_TRUE, ## __VA_ARGS__, literal_type_to_policy \ +>::type + + /* Given specializations of `literal_as_type`, find the most + * specific error-reporting style and evaluate to + * `literal_type_to_policy` specialized on that style. Consumers + * can then access `error_style_dispatch<...>::value` to obtain the + * chosen error-reporting style as an enum member of + * report_error_style. + */ + template + using error_style_dispatch = + DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH(style_qualified_, managed_type, + DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH(style_default_, managed_type, + typename std::conditional< + std::is_same< + decltype( + concatenate( + std::declval(), + "default"_literal_as_type + ) + ), + style_qualified_default + >::value, + literal_type_to_policy, + literal_type_to_policy + >::type + ) + ); +#undef DXX_VALPTRIDX_ERROR_STYLE_DISPATCH_BRANCH /* The style can be selected on a per-const-qualified-type basis at * compile-time by defining a DXX_VALPTRIDX_REPORT_ERROR_STYLE_* * macro. See the banner comment by the definition of @@ -183,71 +281,79 @@ protected: class rebind_policy; }; +/* Map the four reporting styles from their `literal_as_type` + * representation to their `report_error_style` enumerated value. + */ +template <> +struct literal_type_to_policy : std::integral_constant +{ +}; + +template <> +struct literal_type_to_policy : std::integral_constant +{ +}; + +template <> +struct literal_type_to_policy : std::integral_constant +{ +}; + +template <> +struct literal_type_to_policy : std::integral_constant +{ +}; + template < typename INTEGRAL_TYPE, std::size_t array_size_value, - valptridx_untyped_utilities::report_error_style report_const_error_value, - valptridx_untyped_utilities::report_error_style report_mutable_error_value + untyped_utilities::report_error_style report_const_error_value, + untyped_utilities::report_error_style report_mutable_error_value > -class valptridx_specialized_type_parameters : public valptridx_untyped_utilities +class specialized_type_parameters : public untyped_utilities { public: using integral_type = INTEGRAL_TYPE; static constexpr std::integral_constant array_size{}; using report_error_uses_exception = std::integral_constant; template - using dispatch_index_mismatch_error = typename valptridx_untyped_utilities::template dispatch_mc_report_error_type< + using dispatch_index_mismatch_error = typename untyped_utilities::template dispatch_mc_report_error_type< array_managed_type, report_const_error_value, report_mutable_error_value, - typename valptridx_untyped_utilities::index_mismatch_trap_verbose, + typename untyped_utilities::index_mismatch_trap_verbose, report_error_exception >; template - using dispatch_index_range_error = typename valptridx_untyped_utilities::template dispatch_mc_report_error_type< + using dispatch_index_range_error = typename untyped_utilities::template dispatch_mc_report_error_type< array_managed_type, report_const_error_value, report_mutable_error_value, - typename valptridx_untyped_utilities::index_range_trap_verbose, + typename untyped_utilities::index_range_trap_verbose, report_error_exception >; template - using dispatch_null_pointer_error = typename valptridx_untyped_utilities::template dispatch_mc_report_error_type< + using dispatch_null_pointer_error = typename untyped_utilities::template dispatch_mc_report_error_type< array_managed_type, report_const_error_value, report_mutable_error_value, - typename valptridx_untyped_utilities::null_pointer_trap_verbose, + typename untyped_utilities::null_pointer_trap_verbose, report_error_exception >; }; -/* These defines must expand to a value of the form (**comma**, *style*, - * **comma**). They are not required to use the _style_ name as a - * trailing suffix, but that convention makes the code easier to - * understand. - * - * *style* must be a member of - * `valptridx_untyped_utilities::report_error_style`. - */ -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_undefined , undefined, -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_trap_terse , trap_terse, -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_trap_verbose , trap_verbose, -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_exception , exception, +} /* If not otherwise defined, set the default reporting style for all * valptridx errors. The macro value must be equal to the suffix of one - * of the four DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_* macros above. - * For convenience, those suffixes match the members of - * `report_error_style`. + * of the four members of `report_error_style`. * * For finer control, valptridx inspects four values and picks the first - * value defined to a valid style. Invalid styles are ignored and later - * values are searched. Clever use of a constexpr comparison could - * detect invalid styles, but until someone needs such detection, this - * is left as a sharp edge for simplicity. + * defined value. Undefined styles are ignored and later values are + * searched. Invalid values produce a compile-time error. * * For const inputs, the four values are: * - DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_ @@ -278,84 +384,13 @@ public: #define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default exception #endif -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH(A) DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_##A -/* This approximates the ability to test defined-ness of a macro - * argument, even though `#if defined(A)` cannot portably be used when - * `A` is a macro parameter. A Python function that does the same (but - * more cleanly and safely) as this macro would be written as: - * - * ``` - * def DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_DISPATCH(A,B,C,D): - * for candidate in (A, B, C, D): - * if cpp_macro_defined(candidate): - * return cpp_macro_expand(candidate) - * return '' - * ``` - * - * Each of the four arguments evaluates, after macro expansion, to one of five values: - * - `undefined` - * - `trap_terse` - * - `trap_verbose` - * - `exception` - * - anything else - * - * Pass each argument to a separate - * DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH. If the argument is one of - * the four recognized values, then after pasting, it will be one of the - * four DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_* macros, and will - * expand to contain a leading and trailing comma. If it is - * unrecognized, it will not expand[1] and will therefore contain no - * commas. After expansion, the arguments to - * DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_UNPACK_ARGS are: - * - * - 0-or-more garbage non-comma tokens resulting from arguments that - * were "anything else" - * - comma from the first matched argument - * - token from the first matched argument (which will be one of the - * four accepted types) - * - another comma from the first matched argument - * - 0-or-more tokens from later arguments, which may or may not have - * been "anything else" and may or may not include more commas - * - * The call to UNPACK_ARGS then drops everything up to and including the - * first comma from the first matched argument and everything at and - * after the second comma from the first matched argument. It passes - * through the non-comma token from the first matched argument, yielding - * one of the four recognized types. - * - * [1] Assuming no definitions for - * DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH_* other than the four - * above. For proper operation, no other macros should be defined with - * that prefix. - */ -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_DISPATCH(A,B,C,D) \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_UNPACK_ARGS( \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH(A) \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH(B) \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH(C) \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_BRANCH(D) \ - ,) -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_UNPACK_ARGS(A,...) \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_DROP1(A, ## __VA_ARGS__) -#define DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_DROP1(DROP,TAKE,...) TAKE - #define DXX_VALPTRIDX_DECLARE_SUBTYPE(NS_TYPE,MANAGED_TYPE,INTEGRAL_TYPE,ARRAY_SIZE_VALUE) \ template <> \ struct valptridx_specialized_types { \ - using type = valptridx_specialized_type_parameters< \ + using type = valptridx_detail::specialized_type_parameters< \ INTEGRAL_TYPE, \ ARRAY_SIZE_VALUE, \ - valptridx_untyped_utilities::report_error_style:: \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_DISPATCH( \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_##MANAGED_TYPE, \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_##MANAGED_TYPE, \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default, \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default), \ - valptridx_untyped_utilities::report_error_style:: \ - DXX_VALPTRIDX_INTERNAL_ERROR_STYLE_DISPATCH( \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_##MANAGED_TYPE, \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_##MANAGED_TYPE, \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default, \ - DXX_VALPTRIDX_REPORT_ERROR_STYLE_default) \ + valptridx_detail::untyped_utilities::error_style_dispatch::value, \ + valptridx_detail::untyped_utilities::error_style_dispatch::value \ >; \ } diff --git a/common/include/valptridx.h b/common/include/valptridx.h index c6227c041..eb3f0817e 100644 --- a/common/include/valptridx.h +++ b/common/include/valptridx.h @@ -80,12 +80,12 @@ public: template < typename INTEGRAL_TYPE, std::size_t array_size_value, - valptridx_untyped_utilities::report_error_style report_const_error_value, - valptridx_untyped_utilities::report_error_style report_mutable_error_value + valptridx_detail::untyped_utilities::report_error_style report_const_error_value, + valptridx_detail::untyped_utilities::report_error_style report_mutable_error_value > -constexpr std::integral_constant valptridx_specialized_type_parameters::array_size; +constexpr std::integral_constant valptridx_detail::specialized_type_parameters::array_size; -class valptridx_untyped_utilities::report_error_undefined +class valptridx_detail::untyped_utilities::report_error_undefined { public: __attribute_cold @@ -95,7 +95,7 @@ public: } }; -class valptridx_untyped_utilities::report_error_trap_terse +class valptridx_detail::untyped_utilities::report_error_trap_terse { public: __attribute_cold @@ -107,7 +107,7 @@ public: } }; -class valptridx_untyped_utilities::index_mismatch_trap_verbose +class valptridx_detail::untyped_utilities::index_mismatch_trap_verbose { public: __attribute_cold @@ -120,7 +120,7 @@ public: } }; -class valptridx_untyped_utilities::index_range_trap_verbose +class valptridx_detail::untyped_utilities::index_range_trap_verbose { public: __attribute_cold @@ -133,7 +133,7 @@ public: } }; -class valptridx_untyped_utilities::null_pointer_trap_verbose +class valptridx_detail::untyped_utilities::null_pointer_trap_verbose { public: __attribute_cold diff --git a/common/unittest/valptridx-range.cpp b/common/unittest/valptridx-range.cpp index 1cedabf32..239b94ca1 100644 --- a/common/unittest/valptridx-range.cpp +++ b/common/unittest/valptridx-range.cpp @@ -222,3 +222,89 @@ BOOST_AUTO_TEST_CASE(ptr_convert_check) })), valptridx_access_override::index_range_exception ); } + +/* For each style selector, set a type. Test that the type is used. + * Reset to a different type. Test that the second type is used. Clear + * the selector, so that any further use is an error. + * + * This combination verifies that the requested style is set, and that + * the match is not an accident. If the first test succeeded because + * the style happened to match for the wrong reason, then redefining the + * style would have no effect, and the second test would fail. + */ + +/* Test DXX_VALPTRIDX_REPORT_ERROR_STYLE_default - the final fallback, + * used only when everything else is unset. + */ +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default exception + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::exception); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::exception); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default undefined + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::undefined); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::undefined); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default /* invalid mapping - force an error on use */ + +/* Test the const/mutable defaults. These are the second lowest + * priority, above only DXX_VALPTRIDX_REPORT_ERROR_STYLE_default. + */ +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default trap_terse +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default trap_verbose + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::trap_terse); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::trap_verbose); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default exception +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default trap_terse + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::exception); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::trap_terse); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_default +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_default + +/* Test the type-specific default. This is the third lowest priority. + */ +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_error_report_test undefined + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::undefined); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::undefined); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_error_report_test +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_error_report_test exception + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::exception); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::exception); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_error_report_test +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_default_error_report_test + +/* Test the const/mutable type-specific. These are the highest + * priority. + */ +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_error_report_test trap_terse +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_error_report_test trap_verbose + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::trap_terse); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::trap_verbose); + +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_error_report_test +#undef DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_error_report_test + +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_const_error_report_test exception +#define DXX_VALPTRIDX_REPORT_ERROR_STYLE_mutable_error_report_test undefined + +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::exception); +static_assert(valptridx_detail::untyped_utilities::error_style_dispatch::value == valptridx_detail::untyped_utilities::report_error_style::undefined);