[swap] End-user allowed to add overloads to boost namespace?

Is any end-user allowed to add her own overloads of existing Boost functions to the boost namespace? The boost::swap utility from Joseph Gauterin (recently moved to the trunk) allows the user to customize boost::swap for her class, by providing a swap function for her class within the namespace of the class. Internally boost::swap does the idiomatical "using std::swap; swap(left,right);", to allow finding such a user-provided swap function by argument-dependent lookup (ADL) <svn.boost.org/svn/boost/trunk/boost/utility/swap.hpp> Unfortunately some compilers don't fully supported ADL, as is shown by the mixed test results of swap/test/specialized_in_global.cpp and swap/test/specialized_in_other.cpp, at http://www.boost.org/development/tests/trunk/developer/utility-swap_.html ADL failures appear to occur for GCC 3.3.1 (PathScale), Intel C++ 8.1, MSVC 7.1 (2003), and Borland (up to 5.9.3). Once the boost::swap utility would be used by other Boost libraries, end users might want to add a custom overload of swap for their classes to the boost namespace. Especially when users would have to deal with lacking ADL support of their compiler. Would it be okay for them to do so? Or are there some moral objections, or technical issues, regarding a user-provided overload within the boost namespace? Kind regards, Niels PS Volodya, thanks for fixing swap/Jamfile.v2! -- Niels Dekker http://www.xs4all.nl/~nd/dekkerware Scientific programmer at LKEB, Leiden University Medical Center

