Last active
September 21, 2016 10:21
-
-
Save flisboac/12ae2a056981fdd4c556ed54ccd59553 to your computer and use it in GitHub Desktop.
Testing new enum schemes in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Testing new enum schemes in C++ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <array> | |
#include <algorithm> | |
#include <functional> | |
// Some helper types (also documentation of a complete enum value's interface) | |
template <typename ID, ID InvalidValue> | |
class BasicNullableEnumValue_ { | |
public: | |
const ID id = InvalidValue; | |
constexpr BasicNullableEnumValue_() = default; | |
constexpr BasicNullableEnumValue_(const BasicNullableEnumValue_& rhs) = default; | |
constexpr BasicNullableEnumValue_(BasicNullableEnumValue_&& rhs) = default; | |
BasicNullableEnumValue_& operator=(const BasicNullableEnumValue_& rhs) = default; | |
BasicNullableEnumValue_& operator=(BasicNullableEnumValue_&& rhs) = default; | |
inline operator bool() const { return id == InvalidValue; } | |
inline bool operator==(const BasicNullableEnumValue_& rhs) const { | |
const bool thisValid = *this, rhsValid = rhs; | |
return (!thisValid && !rhsValid) || (thisValid && rhsValid && id == rhs.id); | |
} | |
inline bool operator!=(const BasicNullableEnumValue_& rhs) const { return !(*this == rhs); } | |
inline bool operator<(const BasicNullableEnumValue_& rhs) const { return id < rhs.id; } | |
protected: | |
constexpr BasicNullableEnumValue_(ID id_): | |
id(id_) {} | |
}; | |
template <typename ID> | |
class BasicEnumValue_ { | |
public: | |
const bool _null = true; | |
const ID id; | |
constexpr BasicEnumValue_() : _null(true), id(ID()) {} | |
constexpr BasicEnumValue_(const BasicEnumValue_& rhs) = default; | |
constexpr BasicEnumValue_(BasicEnumValue_&& rhs) = default; | |
BasicEnumValue_& operator=(const BasicEnumValue_& rhs) = default; | |
BasicEnumValue_& operator=(BasicEnumValue_&& rhs) = default; | |
inline operator bool() const { return !_null; } | |
inline bool operator==(const BasicEnumValue_& rhs) const { | |
const bool thisValid = *this, rhsValid = rhs; | |
return (!thisValid && !rhsValid) || (thisValid && rhsValid && id == rhs.id); | |
} | |
inline bool operator!=(const BasicEnumValue_& rhs) const { return !(*this == rhs); } | |
inline bool operator<(const BasicEnumValue_& rhs) const { return !!*this ? true : id < rhs.id; } | |
protected: | |
constexpr BasicEnumValue_(ID id_): | |
id(id_), _null(false) {} | |
}; | |
// Enum effectively starts here | |
enum class ETestType { | |
Unknown, | |
Raw, | |
Closure, | |
Pointer, | |
Inheritance | |
}; | |
template <ETestType type = ETestType::Unknown> | |
class TestType { | |
}; | |
template <> | |
struct TestType<ETestType::Raw> { | |
constexpr static const ETestType id = ETestType::Raw; | |
constexpr static const char repr = 'r'; | |
constexpr static const char *const name = "raw"; | |
}; | |
template <> | |
struct TestType<ETestType::Closure> { | |
constexpr static const ETestType id = ETestType::Closure; | |
constexpr static const char repr = 'c'; | |
constexpr static const char *const name = "closure"; | |
}; | |
template <> | |
struct TestType<ETestType::Pointer> { | |
constexpr static const ETestType id = ETestType::Pointer; | |
constexpr static const char repr = 'p'; | |
constexpr static const char *const name = "pointer"; | |
}; | |
template <> | |
struct TestType<ETestType::Inheritance> { | |
constexpr static const ETestType id = ETestType::Inheritance; | |
constexpr static const char repr = 'i'; | |
constexpr static const char *const name = "inheritance"; | |
}; | |
template <> | |
class TestType<ETestType::Unknown> { | |
public: | |
constexpr static const ETestType id = ETestType::Unknown; | |
constexpr static const char repr = '?'; | |
constexpr static const char *const name = "unknown"; | |
class Value : public BasicNullableEnumValue_<ETestType, ETestType::Unknown> { | |
public: | |
friend class TestType<ETestType::Unknown>; | |
const char repr = '\0'; | |
const char *const name = nullptr; | |
constexpr Value() = default; | |
private: | |
constexpr Value(ETestType id_, char repr_, const char *const name_): | |
BasicNullableEnumValue_<ETestType, ETestType::Unknown>::BasicNullableEnumValue_(id_), | |
repr(repr_), | |
name(name_) {} | |
}; | |
constexpr static inline std::array<ETestType, 5> ids() { | |
return { | |
ETestType::Unknown, | |
ETestType::Raw, | |
ETestType::Closure, | |
ETestType::Pointer, | |
ETestType::Inheritance | |
}; | |
} | |
constexpr static inline Value null_value() | |
{ return Value(); } | |
template <ETestType t = ETestType::Unknown> | |
constexpr static inline Value value() { | |
return Value( | |
TestType<t>::id, TestType<t>::repr, TestType<t>::name | |
); | |
} | |
constexpr static inline Value value(ETestType t) { | |
switch(t) { | |
case ETestType::Raw: return value<ETestType::Raw>(); | |
case ETestType::Closure: return value<ETestType::Closure>(); | |
case ETestType::Pointer: return value<ETestType::Pointer>(); | |
case ETestType::Inheritance: return value<ETestType::Inheritance>(); | |
} | |
return TestType<ETestType::Unknown>::value(); | |
} | |
}; | |
int main(void) { | |
std::cout << "Static ETestType::Raw::name: '" << TestType<ETestType::Raw>::name << "'" << std::endl; | |
std::cout << "Value-based ETestType::Closure::name: '" << TestType<>::value<ETestType::Closure>().name << std::endl; | |
std::cout << "null == null: " << (TestType<>::null_value() == TestType<>::null_value()) << std::endl; | |
std::cout << "null == ETestType::Closure: " << (TestType<>::null_value() == TestType<>::value<ETestType::Closure>()) << std::endl; | |
std::cout << "ETestType::Closure == null: " << (TestType<>::value<ETestType::Closure>() == TestType<>::null_value()) << std::endl; | |
std::cout << "ETestType::Closure == ETestType::Closure: " << (TestType<>::value<ETestType::Closure>() == TestType<>::value<ETestType::Closure>()) << std::endl; | |
auto ids = TestType<>::ids(); | |
std::for_each(ids.begin(), ids.end(), [](ETestType id) { | |
auto value = TestType<>::value(id); | |
std::cout << "ETestType(" << (int)id << ")" << std::endl | |
<< "\tid: " << (int)(value.id) << std::endl | |
<< "\trepr: " << value.repr << std::endl | |
<< "\tname: " << value.name << std::endl | |
; | |
}); | |
} | |
// This is just documentation on what it is to be expected from an enum value. | |
// Comparison is BY STATE. This means a semantic, value comparison, that takes into consideration | |
// the current state of the objects being compared, and not a specific instance of the class. | |
// For example, considering: | |
// enum EnumEnum { ENUM_A, ENUM_B }; | |
// struct EnumValue { int id; }; | |
// EnumValue someEnumValue = { ENUM_A }, anotherEnumValue = { ENUM_A }; | |
// A valid comparison would be like: | |
// someEnumValue.id == anotherEnumValue.id | |
// An invalid comparison would be like ... | |
// &someEnumValue == &anotherEnumValue | |
// ... Because it will fail for semantically equal values. Also, it should be possible to do this by | |
// way of operator overloading, that will be converted to the correct comparison independently of | |
// which instance is being used on both sides (singleton or not): | |
// someEnumValue == anotherEnumValue | |
// If a singleton comparison is needed, compare the enum IDs: | |
// ENUM_A == ENUM_B | |
// | |
// The enum value is immutable, independently of constness of the `this` object. This means all-const | |
// fields, in practice. | |
// | |
// The value object MUST ALLOW a null state. The default constructor MUST construct a null enum value, | |
// and additional constructors must be provided for creating non-null enum values. No constructor is | |
// allowed to create an enum value with an invalid enum ID. If needed, the non-default constructors may | |
// be set private or protected, to better control where instantiation is allowed (e.g. by using `friend`). | |
// | |
// There must be only a SINGLE null state. | |
// | |
// This rationale is adopted to allow for the enum's declaration style demonstrated below, as well as | |
// some compile-time optimizations (e.g. constexpr). | |
template <typename ID> class MinimumEnumValueConcept { | |
public: | |
// friend to the enum's template class, e.g. friend EnumClass<Enum::DefaultValue>; | |
// const-valued (const-only!) fields as needed, e.g. `const int number` | |
const ID id; // At least the `id` field must be present. | |
MinimumEnumValueConcept(); // instantiates a null enum value. Makes life much easier. | |
// copy constructors, move constructos, copy assignments, move assignments and operators as needed. | |
inline operator bool() const; // true if `this` is not null, false otherwise. | |
inline bool operator==(const MinimumEnumValueConcept& rhs) const; // - two nulls are equal; | |
// - null and non-null are different; | |
// - non-nulls with the same id are equal; | |
// - else, different. | |
inline bool operator!=(const MinimumEnumValueConcept& rhs) const; // The usual deal, `!(*this == other)` | |
inline bool operator<(const MinimumEnumValueConcept& rhs) const;// optional, but MUST compare ONLY ids | |
// (except for null values not represented by | |
// enum IDs, on which case they will always | |
// be less than every non-null value.) | |
private: | |
MinimumEnumValueConcept(ID id_); // instantiates a (generally non-null) enum value | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment