mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
I think there's more we can do here, but this seemed like a good checkpoint to make sure the path I'm going down is roughly what you expected. There's one actual edit in if expression structure to match the increased enforcement.
209 lines
8.9 KiB
C++
209 lines
8.9 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 <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.
|
|
//
|
|
// It also supports some opt-in APIs that classes can enable by `using` the
|
|
// names to make them public: `AsInt` and `FromInt` allow converting to and from
|
|
// the underlying type of the enumerator.
|
|
//
|
|
// 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_DECLARATION(Name)
|
|
// #include ".../my_kind.def"
|
|
// };
|
|
//
|
|
// #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"
|
|
// };
|
|
// ```
|
|
template <typename DerivedT, typename EnumT>
|
|
class EnumBase {
|
|
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)
|
|
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.
|
|
//
|
|
// This method will be automatically defined using the static `names` string
|
|
// table in the base class, which is in turn will be populated for each
|
|
// derived type using the macro helpers in this file.
|
|
[[nodiscard]] auto name() const -> llvm::StringRef;
|
|
|
|
// Prints this value using its name.
|
|
void Print(llvm::raw_ostream& out) const {
|
|
out << static_cast<const EnumType*>(this)->name();
|
|
}
|
|
|
|
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 `Create` 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 Create(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 Create(static_cast<RawEnumType>(value));
|
|
}
|
|
|
|
private:
|
|
static llvm::StringLiteral names[];
|
|
|
|
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 { \
|
|
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
|
|
enum class EnumClassName##RawEnum : UnderlyingType; \
|
|
} \
|
|
enum class ::Carbon::Internal::EnumClassName##RawEnum : UnderlyingType
|
|
|
|
// In 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_ENUM_BASE_CRTP(EnumClassName, EnumClassName)
|
|
// This variant handles the case where the external name for the Carbon enum is
|
|
// not the same as the name by which we refer to it from this context.
|
|
#define CARBON_ENUM_BASE_CRTP(EnumClassName, LocalTypeNameForEnumClass) \
|
|
::Carbon::Internal::EnumBase<LocalTypeNameForEnumClass, \
|
|
::Carbon::Internal::EnumClassName##RawEnum>
|
|
|
|
// Use this within the Carbon enum class body to generate named constant
|
|
// declarations for each value.
|
|
#define CARBON_ENUM_CONSTANT_DECLARATION(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::Create(RawEnumType::Name);
|
|
|
|
// Alternatively, use this within the Carbon enum class body to declare and
|
|
// define each named constant. Due to type completeness constraints, this will
|
|
// only work if the enum-like class is templated.
|
|
//
|
|
// This requires the template to have a member named `Base` that names the
|
|
// `EnumBase` base class.
|
|
#define CARBON_INLINE_ENUM_CONSTANT_DEFINITION(Name) \
|
|
static constexpr const typename Base::EnumType& Name = \
|
|
Base::Create(Base::RawEnumType::Name);
|
|
|
|
// Use this to define a custom `name()` function for an enum-like class. Usage:
|
|
//
|
|
// CARBON_ENUM_NAME_FUNCTION(MyEnum) {
|
|
// // Return a StringRef based on the value of *this.
|
|
// }
|
|
//
|
|
// You should usually use CARBON_DEFINE_ENUM_CLASS_NAMES instead.
|
|
#define CARBON_ENUM_NAME_FUNCTION(EnumClassName) \
|
|
template <> \
|
|
auto \
|
|
Internal::EnumBase<EnumClassName, Internal::EnumClassName##RawEnum>::name() \
|
|
const->llvm::StringRef
|
|
|
|
// 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) \
|
|
/* First declare an explicit specialization of the names array so we can \
|
|
* reference it from an explicit function specialization. */ \
|
|
template <> \
|
|
llvm::StringLiteral Internal::EnumBase< \
|
|
EnumClassName, Internal::EnumClassName##RawEnum>::names[]; \
|
|
\
|
|
/* Now define an explicit function specialization for the `name` method, as \
|
|
* it can now reference our specialized array. */ \
|
|
CARBON_ENUM_NAME_FUNCTION(EnumClassName) { \
|
|
return names[static_cast<int>(value_)]; \
|
|
} \
|
|
\
|
|
/* Finally, open up the definition of our specialized array for the user to \
|
|
* populate using the x-macro include. */ \
|
|
template <> \
|
|
llvm::StringLiteral Internal::EnumBase< \
|
|
EnumClassName, Internal::EnumClassName##RawEnum>::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_
|