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;
  // Room r; //Error: directly not accessible.

  return 0;
}

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 thrown out.
  using namespace A;
  using namespace B;
  Base base;// Error: which one, A::Base or B::Base?

  return 0;
}

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
int main(){
    Shop::Car car;
    Shop::Client client;
    return 0;
}

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;
}

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;
}

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;
}

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.

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