enum_base.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_COMMON_ENUM_BASE_H_
  5. #define CARBON_COMMON_ENUM_BASE_H_
  6. #include <compare>
  7. #include <type_traits>
  8. #include "common/ostream.h"
  9. #include "llvm/ADT/StringRef.h"
  10. namespace Carbon::Internal {
  11. // CRTP-style base class used to define the common pattern of Carbon enum-like
  12. // classes. The result is a class with named constants similar to enumerators,
  13. // but that are normal classes, can contain other methods, and support a `name`
  14. // method and printing the enums. These even work in switch statements and
  15. // support `case MyEnum::Name:`.
  16. //
  17. // It is specifically designed to compose with X-MACRO style `.def` files that
  18. // stamp out all the enumerators.
  19. //
  20. // Users must be in the `Carbon` namespace and should look like the following.
  21. //
  22. // In `my_kind.h`:
  23. // ```
  24. // CARBON_DEFINE_RAW_ENUM_CLASS(MyKind, uint8_t) {
  25. // #define CARBON_MY_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
  26. // #include ".../my_kind.def"
  27. // };
  28. //
  29. // class MyKind : public CARBON_ENUM_BASE(MyKind) {
  30. // public:
  31. // #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name)
  32. // #include ".../my_kind.def"
  33. //
  34. // // OPTIONAL: To support converting to and from the underlying type of
  35. // // the enumerator, add these lines:
  36. // using EnumBase::AsInt;
  37. // using EnumBase::FromInt;
  38. //
  39. // // OPTIONAL: To expose the ability to create an instance from the raw
  40. // // enumerator (for unusual use cases), add this:
  41. // using EnumBase::Make;
  42. //
  43. // // Plus, anything else you wish to include.
  44. // };
  45. //
  46. // #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DEFINITION(MyKind, Name)
  47. // #include ".../my_kind.def"
  48. // ```
  49. //
  50. // In `my_kind.cpp`:
  51. // ```
  52. // CARBON_DEFINE_ENUM_CLASS_NAMES(MyKind) {
  53. // #define CARBON_MY_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
  54. // #include ".../my_kind.def"
  55. // };
  56. // ```
  57. //
  58. // The result of the above:
  59. // - An enum class (`RawEnumType`) defined in an `Internal` namespace with one
  60. // enumerator per call to CARBON_MY_KIND(Name) in `.../my_kind.def`, with name
  61. // `Name`. This won't generally be used directly, but may be needed for niche
  62. // use cases such as a template argument.
  63. // - A type `MyKind` that extends `Carbon::Internal::EnumBase`.
  64. // - `MyKind` includes all the public members of `EnumBase`, like `name` and
  65. // `Print`. For example, you might call `name()` to construct an error
  66. // message:
  67. // ```
  68. // auto ErrorMessage(MyKind k) -> std::string {
  69. // return k.name() + " not found";
  70. // }
  71. // ```
  72. // - `MyKind` includes all protected members of `EnumBase`, like `AsInt`,
  73. // `FromInt`. They will be part of the public API of `EnumBase` if they
  74. // were included in a `using` declaration.
  75. // - `MyKind` includes a member `static const MyKind Name;` per call to
  76. // `CARBON_MY_KIND(Name)` in `.../my_kind.def`. It will have the
  77. // corresponding value from `RawEnumType`. This is the primary way to create
  78. // an instance of `MyKind`. For example, it might be used like:
  79. // ```
  80. // ErrorMessage(MyKind::Name1);
  81. // ```
  82. // - `MyKind` includes an implicit conversion to the `RawEnumType`, returning
  83. // the value of a private field in `EnumBase`. This is used when writing a
  84. // `switch` statement, as in this example:
  85. // ```
  86. // auto MyFunction(MyKind k) -> void {
  87. // // Implicitly converts `k` and every `case` expression to
  88. // // `RawEnumType`:
  89. // switch (k) {
  90. // case MyKind::Name1:
  91. // // ...
  92. // break;
  93. //
  94. // case MyKind::Name2:
  95. // // ...
  96. // break;
  97. //
  98. // // No `default` case needed if the above cases are exhaustive.
  99. // // Prefer no `default` case when possible, to get an error if
  100. // // a case is skipped.
  101. // }
  102. // }
  103. // ```
  104. //
  105. template <typename DerivedT, typename EnumT, const llvm::StringLiteral Names[]>
  106. class EnumBase : public Printable<DerivedT> {
  107. public:
  108. // An alias for the raw enum type. This is an implementation detail and
  109. // should rarely be used directly, only when an actual enum type is needed.
  110. using RawEnumType = EnumT;
  111. using EnumType = DerivedT;
  112. using UnderlyingType = std::underlying_type_t<RawEnumType>;
  113. // Enable conversion to the raw enum type, including in a `constexpr` context,
  114. // to enable comparisons and usage in `switch` and `case`. The enum type
  115. // remains an implementation detail and nothing else should be using this
  116. // function.
  117. //
  118. // NOLINTNEXTLINE(google-explicit-constructor)
  119. explicit(false) constexpr operator RawEnumType() const { return value_; }
  120. // Conversion to bool is deleted to prevent direct use in an `if` condition
  121. // instead of comparing with another value.
  122. explicit operator bool() const = delete;
  123. // Returns the name of this value.
  124. auto name() const -> llvm::StringRef { return Names[AsInt()]; }
  125. // Prints this value using its name.
  126. auto Print(llvm::raw_ostream& out) const -> void { out << name(); }
  127. // Don't support comparison of enums by default.
  128. friend auto operator<(DerivedT lhs, DerivedT rhs) -> bool = delete;
  129. friend auto operator<=(DerivedT lhs, DerivedT rhs) -> bool = delete;
  130. friend auto operator>(DerivedT lhs, DerivedT rhs) -> bool = delete;
  131. friend auto operator>=(DerivedT lhs, DerivedT rhs) -> bool = delete;
  132. friend auto operator<=>(DerivedT lhs, DerivedT rhs)
  133. -> std::partial_ordering = delete;
  134. protected:
  135. // The default constructor is explicitly defaulted (and constexpr) as a
  136. // protected constructor to allow derived classes to be constructed but not
  137. // the base itself. This should only be used in the `Make` function below.
  138. constexpr EnumBase() = default;
  139. // Create an instance from the raw enumerator. Mainly used internally, but may
  140. // be exposed for unusual use cases.
  141. static constexpr auto Make(RawEnumType value) -> EnumType {
  142. EnumType result;
  143. result.value_ = value;
  144. return result;
  145. }
  146. // Convert to the underlying integer type. Derived types can choose to expose
  147. // this as part of their API.
  148. constexpr auto AsInt() const -> UnderlyingType {
  149. return static_cast<UnderlyingType>(value_);
  150. }
  151. // Convert from the underlying integer type. Derived types can choose to
  152. // expose this as part of their API.
  153. static constexpr auto FromInt(UnderlyingType value) -> EnumType {
  154. return Make(static_cast<RawEnumType>(value));
  155. }
  156. private:
  157. template <typename MaskDerivedT, typename MaskEnumT,
  158. const llvm::StringLiteral MaskNames[]>
  159. friend class EnumMaskBase;
  160. RawEnumType value_;
  161. };
  162. } // namespace Carbon::Internal
  163. // Use this before defining a class that derives from `EnumBase` to begin the
  164. // definition of the raw `enum class`. It should be followed by the body of that
  165. // raw enum class.
  166. #define CARBON_DEFINE_RAW_ENUM_CLASS(EnumClassName, UnderlyingType) \
  167. namespace Internal { \
  168. struct EnumClassName##Data { \
  169. static const llvm::StringLiteral Names[]; \
  170. enum class RawEnum : UnderlyingType; \
  171. }; \
  172. } \
  173. enum class Internal::EnumClassName##Data::RawEnum : UnderlyingType
  174. // In the `CARBON_DEFINE_RAW_ENUM_CLASS` block, use this to generate each
  175. // enumerator.
  176. #define CARBON_RAW_ENUM_ENUMERATOR(Name) Name,
  177. // Use this to compute the `Internal::EnumBase` specialization for a Carbon enum
  178. // class. It both computes the name of the raw enum and ensures all the
  179. // namespaces are correct.
  180. #define CARBON_ENUM_BASE(EnumClassName) \
  181. ::Carbon::Internal::EnumBase<EnumClassName, \
  182. Internal::EnumClassName##Data::RawEnum, \
  183. Internal::EnumClassName##Data::Names>
  184. // Use this within the Carbon enum class body to generate named constant
  185. // declarations for each value.
  186. #define CARBON_ENUM_CONSTANT_DECL(Name) static const EnumType Name;
  187. // Use this immediately after the Carbon enum class body to define each named
  188. // constant.
  189. #define CARBON_ENUM_CONSTANT_DEFINITION(EnumClassName, Name) \
  190. inline constexpr EnumClassName EnumClassName::Name = \
  191. EnumClassName::Make(RawEnumType::Name);
  192. // Use this in the `.cpp` file for an enum class to start the definition of the
  193. // constant names array for each enumerator. It is followed by the desired
  194. // constant initializer.
  195. //
  196. // `clang-format` has a bug with spacing around `->` returns in macros. See
  197. // https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
  198. #define CARBON_DEFINE_ENUM_CLASS_NAMES(EnumClassName) \
  199. constexpr llvm::StringLiteral Internal::EnumClassName##Data::Names[] =
  200. // Use this within the names array initializer to generate a string for each
  201. // name.
  202. #define CARBON_ENUM_CLASS_NAME_STRING(Name) #Name,
  203. #endif // CARBON_COMMON_ENUM_BASE_H_