Creating namespaces from namespaces

It’s difficult to imagine even a simple program which doesn’t use something from the std namespace, so most C++ programmers are introduced to namespaces early on. Namespaces use the same double-colon syntax as static members of structs and classes, so sometimes it’s not clear to user code which is which. Consider the following code fragment:

#include <cmath>
//...
namespace Math {
    struct Constants {
        constexpr static auto PI = 2.0 * asin(1.0);
    }; // semi-colon is mandatory
}

The fully qualified name of PI is, of course, Math::Constants::PI. It is important to note that namespaces are open, that is they can be added to by multiple code fragments (including from different source files). In contrast, the struct shown here is closed, nothing can be added after the closing curly brace. Here is a possible subsequent addition to namespace Math:

namespace Math {
    struct Functions {
        constexpr static double asin(double n) { return ::asin(n); }
    };
}

Notice that the prefix :: within the function body selects the global asin, thus preventing infinite recursion. Also notice that the same technique, opening a previously defined namespace, could be used to add to the std namespace; however, according to the Standard doing so leads to undefined behaviour (with a few exceptions).

So, we’re all good with nesting structs with static members inside namespaces. What you may not know is that it is possible to rename a namespace (actually, to make a copy) with a using directive, for example:

namespace MyNamespace {
    using namespace std;
}

This copies all of the contents of the std namespace, and puts them in the new namespace MyNamespace, so you would now use MyNamespace::cout, for example. (This is not possible with structs.) More usefully perhaps, it is possible to specify individual entities from std to put into the namespace with using statements, as shown in this example program:

#include <iostream>
#include <string_view>
#include <vector>

namespace ext {
    using std::cout;
    using std::ostream;
    using std::vector;
    using std::string_view;
}

using namespace std::string_view_literals;

namespace ext {
template <typename T>
ostream& operator<< (ostream& os, vector<T> vec) {
    if (vec.empty()) {
        return os << "{}"sv;
    }
    auto sep = ""sv;
    os << "{ "sv;
    for (const auto &elem : vec) {
        os << sep << elem;
        sep = ", "sv;
    }
    os << " }"sv;
    return os;
}
}

int main() {
    using namespace ext;
    vector<int> primes{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
    cout << primes << '\n';
    vector<string_view> odd{ "one"sv, "three"sv, "five"sv, "seven"sv, "nine"sv };
    cout << odd << '\n';
}

Here we have created a new namespace called ext at lines 7-12 and added four entities (one object and three types) from std. The name ext is commonly used to indicate “extending the Standard Library”. Then line 14 proves you really can mix and match, by enabling string view literal suffixes globally (this means "a string view"sv is a string view object, not a const char *).

Lines 18-30 overload the stream extraction operator << for ext::vector (with any element type, you may find this code useful in your own projects) and has to be a generic function qualified with the template keyword. The parameter list and return type do not have to be fully qualified names, as the whole definition has been wrapped in a second namespace ext scope (beginning and ending on lines 16 and 31).

The main() function defined in lines 33-39 should present no surprises. Line 34 is a using directive that makes all of the contents of ext (but not std) available to use locally within the function, without needing to qualify with the namespace name. This includes the overloaded stream extraction operator << for ext::vector, which would otherwise not be visible.

You’re all set to experiment with namespaces on your own now; as an exercise you could follow the steps needed to use endl instead of '\n' twice, within main(). Also, you could try to adapt the code having removed the second namespace scope (qualifying names as needed), and generalize the operator<< to support any container.

Resources: Download or browse the source code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s