How to use C++ namespace for large projects

Introduction

When reading online C++ tutorials and training examples, one important feature is mostly overlooked: namespaces. They are used in real-life libraries and applications. With namespace, blocks of code can be isolated in big projects, therefore, name conflicts can be avoided.

Basics

A namespace block can be defined like

namespace A{
    class X{};
    class Y{};
} 

We can refer to X outside of namespace A with :: operator

namespace A{
    class X{};
    class Y{};
} 

A::X x; // Note :: operator

Reveal content

We can reveal all the content to a scope with using directive:

namespace People{
    class Employee{};
    class Manager{};
}
namespace Building{
    class Department{};
    class Room{};
} 

using namespace People; // all content of People are visible in the code below
using Building::Department; // only Department is visible in the code below

int main(){
  Employee e; // OK 
  Manager m; // OK

  Department d; // OK
  Room r; //Error: directly not accessible.

  return 0;
}
(.Get 1)

The using namespace directive can be declared inside a block or function, where it is valid only within those scopes:

namespace People{
    class Employee{};
}

void f(){
  using namespace People;
  Employee e; // OK
}

void g(){
    {
        using namespace People;
        Employee e; // OK
    }
    // Employee e; // Error: 'Employee' is not defined in this scope
}

// Employee e; // Error: 'Employee' is not defined in this scope

This strategy can make the code easier to read, especially when a function uses many symbols from a single namespace.

However, you cannot declare using namespace within a class:

class A{
    using namespace People; // Error: 'using directive' is not allowed
};

Names can be categorized to avoid conflicts

namespace A{class Base{};} 
namespace B{class Base{};} 

int main(){

  A::Base baseA; // OK
  B::Base baseB; // OK

  // But they conflict if namespaces thrown away.
  using namespace A;
  using namespace B;
  Base base;// Error: which one, A::Base or B::Base?

  return 0;
}
(.Get 1)

Namespace extension

A namespace can be extended with separated blocks, even in different files:

// car.h
namespace Shop{class Car{};}  // Car class added to Shop namespace

// client.h
namespace Shop{class Client{};} // Client class also added to Shop namespace

// app.cpp
#include "car.h"
#include "client.h"
int main(){
    Shop::Car car;
    Shop::Client client;
    return 0;
}
(.Get 1)

The global namespace

Any declaration outside of explicitly defined namespaces belongs to the global namespace. To specifically access the global namespace use ::SomeClass.

namespace MyProject {
  class Person{public: double height;};
}
class Person{public: int age;};

using namespace MyProject; 

int main(){
    ::Person p; // OK, global Person is used. 
    p.age=30;
  return 0;
}

Nested namespaces

We can have nested namespaces

namespace App{
    namespace Shape{
        class Box{};
    }
} 

It is easier to write the above code as:

namespace App::Shape{
    class Box{};
}

int main(){
    App::Shape::Box box; // Access nested parameter 
    return 0;
}
(.Get 1)

Namespace aliases

We can create an alias for a long name or nested namespace:

namespace Zoo::Animal::Birds{ class Penguin{};}

namespace ZAB = Zoo::Animal::Birds;
int main(){    
  ZAB::Penguin p;
  return 0;
}

Use aliases in source (.cpp) files. Avoid using them in header (.h or .hpp) files as they will propagate wherever the headers are included.

Namespace content

Everything, except the main function, can be declared/defined in a namespace:

namespace A{
  int x; 
  using mint = int;
  class person{};
  struct data{};
  void func(){};
  namespace B{/*more definitions*/};
} 

int main(){
  A::x = 10;
  A::mint j;
  A::person p;
  A::data d;
  A::func();
  return 0;
}

Unnamed namespace

Unnamed or anonymous namespaces are used to ensure internal linkage for symbols of a translation unit (a cpp file including its headers). At times, you may want to have private symbols (classes, functions, or variables) specific to a translation unit. To do so, we add them to the cpp file inside an unnamed namespace.

In the example below, both add and sqAdd translation units accidentally have two separate private functions with the same name, helper:

// add.h
namespace MyProject
{ int add(int a, int b); }

// add.cpp
int helper(int a, int b)
{
    return a + b;
}

namespace MyProject
{
    int add(int a, int b)
    {
        return helper(a, b);
    }
}

