Search code examples
c++architecturenamespacesinversion-of-controlpackaging

Namespaces and packaging in C++


What is the best/cleanest way to use namespaces in packaged code?

E.g. in libraries like boost there seem to be very organized namespaces management, some techniques are used that allow to disambiguate names. The important thing, however, is that one won't see much code like

typedef namespace1::namespace2::sth_else::a_class<namespace3::namespace4::b_class> type;

usually, there's not much cross-namespacing, which indicates good architecture but also a good namespace management. The question is: what IS the good namespace management?

Say we have file structure like this:

component1/...  (depends on reusable_if)
component2/...  (depends directly on reusable_if and on component 1)
reusable/
reusable/some_part/
reusable/some_part/...
reusable/some_other_part/
reusable/some_other_part/...
reusable/SthThatUsesBothReusableParts.h   (implements reusable_if/ISth.h)
reusable/SthThatUsesBothReusableParts.cpp (implements reusable_if/ISth.h)
reusable_if/
reusable_if/ISth.h   (pure abstract class)
reusable_if/ISthElse.h (pure abstract class)
main.cpp (e.g. instantiates SthThatUsesBothReusableParts and passes to component1/2)

The reason why there is reusable_if/ folder is because both component1 and component2 want to reuse the same interfaces (hence none of them 'owns' the interfaces exclusively). Also, the assumption is that the project is indeed very big and needs proper namespaces for classes in each of the folders.

How would you apply namespaces in such a project? Say I declare all classes in reusable/ in namespace ::reusable. Should I put interfaces from reusable_if into namespace ::reusable or into ::reusable_if? Or maybe into none since it is used by component1 and component2?

What about namespaces in component1 and component2? Anything to remember? What about keyword using? Let's say that I decide to add this ::reusable_if namespace. Can I put using reusable_if into header files in component1 and component2, provided that using ... is placed inside namespace ::component1 and ::component2?

I am open to any suggestions, also those not necessarily related to the above example.


Solution

  • Here's what I use for my projects. My main rule is that each directory is a namespace and each file is a class, with very few exceptions (sometimes I group helper functions in subdirectories of a namespace detail, but without another nested namespace).

    1. Keep your entire project inside a single top-level namespace named after your project.

    2. Keep each utility component inside the top-level namespace, but in a separate directory. This is the only time I don't let my namespaces overlap with my directory tree.

    3. Keep each independently releasable component within your project inside a nested namespace named after your component. For convenience, provide a single header named after your component and containing your entire component interface either in the directory corresponding to your namespace or directly at the top-level directory of your project.

    4. Keep the implementation of each component inside a nested namespace detail. Contrary to classes, namespace do not have language support for private members, but the convention in Boost is that namespace detail should not directly be called by user-code.

    No further nesting than project::component::detail::function() or project:::component::class.member() is required. If you provide complete interfaces that facilitate ADL, you can reuse your component functions inside your project as function(x) for a variable x of type project::component::class without worrying about name clashes.

    Note that in Uncle Bob's language: "the unit of reuse is the unit of release". Each component should provide a bunch of coherent and mutually dependent classes and functions. In particular, it should provide a complete interface for that component. The C++ language will support this through argument-dependent-lookup (ADL). See this old column "Namespaces and the Interface Principle" by Herb Sutter.

    The presence of reuse_if and the presence of both component and reusable might be a code smell, unless the considerations that you mentioned in the comments actually apply. A component should be the unit of release. If you could independently reuse a piece of code, make it a separate component. If the code depends on another piece, release it together with that other code. See Sutter's column for these dependency relations.