Search code examples
c++compiler-construction

defining an enclosing_namespace - how does this impact clang and g++ (compile times, memory usage, etc)


Given the example code segment:

namespace project_namespace::my_math
{
    namespace enclosing_namespace = my_math;

    struct sphere {};
    
    struct sphere_stuff {
        using sphere = enclosing_namespace::sphere;
    };
    
    struct line {};
    
    struct line_stuff {
        using line = enclosing_namespace::line;
    } ;
}


int main()
{
    using sphere_stuff = project_namespace::my_math::sphere_stuff;
    sphere_stuff::sphere sphere;

    using line_stuff = project_namespace::my_math::line_stuff;
    line_stuff::line line;
    
    return 0;
}

When I look at the above code, I think to myself:

  1. Yay, enclosing_namespace/super_namespace, I wish c++ had that! Did I just figure out how to do this?
  2. I wonder if this is a horrible idea?

So my question has two parts:

  1. When the clang and g++ compilers encounter this "namespace enclosing_namespace = my_math", how do they handle this (c++17 and c++20)? In the above segment (if used in a large code base), am I creating a situation where namespaces are copied into namespaces into namespace, etc, a nightmare situation for a compiler? Or is this trivial for a compiler.

  2. Is there some reason why this really shouldn't be done (in 2023, c++17 or c++20)?

Edit: Originally the code used "super_namespace" instead of "enclosing_namespace" - some of the comments below referred to this.


