I am trying to write a macro which assists in building an enum class
with various helper functions, e.g. for conversion to string. It would be natural to provide access to all values of the enum in some kind of collection:
DEFINE_ENUM(Foo, Value1, Value2);
for (Foo v : enum_traits<Foo>::all_values) {
// ...
}
It seems like this could be accomplished by making the DEFINE_ENUM()
macro specialize a common enum_traits
class:
// globally:
template<typename T> struct enum_traits {};
// inside the macro:
#define DEFINE_ENUM(Name, ...) \
/* define "enum class Name" ... */ \
template<> struct my_enum_traits<Name> { \
/* define all_values member */ \
};
However, if the expansion of DEFINE_ENUM(Foo, Value1, Value2);
occurs inside a namespace, then it appears impossible for it to specialize a template from outside that namespace:
template<typename T> struct enum_traits {};
namespace foo {
// imagine DEFINE_ENUM is invoked here:
enum class Foo { Value1, Value2 };
// error: class template specialization of 'enum_traits'
// must occur at global scope
template<> struct ::enum_traits<Foo> { /* ... */ };
}
Is there any way to achieve this, i.e. for the macro to "escape" the namespace enclosing its invocation and specialize a template from a different namespace (even the global namespace)?
No you can't unuse a namespace. The only thing you can do is putting the using namespace -statement a block to limit it's scope.
using directives Use a using directive in an implementation file (i.e. *. cpp) if you are using several different identifiers in a namespace; if you are just using one or two identifiers, then consider a using declaration to only bring those identifiers into scope and not all the identifiers in the namespace.
Namespace is a feature added in C++ and is not present in C. A namespace is a declarative region that provides a scope to the identifiers (names of functions, variables or other user-defined data types) inside it. Multiple namespace blocks with the same name are allowed.
Avoid using directives (particularly using namespace std; ), except in specific circumstances. Using declarations are generally considered safe to use inside blocks. Limit their use in the global namespace of a code file, and never use them in the global namespace of a header file.
Well, I cannot directly help in this traits template specialization problem, I suspect this is not possible.
But it is possible to achieve your real goal with ADL
See the trick:
// globally:
template<typename T>
using enum_traits = decltype(get_enum_traits(T{}));
The trick is to define function get_enum_traits
in namespace of T
. This function shall have return type - the type that should be your traits. This function does not need an implementation - it is only ADL way to get type from within namespace of newly define enum type.
// inside the macro:
#define DEFINE_ENUM(Name, ...) \
/* define "enum class Name" ... */ \
enum class Name { __VA_ARGS__ }; \
struct Name##_type_traits { \
/* define all_values member */ \
}; \
Name##_type_traits get_enum_traits(Name);
Some demo that it really works.
#include <array>
// globally:
template<typename T>
using enum_traits = decltype(get_enum_traits(T{}));
// inside the macro:
#define DEFINE_ENUM(Name, ...) \
/* define "enum class Name" ... */ \
enum class Name { __VA_ARGS__ }; \
struct Name##_type_traits { \
static constexpr std::array<Name,1> values{{ Name{} }}; \
}; \
Name##_type_traits get_enum_traits(Name); // does not need implementation
namespace foo {
DEFINE_ENUM(Foo, Value1, Value2);
}
int main( ) {
for (auto e: enum_traits<foo::Foo>::values)
{}
}
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