File
Blob: src/workerd/util/strong-bool.h
| 1 | // Copyright (c) 2025 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | #pragma once |
| 5 | |
| 6 | #include <kj/string.h> |
| 7 | |
| 8 | #include <compare> |
| 9 | #include <cstdint> |
| 10 | |
| 11 | namespace workerd { |
| 12 | |
| 13 | // WD_STRONG_BOOL(StrongBool) defines a class type, `StrongBool`, which acts like a boolean flag, |
| 14 | // but with greater type safety. |
| 15 | // |
| 16 | // `StrongBool` has the following restrictions: |
| 17 | // - No default constructor: you must explicitly initialize values |
| 18 | // - No possibility for uninitialized values |
| 19 | // - No implicit conversion to or from boolean values |
| 20 | // |
| 21 | // `StrongBool` supports the following explicit and contextual boolean conversions: |
| 22 | // - Explicit conversion from boolean values: `StrongBool(true)`, `StrongBool(false)` |
| 23 | // - Explicit conversion to boolean values: `bool(StrongBool::YES)`, `StrongBool::NO.toBool()` |
| 24 | // - Contextual boolean conversion: `if (strongBool)`, `while (strongBool)`, `!strongBool` |
| 25 | // |
| 26 | // The `.toBool()` function exists to make safe, explicit conversion to `bool` more convenient, |
| 27 | // avoiding the verbosity of `static_cast` and the risk of C-style/functional casts. |
| 28 | // |
| 29 | // `StrongBool` supports the full suite of comparison and logical operators. Note that ! is |
| 30 | // supported via contextual conversion (`explicit operator bool()`), rather than `operator!()`. |
| 31 | // |
| 32 | // Logical operators (&& and ||) preserve the type of their operands when the operands are the same |
| 33 | // `StrongBool` type. Otherwise, contextual boolean conversion applies, and the operands will be |
| 34 | // converted to `bool` before the operator is invoked. |
| 35 | #define WD_STRONG_BOOL(Type) \ |
| 36 | class Type final { \ |
| 37 | public: \ |
| 38 | static const Type NO; \ |
| 39 | static const Type YES; \ |
| 40 | constexpr explicit Type(bool booleanValue): value(booleanValue ? Value::YES : Value::NO) {} \ |
| 41 | constexpr explicit operator bool() const { \ |
| 42 | return toBool(); \ |
| 43 | } \ |
| 44 | constexpr bool toBool() const { \ |
| 45 | return value == YES; \ |
| 46 | } \ |
| 47 | constexpr auto operator<=>(const Type&) const = default; \ |
| 48 | constexpr Type operator&&(const Type& other) const { \ |
| 49 | return Type(value == YES && other.value == YES); \ |
| 50 | } \ |
| 51 | constexpr Type operator||(const Type& other) const { \ |
| 52 | return Type(value == YES || other.value == YES); \ |
| 53 | } \ |
| 54 | \ |
| 55 | private: \ |
| 56 | enum class Value : std::uint8_t { NO, YES }; \ |
| 57 | constexpr Type(Value value): value(value) {} \ |
| 58 | Value value; \ |
| 59 | }; \ |
| 60 | constexpr inline kj::LiteralStringConst KJ_STRINGIFY(Type value) { \ |
| 61 | return value ? #Type "::YES"_kjc : #Type "::NO"_kjc; \ |
| 62 | } \ |
| 63 | inline constexpr Type Type::NO{Type::Value::NO}; \ |
| 64 | inline constexpr Type Type::YES { \ |
| 65 | Type::Value::YES \ |
| 66 | } |
| 67 | |
| 68 | } // namespace workerd |