Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++ Namespaces


AUG94: C++ Namespaces

Controlling global-namespace pollution

Tom is vice president of engineering at MetaWare and can be contacted at 2161 Delaware Ave., Santa Cruz, CA 95060.


Most programmers understand the global namespace problem that C++ inherited from C. Global names--those of functions, variables, types, and enumerators--declared in one third-party library can clash with names found in another third-party library or in the programmer's own application. For example, a graphics library from Vendor A and a math library from Vendor B can both have a method called Curve::rotate(int degrees). If you run into this situation, your only recourse is to change the name of the class Curve in one of the libraries. Sometimes this change is difficult, if not impossible, to implement.

To address such problems, C++ includes namespaces, which make it possible to encapsulate library names in a library namespace. In general, a namespace is a named declarative region, and its name is used to access the names declared within that region. Thus, Vendor A's rotate function can be known as A_graphics::Curve::rotate, and Vendor B's as B_math::Curve::rotate. The external (mangled) names given to the linker for such encapsulated library names include the namespace name, so that they are distinct to the linker. Even if you are using a library that predates namespaces, you can use namespaces to encapsulate names in your own code that clash with those of the library.

Namespaces are a recent addition to C++, accepted in July 1993 at the Munich meeting of X3J16. They were preceded by many experiments in programming-language design, such as the packages of Ada (1980), which were also incorporated in MetaWare's Pascal (1984). C++'s namespaces offer new twists not present in these two languages, such as the distinction between a using declaration and a using directive.

Names are introduced in a namespace via the namespace definition in Example 1(a). A namespace definition is syntactically a declaration, but it may only appear at the global scope or within another namespace definition. All names declared directly within a namespace definition, except those introduced by a using declaration, are "owned" by the namespace.

If the namespace identifier is not declared in the current scope, it is declared as a namespace. If it has already been declared, the definition "continues" the namespace. In this way, a namespace can be extended to allow a single large namespace to be defined over different header files; see Example 1 (b).

A library vendor can choose a very long name for a namespace to minimize potential clashes with other vendors; see Example 1(c). However, it may be unwieldy to use such a long name to refer to names in the namespace. A namespace-aliasing definition allows you to use a shorthand name like that in Example 1(d), where DSOML is declared as a namespace that stands for the longer name in the scope of the alias definition.

Using the Names: Explicit Qualification

The names declared in a namespace can be denoted explicitly using the existing qualified name syntax for classes. In Example 2(a), for instance, Colors::invert denotes a member invert of namespace Colors. A name declared in a namespace can be defined outside the namespace, if not already defined within it, using the qualified name to denote the member being defined; see Example 2(b). Just as with classes, the text following the qualification is considered in the scope of the namespace. So, although we say Colors::color for the return type, namespace members color (the parameter type) and red (the initializer) can appear unqualified.

The using Declaration

A using declaration declares a name N from a namespace as a current declaration in the scope in which the using declaration appears. The declaration of N is an alias of its declaration in the namespace. In Example 3(a), for example, a using declaration uses a qualified name to refer to the used name. (The current X3J16 proposal does not allow the comma-separated list; MetaWare High C++ does.)

Just as with normal declarations, a using declaration can introduce a function that overloads other functions in the same scope. And just as with normal declarations, a using declaration can cause a duplicate declaration if a nonfunction of the same name has already been declared; see Example 3(b).

Unlike normal declarations, using declarations can overload functions with the same argument types. The ambiguity is detected at the point of use, rather than the point of declaration. Suppose Other_namespace::invert had the same signature as Colors::invert; then for //1 invert(C), an error message would result.

If ambiguity exists between two functions with identical argument types, where one function was introduced by a using declaration and the other by a non-using declaration, the non-using declaration is preferred over the using declaration. In Example 3(c), //2 invert(C) calls the extern int function rather than the function introduced by the using declaration. No diagnostic is produced; this silent choice could easily be a point of disagreement among language designers.

