Skip to content

Instantly share code, notes, and snippets.

@flisboac
Last active September 21, 2016 10:21
Show Gist options
  • Save flisboac/12ae2a056981fdd4c556ed54ccd59553 to your computer and use it in GitHub Desktop.
Save flisboac/12ae2a056981fdd4c556ed54ccd59553 to your computer and use it in GitHub Desktop.
Testing new enum schemes in C++
Testing new enum schemes in C++
#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