SHARE
TWEET

kung-fuC++

a guest Sep 21st, 2019 99 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <cstddef>      // For size_t.
  2. #include <cstring>      // For strcspn, strncpy.
  3. #include <stdexcept>    // For runtime_error.
  4.  
  5.  
  6.  
  7. // A "typical" mapping macro. MAP(macro, a, b, c, ...) expands to
  8. // macro(a) macro(b) macro(c) ...
  9. // The helper macro COUNT(a, b, c, ...) expands to the number of
  10. // arguments, and IDENTITY(x) is needed to control the order of
  11. // expansion of __VA_ARGS__ on Visual C++ compilers.
  12. #define MAP(macro, ...) \
  13.     IDENTITY( \
  14.         APPLY(CHOOSE_MAP_START, COUNT(__VA_ARGS__)) \
  15.             (macro, __VA_ARGS__))
  16.  
  17. #define CHOOSE_MAP_START(count) MAP ## count
  18.  
  19. #define APPLY(macro, ...) IDENTITY(macro(__VA_ARGS__))
  20.  
  21. #define IDENTITY(x) x
  22.  
  23. #define MAP1(m, x)      m(x)
  24. #define MAP2(m, x, ...) m(x) IDENTITY(MAP1(m, __VA_ARGS__))
  25. #define MAP3(m, x, ...) m(x) IDENTITY(MAP2(m, __VA_ARGS__))
  26. #define MAP4(m, x, ...) m(x) IDENTITY(MAP3(m, __VA_ARGS__))
  27. #define MAP5(m, x, ...) m(x) IDENTITY(MAP4(m, __VA_ARGS__))
  28. #define MAP6(m, x, ...) m(x) IDENTITY(MAP5(m, __VA_ARGS__))
  29. #define MAP7(m, x, ...) m(x) IDENTITY(MAP6(m, __VA_ARGS__))
  30. #define MAP8(m, x, ...) m(x) IDENTITY(MAP7(m, __VA_ARGS__))
  31.  
  32. #define EVALUATE_COUNT(_1, _2, _3, _4, _5, _6, _7, _8, count, ...) \
  33.     count
  34.  
  35. #define COUNT(...) \
  36.     IDENTITY(EVALUATE_COUNT(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1))
  37.  
  38.  
  39.  
  40. // The type "T" mentioned above that drops assignment operations.
  41. template <typename U>
  42. struct ignore_assign {
  43.     constexpr explicit ignore_assign(U value) : _value(value) { }
  44.     constexpr operator U() const { return _value; }
  45.  
  46.     constexpr const ignore_assign& operator =(int dummy) const
  47.         { return *this; }
  48.  
  49.     U   _value;
  50. };
  51.  
  52.  
  53.  
  54. // Prepends "(ignore_assign<_underlying>)" to each argument.
  55. #define IGNORE_ASSIGN_SINGLE(e) (ignore_assign<_underlying>)e,
  56. #define IGNORE_ASSIGN(...) \
  57.     IDENTITY(MAP(IGNORE_ASSIGN_SINGLE, __VA_ARGS__))
  58.  
  59. // Stringizes each argument.
  60. #define STRINGIZE_SINGLE(e) #e,
  61. #define STRINGIZE(...) IDENTITY(MAP(STRINGIZE_SINGLE, __VA_ARGS__))
  62.  
  63.  
  64.  
  65. // Some helpers needed for _from_string.
  66. constexpr const char    terminators[] = " =\t\r\n";
  67.  
  68. // The size of terminators includes the implicit '\0'.
  69. constexpr bool is_terminator(char c, size_t index = 0)
  70. {
  71.     return
  72.         index >= sizeof(terminators) ? false :
  73.         c == terminators[index] ? true :
  74.         is_terminator(c, index + 1);
  75. }
  76.  
  77. constexpr bool matches_untrimmed(const char *untrimmed, const char *s,
  78.                                  size_t index = 0)
  79. {
  80.     return
  81.         is_terminator(untrimmed[index]) ? s[index] == '\0' :
  82.         s[index] != untrimmed[index] ? false :
  83.         matches_untrimmed(untrimmed, s, index + 1);
  84. }
  85.  
  86.  
  87.  
  88. // The macro proper.
  89. //
  90. // There are several "simplifications" in this implementation, for the
  91. // sake of brevity. First, we have only one viable option for declaring
  92. // constexpr arrays: at namespace scope. This probably should be done
  93. // two namespaces deep: one namespace that is likely to be unique for
  94. // our little enum "library", then inside it a namespace whose name is
  95. // based on the name of the enum to avoid collisions with other enums.
  96. // I am using only one level of nesting.
  97. //
  98. // Declaring constexpr arrays inside the struct is not viable because
  99. // they will need out-of-line definitions, which will result in
  100. // duplicate symbols when linking. This can be solved with weak
  101. // symbols, but that is compiler- and system-specific. It is not
  102. // possible to declare constexpr arrays as static variables in
  103. // constexpr functions due to the restrictions on such functions.
  104. //
  105. // Note that this prevents the use of this macro anywhere except at
  106. // namespace scope. Ironically, the C++98 version of this, which can
  107. // declare static arrays inside static member functions, is actually
  108. // more flexible in this regard. It is shown in the CodeProject
  109. // article.
  110. //
  111. // Second, for compilation performance reasons, it is best to separate
  112. // the macro into a "parametric" portion, and the portion that depends
  113. // on knowing __VA_ARGS__, and factor the former out into a template.
  114. //
  115. // Third, this code uses a default parameter in _from_string that may
  116. // be better not exposed in the public interface.
  117.  
  118. #define ENUM(EnumName, Underlying, ...)                               \
  119. namespace data_ ## EnumName {                                         \
  120.     using _underlying = Underlying;                                   \
  121.     enum { __VA_ARGS__ };                                             \
  122.                                                                       \
  123.     constexpr const size_t           _size =                          \
  124.         IDENTITY(COUNT(__VA_ARGS__));                                 \
  125.                                                                       \
  126.     constexpr const _underlying      _values[] =                      \
  127.         { IDENTITY(IGNORE_ASSIGN(__VA_ARGS__)) };                     \
  128.                                                                       \
  129.     constexpr const char * const     _raw_names[] =                   \
  130.         { IDENTITY(STRINGIZE(__VA_ARGS__)) };                         \
  131. }                                                                     \
  132.                                                                       \
  133. struct EnumName {                                                     \
  134.     using _underlying = Underlying;                                   \
  135.     enum _enum : _underlying { __VA_ARGS__ };                         \
  136.                                                                       \
  137.     const char * _to_string() const                                   \
  138.     {                                                                 \
  139.         for (size_t index = 0; index < data_ ## EnumName::_size;      \
  140.              ++index) {                                               \
  141.                                                                       \
  142.             if (data_ ## EnumName::_values[index] == _value)          \
  143.                 return _trimmed_names()[index];                       \
  144.         }                                                             \
  145.                                                                       \
  146.         throw std::runtime_error("invalid value");                    \
  147.     }                                                                 \
  148.                                                                       \
  149.     constexpr static EnumName _from_string(const char *s,             \
  150.                                            size_t index = 0)          \
  151.     {                                                                 \
  152.         return                                                        \
  153.             index >= data_ ## EnumName::_size ?                       \
  154.                     throw std::runtime_error("invalid identifier") :  \
  155.             matches_untrimmed(                                        \
  156.                 data_ ## EnumName::_raw_names[index], s) ?            \
  157.                     (EnumName)(_enum)data_ ## EnumName::_values[      \
  158.                                                             index] :  \
  159.             _from_string(s, index + 1);                               \
  160.     }                                                                 \
  161.                                                                       \
  162.     EnumName() = delete;                                              \
  163.     constexpr EnumName(_enum value) : _value(value) { }               \
  164.     constexpr operator _enum() const { return (_enum)_value; }        \
  165.                                                                       \
  166.   private:                                                            \
  167.     _underlying     _value;                                           \
  168.                                                                       \
  169.     static const char * const * _trimmed_names()                      \
  170.     {                                                                 \
  171.         static char     *the_names[data_ ## EnumName::_size];         \
  172.         static bool     initialized = false;                          \
  173.                                                                       \
  174.         if (!initialized) {                                           \
  175.             for (size_t index = 0; index < data_ ## EnumName::_size;  \
  176.                  ++index) {                                           \
  177.                                                                       \
  178.                 size_t  length =                                      \
  179.                     std::strcspn(data_ ## EnumName::_raw_names[index],\
  180.                                  terminators);                        \
  181.                                                                       \
  182.                 the_names[index] = new char[length + 1];              \
  183.                                                                       \
  184.                 std::strncpy(the_names[index],                        \
  185.                              data_ ## EnumName::_raw_names[index],    \
  186.                              length);                                 \
  187.                 the_names[index][length] = '\0';                      \
  188.             }                                                         \
  189.                                                                       \
  190.             initialized = true;                                       \
  191.         }                                                             \
  192.                                                                       \
  193.         return the_names;                                             \
  194.     }                                                                 \
  195. };
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top