AMDG Niels Dekker - mail address until 2008-12-31 wrote:
Once the boost::swap utility would be used by other Boost libraries, end users might want to add a custom overload of swap for their classes to the boost namespace. Especially when users would have to deal with lacking ADL support of their compiler. Would it be okay for them to do so? Or are there some moral objections, or technical issues, regarding a user-provided overload within the boost namespace?
Adding overloads to namespace boost is not guaranteed to work either. template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); } class X {}; namespace boost { void swap(X&, X&); // not found by foo. } In Christ, Steven Watanabe

on Sat Jul 26 2008, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
AMDG
Niels Dekker - mail address until 2008-12-31 wrote:
Once the boost::swap utility would be used by other Boost libraries, end users might want to add a custom overload of swap for their classes to the boost namespace. Especially when users would have to deal with lacking ADL support of their compiler. Would it be okay for them to do so? Or are there some moral objections, or technical issues, regarding a user-provided overload within the boost namespace?
Adding overloads to namespace boost is not guaranteed to work either.
template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); }
class X {};
namespace boost { void swap(X&, X&); // not found by foo. }
Well, not by a conforming compiler. Niels is talking about compilers with bugs, though. I don't know if this would be a possible workaround for any of those compilers or not, but several compilers do all name lookup in phase 2 so this stands a chance of working. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Steven Watanabe wrote:
Adding overloads to namespace boost is not guaranteed to work either.
template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); }
class X {};
namespace boost { void swap(X&, X&); // not found by foo. }
Thanks, Steven! So the user should make sure that all of her custom boost swap overloads are declared, /before/ including any function that actually calls boost::swap... right? class X; // forward declaration. namespace boost { void swap(X&, X&); // forward declaration. } template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); // should find swap(X&, X&), right? } class X {}; David Abrahams wrote:
Niels is talking about compilers with bugs, though. I don't know if this would be a possible workaround for any of those compilers or not, but several compilers do all name lookup in phase 2 so this stands a chance of working.
We could add more unit tests, to see if it would work for those specific compilers. But only if we'd agree that there's no objection against having end-users adding their overloads to the boost namespace... I tend to think that it's "morally right" to have the end-user provide template specializations of boost function templates, but I'm not sure about overloads... what do you think?
http://www.boost.org/development/tests/trunk/developer/utility-swap_.html ADL failures appear to occur for GCC 3.3.1 (PathScale), Intel C++ 8.1, MSVC 7.1 (2003), and Borland (up to 5.9.3).
I just looked into some of these; it doesn't look like you need to do anything for msvc-7.1 and gcc-3.1.1 other than to place your class in a namespace other than the global one.
gcc-3.1.1 can indeed find the custom swap by ADL when the user's class is in her own namespace (other than the global one). As you see, "specialized_in_other" succeeds for gcc-3.1.1. Unfortunately this is not the case for msvc-7.1: msvc-7.1 fails on both "specialized_in_global" and "specialized_in_other". It just doesn't seem to do ADL on template arguments. :-( Thanks for your replies so far, Kind regards, Niels

AMDG Niels Dekker - mail address until 2008-12-31 wrote:
Steven Watanabe wrote:
Adding overloads to namespace boost is not guaranteed to work either.
template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); }
class X {};
namespace boost { void swap(X&, X&); // not found by foo. }
Thanks, Steven! So the user should make sure that all of her custom boost swap overloads are declared, /before/ including any function that actually calls boost::swap... right?
Yes, this should work, but it is hard to guarantee.
I tend to think that it's "morally right" to have the end-user provide template specializations of boost function templates, but I'm not sure about overloads... what do you think?
I don't see a problem in this case.
Unfortunately this is not the case for msvc-7.1: msvc-7.1 fails on both "specialized_in_global" and "specialized_in_other". It just doesn't seem to do ADL on template arguments. :-(
msvc-7.1 is one of the compilers that does all look up in phase 2, I think. So... namespace users_namespace { class X {}; void swap(X&, X&); } #if BOOST_WORKAROUND(BOOST_MSVC, == 1310) namespace boost { using users_namespace::swap; } #endif We have to use a #if to avoid an ODR violation for compilers that implement two-phase name lookup correctly. In Christ, Steven Watanabe

on Sat Jul 26 2008, Niels Dekker - mail address until 2008-12-31 <nd_mail_address_valid_until_2008-12-31-AT-xs4all.nl> wrote:
Steven Watanabe wrote:
Adding overloads to namespace boost is not guaranteed to work either.
template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); }
class X {};
namespace boost { void swap(X&, X&); // not found by foo. }
Thanks, Steven! So the user should make sure that all of her custom boost swap overloads are declared, /before/ including any function that actually calls boost::swap... right?
That is effectively impossible in practice. Someone else who is using boost::swap may #include it earlier.
class X; // forward declaration.
namespace boost { void swap(X&, X&); // forward declaration. }
template<class T> void foo(T& t1, T&t2) { boost::swap(t1, t2); // should find swap(X&, X&), right? }
class X {};
David Abrahams wrote:
Niels is talking about compilers with bugs, though. I don't know if this would be a possible workaround for any of those compilers or not, but several compilers do all name lookup in phase 2 so this stands a chance of working.
We could add more unit tests, to see if it would work for those specific compilers. But only if we'd agree that there's no objection against having end-users adding their overloads to the boost namespace... I tend to think that it's "morally right" to have the end-user provide template specializations of boost function templates, but I'm not sure about overloads... what do you think?
It's probably not a good idea, but as far as I'm concerned, when compilers are broken, you can do all kinds of evil things to compensate without getting sent to Heck.
http://www.boost.org/development/tests/trunk/developer/utility-swap_.html ADL failures appear to occur for GCC 3.3.1 (PathScale), Intel C++ 8.1, MSVC 7.1 (2003), and Borland (up to 5.9.3).
I just looked into some of these; it doesn't look like you need to do anything for msvc-7.1 and gcc-3.1.1 other than to place your class in a namespace other than the global one.
gcc-3.1.1 can indeed find the custom swap by ADL when the user's class is in her own namespace (other than the global one). As you see, "specialized_in_other" succeeds for gcc-3.1.1. Unfortunately this is not the case for msvc-7.1: msvc-7.1 fails on both "specialized_in_global" and "specialized_in_other".
I could have sworn that's not what I saw when I responded to your message, but it appears to be the case now. Also it really surprises me because I know msvc-7.1 does do ADL.
It just doesn't seem to do ADL on template arguments. :-(
I'm sorry, I'm not seeing how that is relevant to any of the failing tests. What am I missing? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

So the user should make sure that all of her custom boost swap overloads are declared, /before/ including any function that actually calls boost::swap... right?
David Abrahams wrote:
That is effectively impossible in practice. Someone else who is using boost::swap may #include it earlier.
MSVC has a compiler option that has the effect of #include-ing a specified header file on the first line of every source file (/FI, "Name Forced Include File"). That header file could contain the declarations of all user provided boost::swap overloads, when using MSVC 7.1... Ok, it wouldn't be particularly elegant!
We could add more unit tests, to see if it would work for those specific compilers. But only if we'd agree that there's no objection against having end-users adding their overloads to the boost namespace...
It's probably not a good idea, but as far as I'm concerned, when compilers are broken, you can do all kinds of evil things to compensate without getting sent to Heck.
Okay, I'll see if I can add such a unit test tomorrow. It would probably be based upon Joseph's "specialized_in_other" test, but having an extra boost::swap overload declared and defined /after/ the #include <boost/test/test_tools.hpp>. Hopefully that would be sufficient.
msvc-7.1 fails on both "specialized_in_global" and "specialized_in_other".
I could have sworn that's not what I saw when I responded to your message, but it appears to be the case now. Also it really surprises me because I know msvc-7.1 does do ADL.
It surprised me as well, I only encountered this ADL bug very recently, while I've been using msvc-7.1 for quite a while.
It just doesn't seem to do ADL on template arguments. :-(
I'm sorry, I'm not seeing how that is relevant to any of the failing tests. What am I missing?
Here's where msvc-7.1 fails to do ADL, in utility/swap.hpp: template<class T> void swap_impl(T& left, T& right) { using std::swap; swap(left,right); // <-- No ADL by msvc-7.1 } While msvc-7.1 would have doing ADL if T would have been a specific type, instead of a template argument. Steven Watanabe wrote:
msvc-7.1 is one of the compilers that does all look up in phase 2, I think. So...
namespace users_namespace { class X {}; void swap(X&, X&); }
#if BOOST_WORKAROUND(BOOST_MSVC, == 1310) namespace boost { using users_namespace::swap; } #endif
We have to use a #if to avoid an ODR violation for compilers that implement two-phase name lookup correctly.
So that would be an alternative workaround, to be used by end-users, right? Instead of having them provide a boost::swap overload. (I guess the #if should include GCC 3.3.1, Intel C++ 8.1, and Borland as well.) Would it be preferable to adding a boost::swap overload? Good night, Niels

David Abrahams wrote:
I could have sworn that's not what I saw when I responded to your message, but it appears to be the case now. Also it really surprises me because I know msvc-7.1 does do ADL.
For the record, I just double-checked. msvc-7.1 doesn't /always/ have ADL failures when template arguments are involved. But still the following C++ program is rejected by msvc-7.1 (because of a link error), as it fails to do ADL in that particular case: //////////////////////////////////////////////// namespace standard_space { template <class T> void swap(T&, T&); template <class T, class U> struct pair; template <class T, class U> void swap(pair<T, U>&, pair<T, U>&); } namespace foo_space { struct foo {}; void swap(foo&, foo&) {}; } template <class T> void temp_fun() { T obj1, obj2; using standard_space::swap; swap(obj1, obj2); // msvc-7.1 fails to do ADL! } int main() { temp_fun<foo_space::foo>(); return 0; } //////////////////////////////////////////////// http://www.dinkumware.com/exam/ says (when selecting "VC v7.1/C++"): error LNK2019: unresolved external symbol "void __cdecl standard_space::swap<struct foo_space::foo> [...] referenced in function "void __cdecl temp_fun<struct foo_space::foo>(void)" This error appears to indicate that msvc-7.1 prefers to call standard_space::swap, instead of foo_space::swap, in that particular case. Which is very much like what's happening inside boost/utility/swap.hpp, when doing #include <algorithm> and "using std::swap". Fortunately, msvc-7.1 end-users can work around the issue by adding their own boost::swap overload, as we've discussed last Saturday. The first results of the "specialized_in_boost_and_other" test I added yesterday indicate that msvc-7.1 will select this overload, even when it's declared /after/ doing #include <boost/utility/swap.hpp> http://www.boost.org/development/tests/trunk/developer/utility-swap_.html Kind regards, Niels

on Sat Jul 26 2008, Niels Dekker - mail address until 2008-12-31 <nd_mail_address_valid_until_2008-12-31-AT-xs4all.nl> wrote:
http://www.boost.org/development/tests/trunk/developer/utility-swap_.html ADL failures appear to occur for GCC 3.3.1 (PathScale), Intel C++ 8.1, MSVC 7.1 (2003), and Borland (up to 5.9.3).
I just looked into some of these; it doesn't look like you need to do anything for msvc-7.1 and gcc-3.1.1 other than to place your class in a namespace other than the global one. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Niels Dekker - mail address until 2008-12-31 wrote:
Is any end-user allowed to add her own overloads of existing Boost functions to the boost namespace?
Niels, It seems like users should not be allowed to add to the boost:: namespace. Here is my reasoning: if users are allowed to add to the boost namespace, what happens when/if that library (to which the user is adding code) becomes part of the standard and is moved to std::? Either the rule against adding to std:: would need to be relaxed or user code would break when the library becomes part of std::. The only exception I can see is if it is a work-around for a compiler that isn't standards compliant since the compiler will (presumably) be updated and not need the work-around at the point that the library is added to the std:: namespace. (I'm not sure if this boost::swap issue falls under this exception.) Thanks, David

David Walthall wrote:
It seems like users should not be allowed to add to the boost:: namespace. Here is my reasoning: if users are allowed to add to the boost namespace, what happens when/if that library (to which the user is adding code) becomes part of the standard and is moved to std::? Either the rule against adding to std:: would need to be relaxed or user code would break when the library becomes part of std::.
Good point! Thanks for your feedback, David.
The only exception I can see is if it is a work-around for a compiler that isn't standards compliant since the compiler will (presumably) be updated and not need the work-around at the point that the library is added to the std:: namespace. (I'm not sure if this boost::swap issue falls under this exception.)
Yes, my question was really based upon the fact that for some compilers, the end user might want to add a boost::swap overload, because of missing compiler support for argument-dependent lookup (ADL). Anyway, I still want to see if we can somehow work around those ADL related compiler bugs within the boost::swap utility itself. But before doing so, I would like to see "test_adl_barrier" go green on the regression: http://www.boost.org/development/tests/trunk/developer/utility-swap_.html Which seems like just a matter of time :-) Kind regards, Niels

Yes, we've got MSVC 8 & 9 and GCC 4+ passing everything now. I expect the tests for most of the other toolsets will pass next time they're run. Then it's just a matter of seeing if there's anything that can be done about the remaining compilers that don't implement ADL properly.
Either the rule against adding to std:: would need to be relaxed or user code would break when the library becomes part of std::.
Am I correct in thinking that it's permissible to provide a template specialization of a function in namespace std but not an overload?

Joseph Gauterin wrote:
Am I correct in thinking that it's permissible to provide a template specialization of a function in namespace std but not an overload?
Yes, you are. :-) According to both the current C++ Standard and the latest Working Draft. The Draft www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2691.pdf says at 17.4.3.1.1, Namespace std [namespace.std]: "The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type of external linkage and the specialization meets the standard library requirements for the original template." Kind regards, Niels

Niels Dekker - mail address until 2008-12-31 wrote:
Joseph Gauterin wrote:
Am I correct in thinking that it's permissible to provide a template specialization of a function in namespace std but not an overload?
Yes, you are. :-) According to both the current C++ Standard and the latest Working Draft. The Draft www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2691.pdf says at 17.4.3.1.1, Namespace std [namespace.std]:
"The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type of external linkage and the specialization meets the standard library requirements for the original template."
Speaking of swap, ADL, and the next standard... I remember reading or hearing a long time ago that in C++0x it is an error to call std::swap(x,y) directly and you have to do using std::swap; swap(x,y);. This seems very unfortunate especially since boost can get us ADL swap without that in C++03. Does anyone know if this is still true? Thanks, Michael Marcin

Michael Marcin wrote:
I remember reading or hearing a long time ago that in C++0x it is an error to call std::swap(x,y) directly and you have to do using std::swap; swap(x,y);.
Curiously, last year I worried about getting a C++0x error from doing just that: "using std::swap; swap(x,y);"! Especially when one would use the C++0x std::Swappable concept. Douglas Gregor dedicated a blog entry to the issue: http://conceptgcc.wordpress.com/2007/01/02/revisiting-name-lookup-with-the-s...
This seems very unfortunate especially since boost can get us ADL swap without that in C++03. Does anyone know if this is still true?
I really don't know. I /think/ that the current version of the boost::swap utility should work in C++0x as well, at least in C++0x code that doesn't have concept checking. Of course, boost::swap doesn't yet support an rvalue as argument, like C++0x's std::swap will. But other than that, I think it should work fine. Please let me know if I'm wrong! Good night, Niels
participants (6)
-
David Abrahams
-
David Walthall
-
Joseph Gauterin
-
Michael Marcin
-
Niels Dekker - mail address until 2008-12-31
-
Steven Watanabe