mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:10:14 +01:00
Adds a unit test, and some smaller edits: - Remove the `=` when defining names, in order to change `}` placement by clang-format on uses. - context: https://github.com/carbon-language/carbon-lang/pull/6053#discussion_r2343423178 - I believe with `EnumBase` that keeping the `=` had been a deliberate choice, so this PR is intended to confirm that removing it is okay. - Delete `EnumMaskBase::name` - context: https://github.com/carbon-language/carbon-lang/pull/6053#discussion_r2344233707 - We can't just do nothing because `EnumBase::name` uses indexing that's incompatible with `EnumMaskBase`. - Some small comment cleanups. - Tests don't need to be in the `Carbon` namespace anymore, macros work fine in other namespaces, but it's still the right namespace. - Documentation on `EnumBase::name` seems to be referring to a prior structure, wherein we had a macro defining the function instead of the `Names` array. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
230 lines
9.0 KiB
C++
230 lines
9.0 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_COMMON_ENUM_BASE_H_
|
|
#define CARBON_COMMON_ENUM_BASE_H_
|
|
|
|
#include <compare>
|
|
#include <type_traits>
|
|
|
|
#include "common/ostream.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
namespace Carbon::Internal {
|
|
|
|
// CRTP-style base class used to define the common pattern of Carbon enum-like
|
|
// classes. The result is a class with named constants similar to enumerators,
|
|
// but that are normal classes, can contain other methods, and support a `name`
|
|
// method and printing the enums. These even work in switch statements and
|
|
// support `case MyEnum::Name:`.
|
|
//
|
|
// It is specifically designed to compose with X-MACRO style `.def` files that
|
|
// stamp out all the enumerators.
|
|
//
|
|
// Users must be in the `Carbon` namespace and should look like the following.
|
|
//
|
|
// In `my_kind.h`:
|
|
// ```
|
|
// CARBON_DEFINE_RAW_ENUM_CLASS(MyKind, uint8_t) {
|
|
// #define CARBON_MY_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
|
|
// #include ".../my_kind.def"
|
|
// };
|
|
//
|
|
// class MyKind : public CARBON_ENUM_BASE(MyKind) {
|
|
// public:
|
|
// #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name)
|
|
// #include ".../my_kind.def"
|
|
//
|
|
// // OPTIONAL: To support converting to and from the underlying type of
|
|
// // the enumerator, add these lines:
|
|
// using EnumBase::AsInt;
|
|
// using EnumBase::FromInt;
|
|
//
|
|
// // OPTIONAL: To expose the ability to create an instance from the raw
|
|
// // enumerator (for unusual use cases), add this:
|
|
// using EnumBase::Make;
|
|
//
|
|
// // Plus, anything else you wish to include.
|
|
// };
|
|
//
|
|
// #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DEFINITION(MyKind, Name)
|
|
// #include ".../my_kind.def"
|
|
// ```
|
|
//
|
|
// In `my_kind.cpp`:
|
|
// ```
|
|
// CARBON_DEFINE_ENUM_CLASS_NAMES(MyKind) {
|
|
// #define CARBON_MY_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
|
|
// #include ".../my_kind.def"
|
|
// };
|
|
// ```
|
|
//
|
|
// The result of the above:
|
|
// - An enum class (`RawEnumType`) defined in an `Internal` namespace with one
|
|
// enumerator per call to CARBON_MY_KIND(Name) in `.../my_kind.def`, with name
|
|
// `Name`. This won't generally be used directly, but may be needed for niche
|
|
// use cases such as a template argument.
|
|
// - A type `MyKind` that extends `Carbon::Internal::EnumBase`.
|
|
// - `MyKind` includes all the public members of `EnumBase`, like `name` and
|
|
// `Print`. For example, you might call `name()` to construct an error
|
|
// message:
|
|
// ```
|
|
// auto ErrorMessage(MyKind k) -> std::string {
|
|
// return k.name() + " not found";
|
|
// }
|
|
// ```
|
|
// - `MyKind` includes all protected members of `EnumBase`, like `AsInt`,
|
|
// `FromInt`. They will be part of the public API of `EnumBase` if they
|
|
// were included in a `using` declaration.
|
|
// - `MyKind` includes a member `static const MyKind Name;` per call to
|
|
// `CARBON_MY_KIND(Name)` in `.../my_kind.def`. It will have the
|
|
// corresponding value from `RawEnumType`. This is the primary way to create
|
|
// an instance of `MyKind`. For example, it might be used like:
|
|
// ```
|
|
// ErrorMessage(MyKind::Name1);
|
|
// ```
|
|
// - `MyKind` includes an implicit conversion to the `RawEnumType`, returning
|
|
// the value of a private field in `EnumBase`. This is used when writing a
|
|
// `switch` statement, as in this example:
|
|
// ```
|
|
// auto MyFunction(MyKind k) -> void {
|
|
// // Implicitly converts `k` and every `case` expression to
|
|
// // `RawEnumType`:
|
|
// switch (k) {
|
|
// case MyKind::Name1:
|
|
// // ...
|
|
// break;
|
|
//
|
|
// case MyKind::Name2:
|
|
// // ...
|
|
// break;
|
|
//
|
|
// // No `default` case needed if the above cases are exhaustive.
|
|
// // Prefer no `default` case when possible, to get an error if
|
|
// // a case is skipped.
|
|
// }
|
|
// }
|
|
// ```
|
|
//
|
|
template <typename DerivedT, typename EnumT, const llvm::StringLiteral Names[]>
|
|
class EnumBase : public Printable<DerivedT> {
|
|
public:
|
|
// An alias for the raw enum type. This is an implementation detail and
|
|
// should rarely be used directly, only when an actual enum type is needed.
|
|
using RawEnumType = EnumT;
|
|
|
|
using EnumType = DerivedT;
|
|
using UnderlyingType = std::underlying_type_t<RawEnumType>;
|
|
|
|
// Enable conversion to the raw enum type, including in a `constexpr` context,
|
|
// to enable comparisons and usage in `switch` and `case`. The enum type
|
|
// remains an implementation detail and nothing else should be using this
|
|
// function.
|
|
//
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
explicit(false) constexpr operator RawEnumType() const { return value_; }
|
|
|
|
// Conversion to bool is deleted to prevent direct use in an `if` condition
|
|
// instead of comparing with another value.
|
|
explicit operator bool() const = delete;
|
|
|
|
// Returns the name of this value.
|
|
auto name() const -> llvm::StringRef { return Names[AsInt()]; }
|
|
|
|
// Prints this value using its name.
|
|
auto Print(llvm::raw_ostream& out) const -> void { out << name(); }
|
|
|
|
// Don't support comparison of enums by default.
|
|
friend auto operator<(DerivedT lhs, DerivedT rhs) -> bool = delete;
|
|
friend auto operator<=(DerivedT lhs, DerivedT rhs) -> bool = delete;
|
|
friend auto operator>(DerivedT lhs, DerivedT rhs) -> bool = delete;
|
|
friend auto operator>=(DerivedT lhs, DerivedT rhs) -> bool = delete;
|
|
friend auto operator<=>(DerivedT lhs, DerivedT rhs)
|
|
-> std::partial_ordering = delete;
|
|
|
|
protected:
|
|
// The default constructor is explicitly defaulted (and constexpr) as a
|
|
// protected constructor to allow derived classes to be constructed but not
|
|
// the base itself. This should only be used in the `Make` function below.
|
|
constexpr EnumBase() = default;
|
|
|
|
// Create an instance from the raw enumerator. Mainly used internally, but may
|
|
// be exposed for unusual use cases.
|
|
static constexpr auto Make(RawEnumType value) -> EnumType {
|
|
EnumType result;
|
|
result.value_ = value;
|
|
return result;
|
|
}
|
|
|
|
// Convert to the underlying integer type. Derived types can choose to expose
|
|
// this as part of their API.
|
|
constexpr auto AsInt() const -> UnderlyingType {
|
|
return static_cast<UnderlyingType>(value_);
|
|
}
|
|
|
|
// Convert from the underlying integer type. Derived types can choose to
|
|
// expose this as part of their API.
|
|
static constexpr auto FromInt(UnderlyingType value) -> EnumType {
|
|
return Make(static_cast<RawEnumType>(value));
|
|
}
|
|
|
|
private:
|
|
template <typename MaskDerivedT, typename MaskEnumT,
|
|
const llvm::StringLiteral MaskNames[]>
|
|
friend class EnumMaskBase;
|
|
|
|
RawEnumType value_;
|
|
};
|
|
|
|
} // namespace Carbon::Internal
|
|
|
|
// Use this before defining a class that derives from `EnumBase` to begin the
|
|
// definition of the raw `enum class`. It should be followed by the body of that
|
|
// raw enum class.
|
|
#define CARBON_DEFINE_RAW_ENUM_CLASS(EnumClassName, UnderlyingType) \
|
|
namespace Internal { \
|
|
struct EnumClassName##Data { \
|
|
static const llvm::StringLiteral Names[]; \
|
|
enum class RawEnum : UnderlyingType; \
|
|
}; \
|
|
} \
|
|
enum class Internal::EnumClassName##Data::RawEnum : UnderlyingType
|
|
|
|
// In the `CARBON_DEFINE_RAW_ENUM_CLASS` block, use this to generate each
|
|
// enumerator.
|
|
#define CARBON_RAW_ENUM_ENUMERATOR(Name) Name,
|
|
|
|
// Use this to compute the `Internal::EnumBase` specialization for a Carbon enum
|
|
// class. It both computes the name of the raw enum and ensures all the
|
|
// namespaces are correct.
|
|
#define CARBON_ENUM_BASE(EnumClassName) \
|
|
::Carbon::Internal::EnumBase<EnumClassName, \
|
|
Internal::EnumClassName##Data::RawEnum, \
|
|
Internal::EnumClassName##Data::Names>
|
|
|
|
// Use this within the Carbon enum class body to generate named constant
|
|
// declarations for each value.
|
|
#define CARBON_ENUM_CONSTANT_DECL(Name) static const EnumType Name;
|
|
|
|
// Use this immediately after the Carbon enum class body to define each named
|
|
// constant.
|
|
#define CARBON_ENUM_CONSTANT_DEFINITION(EnumClassName, Name) \
|
|
constexpr EnumClassName EnumClassName::Name = \
|
|
EnumClassName::Make(RawEnumType::Name);
|
|
|
|
// Use this in the `.cpp` file for an enum class to start the definition of the
|
|
// constant names array for each enumerator. It is followed by the desired
|
|
// constant initializer.
|
|
//
|
|
// `clang-format` has a bug with spacing around `->` returns in macros. See
|
|
// https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
|
|
#define CARBON_DEFINE_ENUM_CLASS_NAMES(EnumClassName) \
|
|
constexpr llvm::StringLiteral Internal::EnumClassName##Data::Names[] =
|
|
|
|
// Use this within the names array initializer to generate a string for each
|
|
// name.
|
|
#define CARBON_ENUM_CLASS_NAME_STRING(Name) #Name,
|
|
|
|
#endif // CARBON_COMMON_ENUM_BASE_H_
|