A using declaration may appear in the same places as any other declaration. A using declaration within a namespace N makes the name declared in N; see Example 3(d). Hence, N::name can refer to the using-declared name, as Example 3(e) illustrates. The only difference is that the imported name is still "owned" by its original namespace. Thus, being using-declared doesn't affect its external mangled name. You can now say TV::color or using TV::color, even though color is owned by namespace Colors.

Partly because using declarations allow a name to be declared in multiple namespaces, and partly because using declarations are just aliases, different using declarations may import the same declaration. Such an otherwise-invalid duplicate declaration is allowed and has the same effect as if the duplicate were not there; see Example 3(e). This is more useful with functions, where you may use a namespace solely for constructing a collection of functions, as in Example 3(f). But, as Example 3(g) shows, you may also now import both collections without fear of duplication. A using declaration does not allow you to select a single, individual function; see Example 3(h). (High C++ also supports using N::*, which effectively performs a using declaration on every name declared in N.)

The using Directive

A using directive is quite different from a using declaration. The directive imports an entire namespace at once, but not as declarations in the scope containing the directive. Instead, within the scope containing the directive, the names are treated as if they were declared in the smallest enclosing non-namespace scope. (This is the global scope for namespaces declared globally or within global namespaces.) Furthermore, apparent duplicate declarations do not occur until a name's point of use.

As Example 4(a) illustrates, a using directive begins with using namespace followed by the (potentially qualified) name denoting the namespace. The names are treated as though they were declared globally, so local declarations of the same name, like those in Example 4(b), hide the global declarations. The local declaration //1 hides the global; //2 invokes //1Other_namespace::invert. However, in Example 4(c) both namespaces' members are introduced globally, so in //3 there is the choice of two globally declared invert functions.

As with using declarations, ambiguity between two functions with identical argument types is resolved in favor of a function that was not introduced by a using directive. Similarly, ambiguity that cannot be resolved via overload resolution is reported at the use of a name, not at the occurrence of the using directive.

Names introduced by using directives are ignored when an expression uses explicit qualification. Thus, even though the names are treated as being declared globally, a reference ::global_name, as in Example 4(d), does not refer to any names introduced by using directives.

According to a preliminary paper on namespaces (93-055), directives may be transitive--if namespace A contains using namespace B in its definition, then using namespace A also gains access to the names in B. In the formal proposal for namespaces (93-105), such an intent is not indicated by the language. The transitivity was intended for "joining" two namespaces together in a single one, but it does not have the intended effect; thus in High C++ directives are not transitive.

Friend and extern Declarations

Friends first declared in a class are declared in the smallest enclosing nonclass, nonfunction-prototype scope. With the addition of namespaces, this means a friend declaration like that in Example 5 can be "injected" into the namespace. The same holds for an extern declaration first declared within a function in a namespace. Rather than being a global extern, the declaration is owned by the namespace.

Unnamed Namespaces

You can omit the identifier in a namespace definition, in which case you are then referring to that "unnamed" namespace that exists for every compilation unit. The unnamed namespace is unique for that compilation unit, and a using directive for it is automatically assumed. The resultant effect is that you can enclose your local data in the unnamed namespace without fear of it clashing with local data in other compilation units. Example 6(a), for instance, is equivalent to having written Example 6(b), where UNIQUE is a compiler-chosen name unique to each compilation unit.

The proposed C++ draft says that names within the unnamed namespace have internal linkage, but this is being debated on the X3J16 core language reflectors, as some think that the internal linkage provision serves no purpose.

Because namespaces are so new, some issues currently under discussion include: statements such as "the scope of a class is a namespace"; the implicit internal linkage of unnamed namespaces; the transitivity of directives; and the possible inclusion of using N::*. But even without decisions on these finer points, MetaWare's engineers have found namespaces extremely useful.

Example 1: (a) Namespace definition; (b) extending a namespace; (c) long names for namespaces can minimize potential clashes; (d) using a shorthand name.

