Document Number: X3J16/94-0173 WG21/N0560 Date: Sep. 26, 1994 Project: Programming Language C++ Reply to: Tom Pennello tom@metaware.com Transitive using directives can't synthesize new libraries out of old ===================================================================== There may be some misperception that a using directive can help in the task of synthesizing a new library (namespace) that is the union of old libraries. I conclude this from two examples. The first example is from Bjarne's 93-055, page 23, where a facility "h1" is described as containing also two other facilities. // h1.h: #include "h11.h" #include "h12.h" namespace h1 { // ... } In a client, "using namespace h1" would not suffice to get all the facilities; you'd have to write #include "h1.h" using namespace h1; using namespace h11; using namespace h12; The transitive using directive is used to ameliorate the problem: // h1.h: #include "h11.h" #include "h12.h" namespace h1 { using namespace h11; using namespace h12; // ... } and now the client can write #include "h1.h" using namespace h1; This does allow easy unqualified access to all of the facilities in h1, even though h1 is implemented as a "union" of h11 and h12 along with possible additional other names. The problem is that the notion that h1 acts like a namespace containing h11 + h12 + other stuff is a fiction. There is no identifier X for which h1::X will give you anything in h11 OR h12. The :: operator selects members of h1, NOT names of h11 and h12 that have been made visible by using directives. This is from 7.3.1.1 in the current WP: 3 ... The search for a name after a :: locates only names members [sic] of a namespace or class. In particular, using-directives (7.3.4) are ignored, as is any enclosing declarative region. So if the client prefers not to drag in all of h1, but instead wants to selectively access name X, he must know whether X is in h11 or h12. If it's in h11, and if h11 is implemented using h11a and h11b, the client must know whether X is in h11a or h11b: #include "h1.h" ... h11a::X(3) ... The client has to know the entire library structure to use qualified name access. No internal structure can be hidden. The second example I'd like to cite is from the WP, 7.3.4/3 discussing the transitivity of the using directive: namespace M { int i;} namespace N { int i; using namespace M; } void f() { N::i = 7; // error: ambiguous: M::i or N::i? } Skaller has pointed out in core 4740 that the example is wrong. N::i refers to N::i, not M::i. The using directive in N does not make M::i a member of N. I can't speak for the intent, but perhaps the author of the example thought that using directives could be used to merge namespaces. They can't. Essentially the using directive cannot synthesize a new library out of the union of old libraries. It only half-does the job -- it works only if a client desires no other access than through using directives. Qualified access does not work. If that is something the committee thinks is desirable, changes must be made. I'll proceed now to discuss possible changes. What you really want is to import all the names from h11 and h12 as declarations in h11_and_h12: namespace h1 { using h11::*; using h12::*; }; The * is new syntax; "using h11:*" is defined to be the same as using h11::X1; using h11::X2; ... using h11::Xn; for all names X1...Xn in namespace h11. It's shorthand, so you don't have to enumerate all the contents of h11, and which will get you all of h11 even when h11 is revised later. Now one can still say "using namespace h1" and get access to names in h11 and h12, since using directives bring in names that were declared with using declarations. Furthermore, h1::X will work even if X is declared in h11 OR h12, and if X is a function, is will denote the union of the functions named X in h11 AND h12. If you don't like the particular syntax of *, choose something else such as "...". I use "*" as in the Kleene star of regular expressions, but perhaps it looks uncomfortably like pointer-to-member. Another syntactic possibility, suggested by Andy Koenig in an email, is namespace h1 = h11+h12; namespace h1 { // Add more stuff if you need to. // ... } No matter what the syntax, the constraints and semantics should be described in terms of a sequence of using declarations, so that we resort only to previously defined concepts. I.e., Andy's namespace A = N1 + ... + Nn; is described exactly as namespace A { using N1::X for all names X in N1 using N2::X for all names X in N2 ... using Nn::X for all names X in Nn } with the possible constraint that A not yet be declared. But the first syntax, "using N::*", is more flexible and can appear in the middle of a namespace: namespace A { ... using B::*; ... using C::*; ... } and so I'm partial to it (and have in fact implemented it in a shipping version of the MetaWare High C++ compiler). One place we might use this is as follows: namespace cstring { extern "C" { extern void * memcpy(void *, const void *, size_t); // ... } } namespace cmath { extern "C" { extern double sin(double __x); // ... } } // ... namespace entire_c_library { using cstring::*; using cmath::*; } Here we can put the various C library functions in separate namespaces, yet have a single namespace containing the entire C library, should a client not care to know in which sub-namespace a C function resides. Both cstring::memcpy and entire_c_library::memcpy are valid; furthermore, "using namespace entire_c_library" gives you direct access to all the names in all the individual namespaces. Given "using N::*", using directives and their transitivity are unnecessary to build up a namespace from other namespaces. I suspect transitivity was added to that end, but as we have shown it doesn't work. I therefore suggest that we discard the transitivity of using directives in favor of using the more general mechanism of "using N::*" (or whatever syntactic embodiment is found for it). Anther reason I dislike transitivity of using directives is that I view "using namespace" for the convenient use of names, not for declaring them; I'll employ the using declaration if I want to declare them. For example: namespace shorthands { typedef int I; typedef unsigned U; } namespace Y { using namespace shorthands; // For my convenience. void f(I,U); void g(U); } using namespace Y; // Code here should NOT SEE I and U. Given the current language definition, using namespace Y gets also the names in namespace shorthands, just because Y wanted convenient use of shorthands to write its definitions. There is no way to prevent this except by placing using directives inside scopes to restrict their effect. This works for function bodies, but not for declarations, and so there is no workaround in the above example. Unless we make using directives non-transitive, namespace providers will be discouraged from make use of such directives except within fuction bodies, lest their clients get unintended access to names. Non-transitive using directives is in fact how the MetaWare High C++ compiler implements them, because in the first formal specification I saw (Bjarne's 93-0105), they were not transitive. Summary ------- I recommend the following: - using directives should not be transitive - introduce shorthand syntax such as "using N::*" to be the equivalent of a using declaration for all names in N. The result is that we can fully re-package names from "old" namespaces.