// sqAdd.h
namespace MyProject
{ int sqAdd(int a, int b); }

// sqAdd.cpp
int helper(int a, int b)
{
    return a*a + b*b;
}

namespace MyProject
{
    int sqAdd(int a, int b)
    {
        return helper(a, b);
    }
}

// main.cpp
#include "add.h"
#include "sqAdd.h"

int main(){
    MyProject::add(2,3);
    MyProject::sqAdd(2,3);
    return 0;
}

You can compile this with MS C++ cl:

cl -c add.cpp
cl -c sqAdd.cpp
cl main.cpp add.obj sqAdd.obj

We get a linkage error:

 error LNK2005: "int __cdecl helper(int,int)" (?helper@@YAHHH@Z) already defined in sqAdd.obj

This is because both add.obj and sqAdd.obj expose their helper functions. To prevent them from exposing them to other units, we use unnamed namespaces:

// add.cpp
namespace{
  int helper(int a, int b)
  {
    return a + b;
  }
}

// sqAdd.cpp
namespace{
  int helper(int a, int b)
  {
      return a*a + b*b;
  }
}

Upon compiling it again, the helper functions will not be exposed.

Some points related to this topic are:

  • It is a good practice to always add tranlational unit’s private symbols in an unnamed namespace.

  • If it aligns with your coding style, you can achieve internal linkage by defining the helper functions as static.

  • Be cautious not to add symbols in an unnamed namespace to public headers to get private behavior, because the symbols will become visible wherever the headers are included.

How to use

The concept of the namespace is very similar to directories in the operating system. On our computer, we organize our files in folders: photos, videos, documents, and so forth. So, we quickly find them and also avoid file name collisions. Moreover, namespaces are well supported by auto-completion and suggestions of IDEs like VS Code.

So my conventions are:

  • For a new project, create a namespace with the project name. Everything is written in this namespace (except main()). So our project becomes portable and can be included in other projects without the fear of name conflicts.
namespace MyProject{
 class Particle{};
 class Motion{};
 class WriteOutput{};
}
  • Each class is saved into two files: the header and implementation. Each class is placed in a namespace.
// Particle.h
namespace MyProject {
  class Particle{
    public:void Move();
  };
}
// Particle.cpp
#include "Particle.h"
namespace MyProject{
  void Particle::Move (){
    std::cout<<"Particle is moving...";
  };
}
  • Do not use using statement in headers, .h files. If you do so, they are added to different files and they inject their names. That defeats the namespace purpose. But, being mindful of other namepspaces, feel free to use them in the implementation, .cpp, files.

  • If there are a few classes in a namespace, probably they don’t need to be divided into new namespaces.

  • However, sometimes, the name of classes indicates the separation of their namespaces from the begining.

namespace MyProject::Particle{
    class Base{};
    class Base2D{};
    class Base3D{};
}
namespace MyProject::Box{
    class Base{};
    class Base2D{};
    class Base3D{};
}
  • When the number of classes in a namespace hits 10 or 20, maybe that’s the time for creating new sub-namespaces.
namespace MyProject{
    class Particle2D_Base{};
    class Particle2D_Point{};
    class Particle2D_Volume{};
    class Particle2D_Multi{};
    class Particle2D_variableRadius{};

    class Particle3D_Base{};
    class Particle3D_Point{};
    class Particle3D_Volume{};
    class Particle3D_Multi{};
    class Particle3D_variableRadius{};
}

The above design is changed to the below one:

namespace MyProject::Particle2D{
    class Base{};
    class Point{};
    class Volume{};
    class Multi{};
    class variableRadius{};
}
namespace MyProject::Particle3D{
    class Base{};
    class Point{};
    class Volume{};
    class Multi{};
    class variableRadius{};
}
  • The namespaces hierarchy is the same as project directories. So the directories of previous examples are:
MyProject
|
|__Particle
|       |_Base.h
|       |_Base.cpp
|       |_ ...
|
|__Box
|   |_Base.h
|   |_Base.cpp
|   |_ ...

Check this example in my CMake post where we have same-name files in different directories and same-name classes in different namespaces.

If there are public headers, use CMake to separate them. See this example in my post on CMake compiling a shared library.

References

Isocpp microsoft docs

Tags ➡ C++

Subscribe

I notify you of my new posts

Latest Posts

Comments

0 comment