Controlling name visibility in C++ using-directive
Have you ever wanted to control which names become visible when your namespace is the subject of a using-directive (e.g., using namespace std;
)? Maybe you have some less-commonly used names in your namespace that you don’t want to bring into the user’s namespace in order to avoid possible conflicts with other libraries?
The other day I ran into a similar problem in ODB, the C++ ORM system I am working on. We are implementing the so-called profile libraries which provide ODB persistence support for containers, smart pointers, and value types that are found in various third-party libraries and frameworks, such as C++ TR1, Boost, and Qt. We’ve decide that the most natural way to organize these profile libraries is for each profile to have a separate namespace inside the odb
namespace that will correspond to the library. So the C++ TR1 support will be in odb::tr1
, Boost will be in odb::boost
, etc. The advantage of this schema is that the user code looks nice and logical, for example:
typedef boost::shared_ptr<employer> employer_ptr; typedef odb::boost::shared_ptr<employee> employee_ptr;
But, as it turns out, there is a big problem with such parallel namespace hierarchies. Consider this innocent-looking code fragment:
using namespace odb; typedef boost::shared_ptr<employer> employer_ptr;
The reference to the boost
namespace in the second line is now ambiguous because the using-directive in the previous line brought in the boost
namespace from odb
.
Asking users not to use using-directives with the odb
namespace is not an option since its use to bring in all the DB-related names, such as database
, transaction
, etc., is quite handy. In fact, we use it ourselves in all ODB examples and tests.
The next thing we looked at while searching for a solution is changing the namespace hierarchy somehow. For example, we could rename the Boost namespace inside odb
(e.g., to odb::boost_profile
) to avoid the ambiguity. Or, we could hide the Boost namespace with an intermediate namespace (e.g., odb::profile::boost
). This way the using-directive would only bring in the intermediate namespace saving us from the ambiguity. The major problem with these solutions is inelegance; with them the user code will no longer be as succinct.
Then we started thinking about the root of this issue. The problem is not the namespace hierarchy that is somehow bad. The problem is the indiscreet C++ using-directive mechanism. Yes, it is convenient since it allows you to bring in a whole bunch of names that you need in one fell swoop. But it will also bring in a lot of names that you don’t need or never even knew existed. Usually this is harmless but in some cases these extraneous names may collide with the ones that we actually want to use. And that’s exactly what happened in ODB.
After this analysis the ideal solution became obvious: for a given namespace we need a way to control which names are subject to the using-directive. If we had such a C++ mechanism, we could have excluded the profile namespaces from this list and the ambiguity problem would have been solved (if, for some reason, someone wanted to have, say, the odb::boost
namespace brought into their current namespace, they would have been able to achieve this with a namespace alias: namespace boost = odb::boost;
).
As we all know there is no such mechanism in C++ and this is probably for the best (the language is complex enough as it is). Fortunately, we can get pretty close using what I call, for a lack of a better term, a “using-directive namespace”. In a nutshell, the idea is to create a nested namespace whose sole purpose is to collect a list of names that should be “exported” with a using-directive. This list is assembled with using-declarations (and namespace aliases, if you wish to include nested namespace). In ODB we called this namespace core
since it contains core ODB API names that should be sufficient for most applications. Below is an outline of the odb
namespace:
namespace odb { class database {...}; class transaction {...}; namespace core { using odb::database; using odb::transaction; } namespace boost { // Boost profile. } namespace tr1 { // TR1 profile. } }
On the client side, we now use odb::core
instead of odb
in using-directives. Note also that using-declarations and qualified names can (and should) continue using the original odb
namespace; odb::core
is purely for using-directives:
using namespace odb::core; using odb::tr1::lazy_shared_ptr; typedef odb::boost::shared_ptr<employee> employee_ptr; void f (database& db);
While some users may find the odb::core
syntax somewhat strange, I actually like the extra assurance it implies: you are going to get the core set of names necessary for this particular functionality instead of everything that have accumulated in this namespace, maybe even the kitchen sink. We can also create several using-directive namespaces for different parts of the library. For example, the std
namespace could have been partitioned like this into std::containers
, std::iostream
, etc.
What do you think? Anything obvious (or not so obvious) that I missed?
February 21st, 2011 at 10:30 am
Perl has really thought this out too. You set up a library/module to export certain names by default, but then you could also combine groups or individual ones. http://perldoc.perl.org/Exporter.html
February 22nd, 2011 at 12:48 pm
That is a clever use of the namespace features. I am definitely going to give this a try to find out for myself how and when to use it. Thank you for sharing it.