(a) namespace identifier {
        ... list of zero or more declarations
        }

(b) namespace A {           // Declare A as a namespace.
         int f() { ... }
         typedef int T;
         }
         ...
    namespace A {           // Add more to namespace A.
        int g();
        }

(c) namespace Distributed_System_Object_Model_Library {
        ...
        }

(d) namespace DSOML = Distributed_System_Object_Model_Library;

Example 2: (a) Colors::invert denotes a member invert of the namespace Colors; (b) defining a name outside the namespace.

(a) namespace Colors {
       enum color {red,white,blue};
       typedef int color;
       color invert(color);
       extern color c;
       };
    ...
    func() {
       Colors::color C = ...;
       Colors::invert(C);
       }

(b) Colors::color Colors::invert(color c) { ... }
    Colors::color Colors::c = red;

Example 3: (a) A using declaration uses a qualified name to refer to the used name; (b) a duplicate declaration; (c) //2 invert(C) calls the extern int function; (d) an imported name is still "owned" by its original namespace; (e) an allowable duplicate declaration; (f) using a namespace for constructing a collection of functions; (g) importing both collections without duplication; (h) a using declaration does not allow you to select a single, individual function.

(a) func() {
      using Colors::red, Colors::white, Colors::blue;
      Colors::color x = red;
      using Colors::invert;
      x = invert(x);
      }

(b) func() {
      extern int invert(int);
      using Colors::invert;         // invert is now overloaded.
      using Other_namespace::invert;// OK if invert's a function.
      Colors::color C = ...;
      invert(C);                   //1 Selects from 3 inverts.
      typedef int T;
      using Other_namespace::T;     // Error.
      }

(c) extern int invert(Colors::color);
    using Colors::invert;             // invert is now overloaded.
    Colors::color C = ...;
    invert(C);                       //2 Selects from 2 inverts.

(d) namespace TV {
      using Colors::color;
      color pixels[1024*1024];
      }

(e) using Colors::color;
      using TV:color;         // OK; same as Colors::color.

(f) namespace Collection1 {
      using A::f, B::f, C::f;
      };
    namespace Collection2 {
      using       B::f, C::f, D::f;
      };
    ...
    ... Collection1::f(...) ...
    ... Collection2::f(...) ...

(g) using Collection1::f, Collection2::f;
    ... f(...) ...  // Select from A::f, B::f, C::f, and D::f.

(h) using N::f(int);        // NOT ALLOWED.
    using N::f(char);       // NOT ALLOWED.
    // But skip a whole bunch of other f's from N.

Example 4: (a) A using directive begins with using namespace, followed by the (potentially qualified) name denoting the namespace; (b) hiding global declarations; (c) namespaces' members are introduced globally.

(a) func() {
      using namespace Colors;
      color C = red;
      invert(C);
      }

(b) func() {
      using namespace Colors;
      using Other_namespace::invert;  //1 local declaration
      color C = red;
      invert(C);                     //2 gets //1.
      }

(c) func() {
      using namespace Colors;         //1 invert declared globally
      using namespace Other_namespace;//2 another global invert
      color C = red;
      invert(C);                     //3 chooses between 1 and 2
      }

(d) namespace A { int glob; }
    using namespace A;
    int glob;       // OK, now we have two of them globally.
    func() {
      glob++;     // Ambiguous:  A::glob or ::glob.
      ::glob++;   // Refers to global, non-namespace glob.
      A::glob++;  // Refers to namespace A's glob.
      }

Example 5: Injecting a friend declaration into the namespace.

namespace A                   {
    class C                   {
        // This is A::func.
        friend func();
        };
    }
int A::func() { ... }

Example 6: (a) is equivalent to (b).

(a) namespace {
       int a, b, c;
       void f() { ... }
       }

(b) namespace UNIQUE {
      int a, b, c;
      void f() { ... }
      }; using namespace UNIQUE;


Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.