I don't know if this is possible, and suspect it probably isn't.
Using an enum based on Andrew Goedhart's EnumClass
Short explanation, Goedhart's EnumClass creates a pseudo Enum that uses constexpr and namespace to create an approximation of an enum class, with the exception that new enums can be added to it inside of separate headers/packages. A use-case for this is wanting to uniquely identify Services by IDs, with the same ID used for a particular service across multiple platforms/products.
Is there a way to perform a compile-time check to verify that each instance of a particular EnumClass has a unique value?
I know that __COUNTER__ exists, which is an incrementing value that keeps track of how often it has been invoked (and it violates a lot of rules in doing so). Is there something similar that I could use - like a compiler set?
EDIT: claryifing the requirements.
This assumes that these IDs are for independent services that live inside of their own library. And do not depend on other service packages unless it needs to. So I cannot use some type enum extension.. Must be able to independly add enumlike values and verify their uniqueness.
You could create a class template taking type parameter (T) and then a parameter pack of non-type parameters (T...).
#include <type_traits>
template <class T, T...>
inline constexpr bool Unique = true;
template <class T, T val, T... vals>
inline constexpr bool Unique<T, val, vals...> =
(... && (val != vals)) && Unique<T, vals...>;
template <class T, T... vals>
requires Unique<T, vals...>
struct Enum {
constexpr Enum(T value) : m_val(value) {
if ((... || (value == vals))) {
// happy
} else {
// not a value defined for this Enum
}
}
constexpr operator T() const { return m_val; }
T m_val;
};
To extend it, you could add a helper:
template <class, auto...>
struct ExtendImpl;
template <class T, T... vals, T... newvals>
struct ExtendImpl<Enum<T, vals...>, newvals...> {
using type = Enum<T, vals..., newvals...>;
};
template <class T, auto... newvals>
using Extend = ExtendImpl<T, newvals...>::type;
You could now create your original Enum with a set of unique values which will be validated at compile time:
using EnumFoo = Enum<int, 2, 8, 9>;
And extend it if you wish:
using EnumBar = Extend<EnumFoo, 10, 11, 12>;
If you try to extend it with a number already present in EnumFoo, it'll fail to compile.
Of course, this doesn't prevent different extensions from creating colliding Enum<T, T...>:
using EnumFoo = Enum<int, 2, 8, 9>;
using EnumFoo2 = Extend<EnumFoo, 10, 11, 12>;
using EnumFoo3 = Extend<EnumFoo, 10>; // oh no...
... but if you can enforce only extending the "latest", it would work:
using EnumFoo = Enum<int, 2, 8, 9>;
using EnumFoo2 = Extend<EnumFoo, 10, 11, 12>;
using EnumFoo3 = Extend<EnumFoo2, 10>; // collision and compile time error
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With