Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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.         }
  397.