admin管理员组文章数量:1356788
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.
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.
Share Improve this question edited Mar 27 at 22:48 Questor asked Mar 27 at 20:51 QuestorQuestor 2481 silver badge8 bronze badges3 Answers
Reset to default 4You 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
Is there a way to perform a compile-time check to verify that each instance of a particular EnumClass has a unique value?
You cannot do it compile time cross translation unit (TU).
What you can do is to auto register the enum values into a std::set
and fail at runtime (before main
) when duplicate happens. something like:
// enumname.h
namespace EnumName {
class Type : public EnumClass<uint8_t>
{
public:
explicit constexpr Type(uint8_t value): EnumClass<uint8_t>(value) {}
constexpr Type() = default;
static bool registerEnums(std::initializer_list<Type> init) {
for (auto t : init) {
if (!types.insert(t).second) {
throw std::runtime_error("Duplicate enum");
}
}
return true;
}
static const std::set<Type>& all() { return types; }
private:
inline static std::set<Type> types;
};
}
// header1.h
namespace EnumName {
constexpr auto Value1 = Type(1);
constexpr auto Value2 = Type(2);
constexpr auto Value3 = Type(3);
inline static const bool _ = Type::registerEnums({Value1, Value2, Value3});
}
// header2.h
namespace EnumName {
constexpr auto Value1b = Type(11);
constexpr auto Value2b = Type(12);
constexpr auto Value3b = Type(13);
inline static const bool _ = Type::registerEnums({Value1b, Value2b, Value3b});
}
Demo
As "bonus", you might even access to the std::set
to iterate over each enum value.
I found a question regarding a similar problem (unique strings instead). And a rejected answer by randomusername which inspired me.
It was rejected because it doesn't identify duplicate strings if they aren't in the same function block.
However using it works in this instance as the EnumClass
needs to be inside of a shared namespace for it to be useable as a "enum".
// ServiceID.hpp
namespace ServiceID {
#define SET_SERVICE_ID(value) \
Type(value); constexpr Type ID_##value = Type(value); // var name collision will cause a compiler error for non-unique values.
class Type : public EnumClass<uint8_t> {
public:
explicit constexpr Type(uint8_t value): EnumClass<uint8_t>(value) {};
};
}
How to use this?
Inside of a service library where the service header is defined create a ServiceID
namespace. And then add the service ID pseudo enums to it that are specific to that service using the SET_SERVICE_ID()
macro.
// Some Service Library "Consts.hpp" or similar file...
#include "ServiceID.hpp"
namespace ServiceID {
constexpr auto LOGIN = SET_SERVICE_ID(1);
constexpr auto LOST_PASSWORD = SET_SERVICE_ID(2);
}
How this works, Creates a constexpr function definition in the ServiceID
namespace that will cause an identifier collision if multiple ServiceID's are created with the same value.
The downsides:
No automatic enum incrementing ... All the enums must be specified manually.
Creates 'unused' code (which is removed by the compiler). Which might violate some coding standards.
Uses a Macro, which might violate some coding standards.
Advantages:
The error message is surprisingly clear/easy to read for a Macro function, Points to previous definitions as well. So makes trouble shooting pretty easy.
Works across independent libraries without requiring them to depend on other libraries. (As long as they are used in the same project, eventually).
Service2.hpp(5) ServiceID::Type ServiceID::ServiceID_1(void) already has a body ... see previous definition Service1.hpp(5)
The code to make this magic happen is:
template <typename TYPE>
class EnumClass {
private:
TYPE m_value;
public:
explicit constexpr EnumClass(TYPE value) :
m_value(value){}
constexpr EnumClass() = default;
~EnumClass() = default;
constexpr explicit EnumClass(const EnumClass &) = default;
constexpr EnumClass& operator=(const EnumClass &) = default;
template<typename T> EnumClass(const T&) = delete; //prevent converting children to parent class, don't want polymorphism here.
template<typename T> EnumClass& operator=(const T&) = delete;
constexpr operator TYPE() const {return m_value;}
constexpr TYPE value() const {return m_value;}
};
namespace ServiceID {
#define SET_SERVICE_ID(value) \
Type(value); constexpr Type ID_##value = Type(value); // Each enum can be refered to by ServiceID::name or ServiceID::ID_value
class Type : public EnumClass<uint32_t> {
public:
explicit constexpr Type(uint32_t value): EnumClass<uint32_t>(value){
};
};
//Here or in other namespace ServiceID blocks
// Will work regardless of what include they are in as long as:
// All of the includes eventually make it into the same translation unit.
// the ServiceID 'enum' (or whatever you call it) is the same namespace (has the same parent namespaces...)
constexpr auto some_service = SET_SERVICE_ID(1);
constexpr auto another_service = SET_SERVICE_ID(2);
constexpr auto yas = SET_SERVICE_ID(3);
constexpr auto this_will_error = SET_SERVICE_ID(1);
}
本文标签: cDetect multiple of instances of a constexpr class with the same valueStack Overflow
版权声明:本文标题:c++ - Detect multiple of instances of a constexpr class with the same value? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744068394a2585436.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论