Solution

  • I went and asked over on the llvm issues page, although with a more complicated example.

    They provided me with a link to an online compiler which generates an AST dump, and also clarified that basically there is just one node inserted per alias.

    The example I provided them was:

    namespace project {
        namespace enclosing_namespace = project;
        
        namespace namespace_1 {
            namespace parent_namespace = project;
            namespace enclosing_namespace = namespace_1;
        
            namespace b {
                namespace parent_namespace = namespace_1;
                namespace enclosing_namespace = b;
                
                struct X {};
                struct Y {
                    using Z = enclosing_namespace::X;
                } ;
            }
        }
        
        namespace namespace_2 {
            namespace parent_namespace = project;
            namespace enclosing_namespace = namespace_2;
        
            namespace a {
                namespace parent_namespace = namespace_2;
                namespace enclosing_namespace = a;
        
                struct X {};
                struct Y {
                    using Z = enclosing_namespace::X;
                } ;
            }
        }
    }
    
    int main()
    {
        project::namespace_1::parent_namespace::namespace_2::a::Y::Z z;
    
        return 0;
    }
    

    And the dump looks like this:

    TranslationUnitDecl
    |-NamespaceDecl <line:1:1, line:33:1> line:1:11 project
    | |-NamespaceAliasDecl <line:2:5, col:37> col:15 enclosing_namespace
    | | `-Namespace 'project'
    | |-NamespaceDecl <line:4:5, line:17:5> line:4:15 namespace_1
    | | |-NamespaceAliasDecl <line:5:9, col:38> col:19 parent_namespace
    | | | `-Namespace 'project'
    | | |-NamespaceAliasDecl <line:6:9, col:41> col:19 enclosing_namespace
    | | | `-Namespace 'namespace_1'
    | | `-NamespaceDecl <line:8:9, line:16:9> line:8:19 b
    | |   |-NamespaceAliasDecl <line:9:13, col:42> col:23 parent_namespace
    | |   | `-Namespace 'namespace_1'
    | |   |-NamespaceAliasDecl <line:10:13, col:45> col:23 enclosing_namespace
    | |   | `-Namespace 'b'
    | |   |-CXXRecordDecl <line:12:13, col:23> col:20 referenced struct X definition
    | |   | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
    | |   | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
    | |   | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
    | |   | | |-MoveConstructor exists simple trivial needs_implicit
    | |   | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
    | |   | | |-MoveAssignment exists simple trivial needs_implicit
    | |   | | `-Destructor simple irrelevant trivial needs_implicit
    | |   | `-CXXRecordDecl <col:13, col:20> col:20 implicit struct X
    | |   `-CXXRecordDecl <line:13:13, line:15:13> line:13:20 struct Y definition
    | |     |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
    | |     | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
    | |     | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
    | |     | |-MoveConstructor exists simple trivial needs_implicit
    | |     | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
    | |     | |-MoveAssignment exists simple trivial needs_implicit
    | |     | `-Destructor simple irrelevant trivial needs_implicit
    | |     |-CXXRecordDecl <col:13, col:20> col:20 implicit struct Y
    | |     `-TypeAliasDecl <line:14:17, col:48> col:23 Z 'enclosing_namespace::X':'project::namespace_1::b::X'
    | |       `-ElaboratedType 'enclosing_namespace::X' sugar
    | |         `-RecordType 'project::namespace_1::b::X'
    | |           `-CXXRecord 'X'
    | `-NamespaceDecl <line:19:5, line:32:5> line:19:15 namespace_2
    |   |-NamespaceAliasDecl <line:20:9, col:38> col:19 parent_namespace
    |   | `-Namespace 'project'
    |   |-NamespaceAliasDecl <line:21:9, col:41> col:19 enclosing_namespace
    |   | `-Namespace 'namespace_2'
    |   `-NamespaceDecl <line:23:9, line:31:9> line:23:19 a
    |     |-NamespaceAliasDecl <line:24:13, col:42> col:23 parent_namespace
    |     | `-Namespace 'namespace_2'
    |     |-NamespaceAliasDecl <line:25:13, col:45> col:23 enclosing_namespace
    |     | `-Namespace 'a'
    |     |-CXXRecordDecl <line:27:13, col:23> col:20 referenced struct X definition
    |     | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
    |     | | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr
    |     | | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
    |     | | |-MoveConstructor exists simple trivial
    |     | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
    |     | | |-MoveAssignment exists simple trivial needs_implicit
    |     | | `-Destructor simple irrelevant trivial needs_implicit
    |     | |-CXXRecordDecl <col:13, col:20> col:20 implicit struct X
    |     | |-CXXConstructorDecl <col:20> col:20 implicit used constexpr X 'void () noexcept' inline default trivial
    |     | | `-CompoundStmt <col:20>
    |     | |-CXXConstructorDecl <col:20> col:20 implicit constexpr X 'void (const X &)' inline default trivial noexcept-unevaluated 0xbb9e218
    |     | | `-ParmVarDecl <col:20> col:20 'const X &'
    |     | `-CXXConstructorDecl <col:20> col:20 implicit constexpr X 'void (X &&)' inline default trivial noexcept-unevaluated 0xbb9e418
    |     |   `-ParmVarDecl <col:20> col:20 'X &&'
    |     `-CXXRecordDecl <line:28:13, line:30:13> line:28:20 struct Y definition
    |       |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
    |       | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
    |       | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
    |       | |-MoveConstructor exists simple trivial needs_implicit
    |       | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
    |       | |-MoveAssignment exists simple trivial needs_implicit
    |       | `-Destructor simple irrelevant trivial needs_implicit
    |       |-CXXRecordDecl <col:13, col:20> col:20 implicit struct Y
    |       `-TypeAliasDecl <line:29:17, col:48> col:23 referenced Z 'enclosing_namespace::X':'project::namespace_2::a::X'
    |         `-ElaboratedType 'enclosing_namespace::X' sugar
    |           `-RecordType 'project::namespace_2::a::X'
    |             `-CXXRecord 'X'
    `-FunctionDecl <line:35:1, line:40:1> line:35:5 main 'int ()'
      `-CompoundStmt <line:36:1, line:40:1>
        |-DeclStmt <line:37:5, col:67>
        | `-VarDecl <col:5, col:66> col:66 z 'project::namespace_1::parent_namespace::namespace_2::a::Y::Z':'project::namespace_2::a::X' callinit
        |   `-CXXConstructExpr <col:66> 'project::namespace_1::parent_namespace::namespace_2::a::Y::Z':'project::namespace_2::a::X' 'void () noexcept'
        `-ReturnStmt <line:39:5, col:12>
          `-IntegerLiteral <col:12> 'int' 0
    

    You can also play with the online compiler here: https://godbolt.org/z/G4beW7YnP

    So it seems that creating these namespace aliases is almost free.