Is there a better way to express nested namespaces in C++ within the header

前端 未结 11 2448
天涯浪人
天涯浪人 2020-12-13 03:31

I switched from C++ to Java and C# and think the usage of namespaces/packages is much better there (well structured). Then I came back to C++ and tried to use namespaces the

相关标签:
11条回答
  • 2020-12-13 03:38

    Yes, you will have to do it like

    namespace A{ 
    namespace B{
    namespace C{} 
    } 
    }
    

    However, you are trying to use the namespaces in a way they are not supposed to be used. Check this question, maybe you will find it useful.

    0 讨论(0)
  • 2020-12-13 03:39

    I fully support peterchen's answer but want to add something that addresses another part of your question.

    Declaring namespaces is one of the very rare cases in C++ where I actually like the use of #defines.

    #define MY_COMPANY_BEGIN  namespace MyCompany { // begin of the MyCompany namespace
    #define MY_COMPANY_END    }                     // end of the MyCompany namespace
    #define MY_LIBRARY_BEGIN  namespace MyLibrary { // begin of the MyLibrary namespace
    #define MY_LIBRARY_END    }                     // end of the MyLibrary namespace
    

    This also removes the need for comments near the closing brace of the namespace (Did you ever scroll down to the bottom of a large source file and tried to add/remove/balance braces that were missing comments about which brace closes which scope? Not fun.).

    MY_COMPANY_BEGIN
    MY_LIBRARY_BEGIN
    
    class X { };
    
    class Y { };
    
    MY_LIBRARY_END
    MY_COMPANY_END
    

    If you want to put all namespace declarations on a single line you can do that as well with a bit of (pretty ugly) preprocessor magic:

    // helper macros for variadic macro overloading
    #define VA_HELPER_EXPAND(_X)                    _X  // workaround for Visual Studio
    #define VA_COUNT_HELPER(_1, _2, _3, _4, _5, _6, _Count, ...) _Count
    #define VA_COUNT(...)                           VA_HELPER_EXPAND(VA_COUNT_HELPER(__VA_ARGS__, 6, 5, 4, 3, 2, 1))
    #define VA_SELECT_CAT(_Name, _Count, ...)       VA_HELPER_EXPAND(_Name##_Count(__VA_ARGS__))
    #define VA_SELECT_HELPER(_Name, _Count, ...)    VA_SELECT_CAT(_Name, _Count, __VA_ARGS__)
    #define VA_SELECT(_Name, ...)                   VA_SELECT_HELPER(_Name, VA_COUNT(__VA_ARGS__), __VA_ARGS__)
    
    // overloads for NAMESPACE_BEGIN
    #define NAMESPACE_BEGIN_HELPER1(_Ns1)             namespace _Ns1 {
    #define NAMESPACE_BEGIN_HELPER2(_Ns1, _Ns2)       namespace _Ns1 { NAMESPACE_BEGIN_HELPER1(_Ns2)
    #define NAMESPACE_BEGIN_HELPER3(_Ns1, _Ns2, _Ns3) namespace _Ns1 { NAMESPACE_BEGIN_HELPER2(_Ns2, _Ns3)
    
    // overloads for NAMESPACE_END
    #define NAMESPACE_END_HELPER1(_Ns1)               }
    #define NAMESPACE_END_HELPER2(_Ns1, _Ns2)         } NAMESPACE_END_HELPER1(_Ns2)
    #define NAMESPACE_END_HELPER3(_Ns1, _Ns2, _Ns3)   } NAMESPACE_END_HELPER2(_Ns2, _Ns3)
    
    // final macros
    #define NAMESPACE_BEGIN(_Namespace, ...)    VA_SELECT(NAMESPACE_BEGIN_HELPER, _Namespace, __VA_ARGS__)
    #define NAMESPACE_END(_Namespace, ...)      VA_SELECT(NAMESPACE_END_HELPER,   _Namespace, __VA_ARGS__)
    

    Now you can do this:

    NAMESPACE_BEGIN(Foo, Bar, Baz)
    
    class X { };
    
    NAMESPACE_END(Baz, Bar, Foo) // order doesn't matter, NAMESPACE_END(a, b, c) would work equally well
    
    Foo::Bar::Baz::X x;
    

    For nesting deeper than three levels you would have to add helper macros up to the desired count.

    0 讨论(0)
  • 2020-12-13 03:40

    This paper covers the subject rather well: Namespace Paper

    Which basically boils down this. The longer your namespaces are the more likely the chance that people are going to use the using namespace directive.

    So looking at the following code you can see an example where this will hurt you:

    namespace abc { namespace testing {
        class myClass {};
    }}
    
    namespace def { namespace testing {
        class defClass { };
    }}
    
    using namespace abc;
    //using namespace def;
    
    int main(int, char**) {
        testing::myClass classInit{};
    }
    

    This code will compile fine, however, if you uncomment the line //using namespace def; then the "testing" namespace will become ambiguous and you will have naming collisions. This means that your code base can go from stable to unstable by including a 3rd party library.

    In C#, even if you were to use using abc; and using def; the compiler is able to recognize that testing::myClass or even just myClass is only in the abc::testing namespace, but C++ will not recognize this and it is detected as a collision.

    0 讨论(0)
  • 2020-12-13 03:41

    Both standards (C++2003 and C++11) are very explicit that the name of the namespace is an identifier. This means that explicit nested headers are required.

    My impression that this is not a big deal to allow placing qualified identifier besides a simple name of the namespace, but for some reason this is not allowed.

    0 讨论(0)
  • 2020-12-13 03:44

    To avoid really deep indenting, I usually do it this way:

    namespace A { namespace B { namespace C
    {
        class X
        {
            // ...
        };
    }}}
    
    0 讨论(0)
  • 2020-12-13 03:44

    C++ namespaces are used to group interfaces, not to divide components or express political division.

    The standard goes out of its way to forbid Java-like use of namespaces. For example, namespace aliases provide a way to easily use deeply-nested or long namespace names.

    namespace a {
    namespace b {
    namespace c {}
    }
    }
    
    namespace nsc = a::b::c;
    

    But namespace nsc {} would then be an error, because a namespace may only be defined using its original-namespace-name. Essentially the standard makes things easy for the user of such a library but hard for the implementer. This discourages people from writing such things but mitigates the effects if they do.

    You should have one namespace per interface defined by a set of related classes and functions. Internal or optional sub-interfaces might go into nested namespaces. But more than two levels deep should be a very serious red flag.

    Consider using underscore characters and identifier prefixes where the :: operator isn't needed.

    0 讨论(0)
提交回复
热议问题