Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible for a declaration to escape its enclosing namespace?

Tags:

c++

namespaces

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)?

like image 674
jtbandes Avatar asked Apr 23 '18 23:04

jtbandes


People also ask

How do I get out of 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.

What are is difference between using namespace directive and using the using declaration for accessing namespace members?

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.

What is the purpose of namespace in C++?

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.

Do not use namespace using directives use using declarations instead?

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.


1 Answers

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)
    {}
}
like image 99
PiotrNycz Avatar answered Nov 02 '22 05:11

PiotrNycz