Customization via Overloading in Namespace

Unless I'm just observing a bug in Comeau, I've just discovered something. In http://www.boost-consulting.com/writing/qn.html I note that, as a means of algorithm customization... library writers are unlikely to invite users to overload functions in the library's namespace, since: a. It is syntactically heavy and inconvenient for users to define overloads in other namespaces: // user.hpp namespace user { class my_class; } // * Extra commented lines required to // * produce overloads in lib:: and namespace lib // * return to user::. Moves f() away { // * from my_class. void f(user::my_class) // also explicit qualification of my_class } // * // * namespace user // * { // * b. Qualified lookups of names from function templates are subject to dangerous order dependencies, since qualified names bind at the point of definition. // lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); // binds only to visible fs } } // user.cpp #include "lib.hpp" #include "user.hpp" // defines f overload too late int main() { user::my_class x; lib::g(x); // calls lib::f(), not user::f()! } It seems that's true for qualified names, but when ADL is disabled with parentheses, names aren't bound until the point of instantiation: // lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { (f)(x); // not bound here } } // user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload int main() { user::my_class x; lib::g(x); // calls user::f() } This hardly removes all the problems with namespaces and ADL, but maybe it restores "overloading in the library's namespace" to viability as a customization technique? -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Thu, Feb 19, 2004 at 04:54:36PM -0500, David Abrahams wrote:
Unless I'm just observing a bug in Comeau, I've just discovered something. ... It seems that's true for qualified names, but when ADL is disabled with parentheses, names aren't bound until the point of instantiation:
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { (f)(x); // not bound here } }
// user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload
int main() { user::my_class x; lib::g(x); // calls user::f() }
The return of name-capture... I dunno enough about the standard to know if this is a bug or not. I am curious what you mean by "when ADL is disabled with parentheses": surely ADL is still needed for lib::g to call user::f? I have seen parens around function names before as a means to something, but can you give me the reminder-summary of what the parens do? In the example, it looks like it is not ADL that is disabled, but rather template hygiene. (Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization! All designs based on overloading or ADL will end in tears!") -- -Brian McNamara (lorgon@cc.gatech.edu)

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 04:54:36PM -0500, David Abrahams wrote:
Unless I'm just observing a bug in Comeau, I've just discovered something. ... It seems that's true for qualified names, but when ADL is disabled with parentheses, names aren't bound until the point of instantiation:
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { (f)(x); // not bound here } }
// user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload
int main() { user::my_class x; lib::g(x); // calls user::f() }
The return of name-capture... I dunno enough about the standard to know if this is a bug or not. I am curious what you mean by "when ADL is disabled with parentheses": surely ADL is still needed for lib::g to call user::f?
Ugh, of course. Sorry, I should've said: lib::g(x); // calls lib::f(user::my_class)
I have seen parens around function names before as a means to something, but can you give me the reminder-summary of what the parens do?
They disable ADL.
In the example, it looks like it is not ADL that is disabled, but rather template hygiene.
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
All designs based on overloading or ADL will end in tears!")
You're preaching to the choir. Read the paper I referenced if you want to know more. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Thu, Feb 19, 2004 at 11:35:13PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 04:54:36PM -0500, David Abrahams wrote: ...
// user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload
int main() { user::my_class x; lib::g(x); // calls user::f() }
The return of name-capture... I dunno enough about the standard to know if this is a bug or not. I am curious what you mean by "when ADL is disabled with parentheses": surely ADL is still needed for lib::g to call user::f?
Ugh, of course. Sorry, I should've said:
lib::g(x); // calls lib::f(user::my_class)
And #include "user.hpp" // defines some user::f overload should be #include "user.hpp" // defines some lib::f overload ? Just checking; I think yes.
I have seen parens around function names before as a means to something, but can you give me the reminder-summary of what the parens do?
They disable ADL.
Neat!
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
I'm not clear about this. // lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } } // user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } } // user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); } Would that not print "foo"? What is the order dependency? (I don't think I have access to a compiler that supposedly does this right, or else I'd try it myself.) What are the other problems? (I know one: function templates can't be "partially" specialized, which means you sometimes need to use a class helper, ugh.) (Apologies if this is a topic we've discussed before.) -- -Brian McNamara (lorgon@cc.gatech.edu)

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 11:35:13PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 04:54:36PM -0500, David Abrahams wrote: ...
// user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload
int main() { user::my_class x; lib::g(x); // calls user::f() }
The return of name-capture... I dunno enough about the standard to know if this is a bug or not. I am curious what you mean by "when ADL is disabled with parentheses": surely ADL is still needed for lib::g to call user::f?
Ugh, of course. Sorry, I should've said:
lib::g(x); // calls lib::f(user::my_class)
And #include "user.hpp" // defines some user::f overload should be #include "user.hpp" // defines some lib::f overload ? Just checking; I think yes.
Yes. Sorry again.
I have seen parens around function names before as a means to something, but can you give me the reminder-summary of what the parens do?
They disable ADL.
Neat!
Well, ugly IMO. I can understand why it'd appeal to someone who usually invokes f this way, though: f x y z ;-)
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
I'm not clear about this.
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } }
// user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } Not an overload.
}
// user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); }
Would that not print "foo"? What is the order dependency?
That's a specialization. Actually, there'd still be an order dependency if g wasn't a function template, but that's beside the point. // lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } } // user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { void f( user::MyClass ) { /* print "foo" */ } // AN OVERLOAD } // user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); // link error; no body for f<T> where T = user::MyClass. }
(I don't think I have access to a compiler that supposedly does this right, or else I'd try it myself.) What are the other problems? (I know one: function templates can't be "partially" specialized, which means you sometimes need to use a class helper, ugh.)
specialization interacts badly with overloading too: template <class T> int f(T); template <> int f(int*); template <class T> void f(T*); int x = 0; int y = f(&x); // error; can't convert void to int.
(Apologies if this is a topic we've discussed before.)
See LWG 225,226,229, and the qn paper I referenced earlier. -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Fri, Feb 20, 2004 at 12:25:49PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 11:35:13PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
I'm not clear about this.
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } }
// user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } Not an overload.
Yes, I know. Chicken Little says Qualified calls and _template_specialization_ are the one true path to such customization! [emphasis added]
}
// user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); }
Would that not print "foo"? What is the order dependency?
That's a specialization. Actually, there'd still be an order dependency if g wasn't a function template, but that's beside the point.
I'm not sure what your second sentence there is trying to tell me; can you give a short example? (If g isn't a function template, what would it be?)
(I don't think I have access to a compiler that supposedly does this right, or else I'd try it myself.) What are the other problems? (I know one: function templates can't be "partially" specialized, which means you sometimes need to use a class helper, ugh.)
specialization interacts badly with overloading too:
template <class T> int f(T); template <> int f(int*); template <class T> void f(T*);
int x = 0; int y = f(&x); // error; can't convert void to int.
Indeed; recall that part of Chicken Little's thesis is All designs based on overloading or ADL will end in tears! the relevant part here being "Overloading bad!" (in the context of library/user customization designs). -- -Brian McNamara (lorgon@cc.gatech.edu)

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Fri, Feb 20, 2004 at 12:25:49PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 11:35:13PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
I'm not clear about this.
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } }
// user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } Not an overload.
Yes, I know. Chicken Little says
Qualified calls and _template_specialization_ are the one true path to such customization! [emphasis added]
But he's wrong. As I've been trying to say, qualified calls in a function template are bound at the point of definition, so if a better overload comes later it isn't considered. That's why you should use parens.
}
// user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); }
Would that not print "foo"? What is the order dependency?
That's a specialization. Actually, there'd still be an order dependency if g wasn't a function template, but that's beside the point.
I'm not sure what your second sentence there is trying to tell me; can you give a short example?
Simple: specializations that follow the point of instantiation aren't considered. This program exits with an error: template <class T> int f(T) { return 1; } int main() { return ::f(0); } template <> int f(int) { return 0; }
(If g isn't a function template, what would it be?)
A regular function.
(I don't think I have access to a compiler that supposedly does this right, or else I'd try it myself.) What are the other problems? (I know one: function templates can't be "partially" specialized, which means you sometimes need to use a class helper, ugh.)
specialization interacts badly with overloading too:
template <class T> int f(T); template <> int f(int*); template <class T> void f(T*);
int x = 0; int y = f(&x); // error; can't convert void to int.
Indeed; recall that part of Chicken Little's thesis is
All designs based on overloading or ADL will end in tears!
There's no ADL here.
the relevant part here being "Overloading bad!" (in the context of library/user customization designs).
I understand this purist's POV, but then, "Specialization Bad", too, for similar but even worse reasons: template <class T> struct fu { int bar(); }; template <> struct fu<int> { typedef whiskey bar; }; -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Fri, Feb 20, 2004 at 04:03:57PM -0500, David Abrahams wrote:
Simple: specializations that follow the point of instantiation aren't considered. This program exits with an error:
template <class T> int f(T) { return 1; }
int main() { return ::f(0); }
template <> int f(int) { return 0; }
Aha; this is part of what I was missing. This clears most of it up for me. One last question, and then I think I'm done. In my example: namespace lib { template <class T> void f(T) { /* print "bar" */ } template <class T> void g(T x) { lib::f(x); } // (1) } namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } } int main() { user::MyClass m; lib::g(m); // (2) } What is printed? (I think this question comes down to whether or not (1) or (2) is the "point of instantiation" of f(), yes?) -- -Brian McNamara (lorgon@cc.gatech.edu)

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Fri, Feb 20, 2004 at 04:03:57PM -0500, David Abrahams wrote:
Simple: specializations that follow the point of instantiation aren't considered. This program exits with an error:
template <class T> int f(T) { return 1; }
int main() { return ::f(0); }
template <> int f(int) { return 0; }
Aha; this is part of what I was missing. This clears most of it up for me.
One last question, and then I think I'm done. In my example:
namespace lib { template <class T> void f(T) { /* print "bar" */ } template <class T> void g(T x) { lib::f(x); } // (1) }
namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } }
int main() { user::MyClass m; lib::g(m); // (2) }
What is printed?
foo
(I think this question comes down to whether or not (1) or (2) is the "point of instantiation" of f(), yes?)
Yeah; if you change g so it callse lib::f(0) it prints bar. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Simple: specializations that follow the point of instantiation aren't considered. This program exits with an error:
template <class T> int f(T) { return 1; }
int main() { return ::f(0); }
template <> int f(int) { return 0; }
It's ill-formed, actually.

"Peter Dimov" <pdimov@mmltd.net> writes:
David Abrahams wrote:
Simple: specializations that follow the point of instantiation aren't considered. This program exits with an error:
template <class T> int f(T) { return 1; }
int main() { return ::f(0); }
template <> int f(int) { return 0; }
It's ill-formed, actually.
Er, Right. An order dependency, but less dangerous than the one you get with overloading. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 11:35:13PM -0500, David Abrahams wrote:
Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 04:54:36PM -0500, David Abrahams wrote: ...
// user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload
int main() { user::my_class x; lib::g(x); // calls user::f() }
The return of name-capture... I dunno enough about the standard to know if this is a bug or not. I am curious what you mean by "when ADL is disabled with parentheses": surely ADL is still needed for lib::g to call user::f?
Ugh, of course. Sorry, I should've said:
lib::g(x); // calls lib::f(user::my_class)
And #include "user.hpp" // defines some user::f overload should be #include "user.hpp" // defines some lib::f overload ? Just checking; I think yes.
Yes. Sorry again.
I have seen parens around function names before as a means to something, but can you give me the reminder-summary of what the parens do?
They disable ADL.
Neat!
Well, ugly IMO. I can understand why it'd appeal to someone who usually invokes f this way, though: f x y z ;-)
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
I'm not clear about this.
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } }
// user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { template <> void f( user::MyClass ) { /* print "foo" */ } Not an overload.
}
// user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); }
Would that not print "foo"? What is the order dependency?
That's a specialization. Actually, there'd still be an order dependency if g wasn't a function template, but that's beside the point. // lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { lib::f(x); } } // user.hpp #include "lib.hpp" namespace user { struct MyClass {}; } namespace lib { void f( user::MyClass ) { /* print "foo" */ } // AN OVERLOAD } // user.cpp #include "user.hpp" int main() { user::MyClass m; lib::g(m); // link error; no body for f<T> where T = user::MyClass. }
(I don't think I have access to a compiler that supposedly does this right, or else I'd try it myself.) What are the other problems? (I know one: function templates can't be "partially" specialized, which means you sometimes need to use a class helper, ugh.)
specialization interacts badly with overloading too: template <class T> int f(T); template <> int f(int*); template <class T> void f(T*); int x = 0; int y = f(&x); // error; can't convert void to int.
(Apologies if this is a topic we've discussed before.)
See LWG 225,226,229, and the qn paper I referenced earlier. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Brian McNamara <lorgon@cc.gatech.edu> writes:
On Thu, Feb 19, 2004 at 04:54:36PM -0500, David Abrahams wrote:
Unless I'm just observing a bug in Comeau, I've just discovered something. ... It seems that's true for qualified names, but when ADL is disabled with parentheses, names aren't bound until the point of instantiation:
// lib.hpp namespace lib { template <class T> void f(T); template <class T> void g(T x) { (f)(x); // not bound here } }
// user.cpp #include "lib.hpp" #include "user.hpp" // defines some user::f overload
int main() { user::my_class x; lib::g(x); // calls user::f() }
The return of name-capture... I dunno enough about the standard to know if this is a bug or not. I am curious what you mean by "when ADL is disabled with parentheses": surely ADL is still needed for lib::g to call user::f?
Ugh, of course. Sorry, I should've said: lib::g(x); // calls lib::f(user::my_class)
I have seen parens around function names before as a means to something, but can you give me the reminder-summary of what the parens do?
They disable ADL.
In the example, it looks like it is not ADL that is disabled, but rather template hygiene.
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization!
You'd be wrong. It'd be nice if it were true, but qualified calls are subject to order dependencies, among other problems.
All designs based on overloading or ADL will end in tears!")
You're preaching to the choir. Read the paper I referenced if you want to know more. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040220031222.GA408@lennon.cc.gatech.edu...
(Despite the fact that no one wants to hear it, I would feel remiss if I didn't play Chicken Little and say "Fool's gold! Qualified calls and template specialization are the one true path to such customization! All designs based on overloading or ADL will end in tears!")
Its an interesting discussion. The standard committee has finally decided to rely on ADL in the standard to ensure the users swap() etc. gets called. I rely on ADL in two of the libs I have in the review queue. br Thorsten
participants (4)
-
Brian McNamara
-
David Abrahams
-
Peter Dimov
-
Thorsten Ottosen