enum_base.h 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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_TOOLCHAIN_COMMON_ENUM_BASE_H_
  5. #define CARBON_TOOLCHAIN_COMMON_ENUM_BASE_H_
  6. #include <type_traits>
  7. #include "common/ostream.h"
  8. #include "llvm/ADT/StringRef.h"
  9. namespace Carbon::Internal {
  10. // CRTP-style base class used to define the common pattern of Carbon enum-like
  11. // classes. The result is a class with named constants similar to enumerators,
  12. // but that are normal classes, can contain other methods, and support a `name`
  13. // method and printing the enums. These even work in switch statements and
  14. // support `case MyEnum::Name:`.
  15. //
  16. // It is specifically designed to compose with X-MACRO style `.def` files that
  17. // stamp out all the enumerators.
  18. //
  19. // It also supports some opt-in APIs that classes can enable by `using` the
  20. // names to make them public: `AsInt` and `FromInt` allow converting to and from
  21. // the underlying type of the enumerator.
  22. //
  23. // Users must be in the `Carbon` namespace and should look like the following.
  24. //
  25. // In `my_kind.h`:
  26. // ```
  27. // CARBON_DEFINE_RAW_ENUM_CLASS(MyKind, uint8_t) {
  28. // #define CARBON_MY_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
  29. // #include "toolchain/.../my_kind.def"
  30. // };
  31. //
  32. // class MyKind : public CARBON_ENUM_BASE(MyKind) {
  33. // public:
  34. // #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DECLARATION(Name)
  35. // #include "toolchain/.../my_kind.def"
  36. // };
  37. //
  38. // #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DEFINITION(MyKind, Name)
  39. // #include "toolchain/.../my_kind.def"
  40. // ```
  41. //
  42. // In `my_kind.cpp`:
  43. // ```
  44. // CARBON_DEFINE_ENUM_CLASS_NAMES(MyKind) = {
  45. // #define CARBON_MY_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
  46. // #include "toolchain/.../my_kind.def"
  47. // };
  48. // ```
  49. template <typename DerivedT, typename EnumT>
  50. class EnumBase {
  51. protected:
  52. // An alias for the raw enum type. This is an implementation detail and
  53. // shouldn't be used, but we need it for a signature so it is declared early.
  54. using RawEnumType = EnumT;
  55. public:
  56. using EnumType = DerivedT;
  57. using UnderlyingType = std::underlying_type_t<RawEnumType>;
  58. // Enable conversion to the raw enum type, including in a `constexpr` context,
  59. // to enable comparisons and usage in `switch` and `case`. The enum type
  60. // remains an implementation detail and nothing else should be using this
  61. // function.
  62. //
  63. // NOLINTNEXTLINE(google-explicit-constructor)
  64. constexpr operator RawEnumType() const { return value_; }
  65. // Conversion to bool is deleted to prevent direct use in an `if` condition
  66. // instead of comparing with another value.
  67. explicit operator bool() const = delete;
  68. // Returns the name of this value.
  69. //
  70. // This method will be automatically defined using the static `names` string
  71. // table in the base class, which is in turn will be populated for each
  72. // derived type using the macro helpers in this file.
  73. [[nodiscard]] auto name() const -> llvm::StringRef;
  74. // Prints this value using its name.
  75. void Print(llvm::raw_ostream& out) const {
  76. out << reinterpret_cast<const EnumType*>(this)->name();
  77. }
  78. protected:
  79. // The default constructor is explicitly defaulted (and constexpr) as a
  80. // protected constructor to allow derived classes to be constructed but not
  81. // the base itself. This should only be used in the `Create` function below.
  82. constexpr EnumBase() = default;
  83. // Create an instance from the raw enumerator, for internal use.
  84. static constexpr auto Create(RawEnumType value) -> EnumType {
  85. EnumType result;
  86. result.value_ = value;
  87. return result;
  88. }
  89. // Convert to the underlying integer type. Derived types can choose to expose
  90. // this as part of their API.
  91. constexpr auto AsInt() const -> UnderlyingType {
  92. return static_cast<UnderlyingType>(value_);
  93. }
  94. // Convert from the underlying integer type. Derived types can choose to
  95. // expose this as part of their API.
  96. static constexpr auto FromInt(UnderlyingType value) -> EnumType {
  97. return Create(static_cast<RawEnumType>(value));
  98. }
  99. private:
  100. static llvm::StringLiteral names[];
  101. RawEnumType value_;
  102. };
  103. } // namespace Carbon::Internal
  104. // Use this before defining a class that derives from `EnumBase` to begin the
  105. // definition of the raw `enum class`. It should be followed by the body of that
  106. // raw enum class.
  107. #define CARBON_DEFINE_RAW_ENUM_CLASS(EnumClassName, UnderlyingType) \
  108. namespace Internal { \
  109. /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
  110. enum class EnumClassName##RawEnum : UnderlyingType; \
  111. } \
  112. enum class ::Carbon::Internal::EnumClassName##RawEnum : UnderlyingType
  113. // In CARBON_DEFINE_RAW_ENUM_CLASS block, use this to generate each enumerator.
  114. #define CARBON_RAW_ENUM_ENUMERATOR(Name) Name,
  115. // Use this to compute the `Internal::EnumBase` specialization for a Carbon enum
  116. // class. It both computes the name of the raw enum and ensures all the
  117. // namespaces are correct.
  118. #define CARBON_ENUM_BASE(EnumClassName) \
  119. ::Carbon::Internal::EnumBase<EnumClassName, \
  120. ::Carbon::Internal::EnumClassName##RawEnum>
  121. // Use this within the Carbon enum class body to generate named constant
  122. // declarations for each value.
  123. #define CARBON_ENUM_CONSTANT_DECLARATION(Name) static const EnumType Name;
  124. // Use this immediately after the Carbon enum class body to define each named
  125. // constant.
  126. #define CARBON_ENUM_CONSTANT_DEFINITION(EnumClassName, Name) \
  127. constexpr EnumClassName EnumClassName::Name = \
  128. EnumClassName::Create(RawEnumType::Name);
  129. // Use this in the `.cpp` file for an enum class to start the definition of the
  130. // constant names array for each enumerator. It is followed by the desired
  131. // constant initializer.
  132. //
  133. // `clang-format` has a bug with spacing around `->` returns in macros. See
  134. // https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
  135. #define CARBON_DEFINE_ENUM_CLASS_NAMES(EnumClassName) \
  136. /* First declare an explicit specialization of the names array so we can \
  137. * reference it from an explicit function specialization. */ \
  138. template <> \
  139. llvm::StringLiteral Internal::EnumBase< \
  140. EnumClassName, Internal::EnumClassName##RawEnum>::names[]; \
  141. \
  142. /* Now define an explicit function specialization for the `name` method, as \
  143. * it can now reference our specialized array. */ \
  144. template <> \
  145. auto \
  146. Internal::EnumBase<EnumClassName, Internal::EnumClassName##RawEnum>::name() \
  147. const->llvm::StringRef { \
  148. return names[static_cast<int>(value_)]; \
  149. } \
  150. \
  151. /* Finally, open up the definition of our specialized array for the user to \
  152. * populate using the x-macro include. */ \
  153. template <> \
  154. llvm::StringLiteral Internal::EnumBase< \
  155. EnumClassName, Internal::EnumClassName##RawEnum>::names[]
  156. // Use this within the names array initializer to generate a string for each
  157. // name.
  158. #define CARBON_ENUM_CLASS_NAME_STRING(Name) #Name,
  159. #endif // CARBON_TOOLCHAIN_COMMON_ENUM_BASE_H_