[range] How to extend Boost.Range?

While investigating test failures with Boost.Foreach, I found this in Boost.Range's documentation for extending the Range library: http://boost.org/libs/range/doc/boost_range.html#minimal_interface This seems to suggest that the only way to extend Boost.Range is for user-defined types to implement a std-container-like interface, with nested iterator and const_iterator types, and begin() and end() member functions. Is this really the intention? I seem to recall some discussion about a more accomodating extensibility mechanism that makes allowances for non-std-container-like user-defined range types. In fact, Boost.Foreach assumes such an interface, with disastrous results. What became of Boost.Range's extensibility mechanism? Is it documented anywhere, and I'm just not seeing it? -- Eric Niebler Boost Consulting www.boost-consulting.com

Hi Eric, On Wed, Aug 31, 2005 at 03:46:32PM -0700, Eric Niebler wrote:
While investigating test failures with Boost.Foreach, I found this in Boost.Range's documentation for extending the Range library:
http://boost.org/libs/range/doc/boost_range.html#minimal_interface
This seems to suggest that the only way to extend Boost.Range is for user-defined types to implement a std-container-like interface, with nested iterator and const_iterator types, and begin() and end() member functions. Is this really the intention? I seem to recall some discussion about a more accomodating extensibility mechanism that makes allowances for non-std-container-like user-defined range types. In fact, Boost.Foreach assumes such an interface, with disastrous results. What became of Boost.Range's extensibility mechanism? Is it documented anywhere, and I'm just not seeing it?
It might a problem with documentation, but it seems that you have misunderstood the basic idea of the Range library. Core of the range library is not in the code, rather in concepts that are defined there. http://boost.org/libs/range/doc/range.html These concepts define a uniform way for accessing range-like structures. They are wholy defined in terms of external (free-standing) functions like begin(r), end(r) and etc. and specializations of metafuctions like boost::range_value<>. Therefor if a structure is to be considered to be a range, it has to provide this interface. In addition to this, library implements this interface for some common range types available in the C++ and the standard library. The page you have refered to actualy describes this implementation. To sumarize, if you want to provide a support for a user-defined class, you have two options. - Either you provide external range accessors. In other words, you make your class to be a model of Range Concept. - Or you make it look like std-container and than you actualy use the accessors provided in the range library. Best regards, Pavol.

Pavol Droba wrote:
It might a problem with documentation, but it seems that you have misunderstood the basic idea of the Range library.
There is a section in the documentation entitled "Extending the Library" that doesn't describe how to extend the library. I'd say that's a pretty serious problem in the documentation.
Core of the range library is not in the code, rather in concepts that are defined there.
In that case, the Range library and the concepts it defines are fundamentally broken. It seems to be requiring that everybody at all times make qualified calls to boost::begin() and boost::end(). Therefore, in order to extend the library, users must put *their* begin() and end() functions in the boost namespace. This is distasteful, but I'll let that slide. The more serious problem is the way this interacts with 2-phase lookup. Since it is a qualified call, any call to boost::begin() from within a template will get resolved during the first lookup phase. If a user's overload has not been seen yet, it will not get considered. So, is Boost.Range broken, or is this another problem with the documentation? -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
Pavol Droba wrote:
Core of the range library is not in the code, rather in concepts that are defined there.
In that case, the Range library and the concepts it defines are fundamentally broken. It seems to be requiring that everybody at all times make qualified calls to boost::begin() and boost::end(). Therefore, in order to extend the library, users must put *their* begin() and end() functions in the boost namespace. This is distasteful, but I'll let that slide.
Don't. It's worse than distasteful: it doesn't work.
The more serious problem is the way this interacts with 2-phase lookup.
Right, that's why it doesn't work. 2-phase lookup is in the language, and anything that doesn't cooperate is broken.
Since it is a qualified call, any call to boost::begin() from within a template will get resolved during the first lookup phase. If a user's overload has not been seen yet, it will not get considered.
So, is Boost.Range broken, or is this another problem with the documentation?
If Boost.Range is about concepts, those amount to the same thing. -- Dave Abrahams Boost Consulting www.boost-consulting.com

In that case, the Range library and the concepts it defines are fundamentally broken. It seems to be requiring that everybody at all times make qualified calls to boost::begin() and boost::end(). Therefore, in order to extend the library, users must put *their* begin() and end() functions in the boost namespace. This is distasteful, but I'll let that slide.
Don't. It's worse than distasteful: it doesn't work.
This is just a documentation problem right? The library uses the "using boost::end()" trick and then makes unqualified calls. Joel Eidsath

On Thu, Sep 01, 2005 at 06:09:00AM -0700, Eric Niebler wrote:
Pavol Droba wrote:
It might a problem with documentation, but it seems that you have misunderstood the basic idea of the Range library.
There is a section in the documentation entitled "Extending the Library" that doesn't describe how to extend the library. I'd say that's a pretty serious problem in the documentation.
Core of the range library is not in the code, rather in concepts that are defined there.
In that case, the Range library and the concepts it defines are fundamentally broken. It seems to be requiring that everybody at all times make qualified calls to boost::begin() and boost::end(). Therefore, in order to extend the library, users must put *their* begin() and end() functions in the boost namespace. This is distasteful, but I'll let that slide.
The more serious problem is the way this interacts with 2-phase lookup. Since it is a qualified call, any call to boost::begin() from within a template will get resolved during the first lookup phase. If a user's overload has not been seen yet, it will not get considered.
So, is Boost.Range broken, or is this another problem with the documentation?
Documentation says, that boost::begin() implementation calls unqualified boost_range_begin(). Therefore this is the function, one needs to implement to make support for his/her class. http://www.boost.org/libs/range/doc/boost_range.html#Semantics It seems that the documentation realy needs some improvements, since it has also confused myself. Regards, Pavol

Pavol Droba wrote:
On Thu, Sep 01, 2005 at 06:09:00AM -0700, Eric Niebler wrote:
So, is Boost.Range broken, or is this another problem with the documentation?
Documentation says, that boost::begin() implementation calls unqualified boost_range_begin(). Therefore this is the function, one needs to implement to make support for his/her class.
http://www.boost.org/libs/range/doc/boost_range.html#Semantics
Um, where in the documentation does it say that? I followed the link, but I see no mention of boost_range_begin().
It seems that the documentation realy needs some improvements, since it has also confused myself.
<nod style=emphatic/> -- Eric Niebler Boost Consulting www.boost-consulting.com

On Thu, Sep 01, 2005 at 11:09:53AM -0700, Eric Niebler wrote:
Pavol Droba wrote:
On Thu, Sep 01, 2005 at 06:09:00AM -0700, Eric Niebler wrote:
So, is Boost.Range broken, or is this another problem with the documentation?
Documentation says, that boost::begin() implementation calls unqualified boost_range_begin(). Therefore this is the function, one needs to implement to make support for his/her class.
http://www.boost.org/libs/range/doc/boost_range.html#Semantics
Um, where in the documentation does it say that? I followed the link, but I see no mention of boost_range_begin().
Oops. This is realy bad. I was actualy digging in the implementation. Then I was tring to find it in the documentation. And it seems, that I have not read it properly. So all I said about boost_range_begin() is valid. But the documentation is perfectly misleading. Regards, Pavol

Pavol Droba <droba@topmail.sk> writes:
Oops. This is realy bad. I was actualy digging in the implementation. Then I was tring to find it in the documentation.
And it seems, that I have not read it properly.
So all I said about boost_range_begin() is valid. But the documentation is perfectly misleading.
Or maybe the documentation is right and the library implementation is wrong? Also, try the same exercise for a "broken compiler." Follow the directions. Does it work? Now how do you write a range that's portable to both broken and conforming compilers without substantial #ifdef-ing? -- Dave Abrahams Boost Consulting www.boost-consulting.com

On Thu, Sep 01, 2005 at 06:04:46PM -0400, David Abrahams wrote:
Pavol Droba <droba@topmail.sk> writes:
Oops. This is realy bad. I was actualy digging in the implementation. Then I was tring to find it in the documentation.
And it seems, that I have not read it properly.
So all I said about boost_range_begin() is valid. But the documentation is perfectly misleading.
Or maybe the documentation is right and the library implementation is wrong?
Or maybe both to some extend.
Also, try the same exercise for a "broken compiler." Follow the directions. Does it work?
Now how do you write a range that's portable to both broken and conforming compilers without substantial #ifdef-ing?
No I haven't tried it. If it is as you says, than it is more broken than I thought. Regards, Pavol

Pavol Droba <droba@topmail.sk> writes:
Core of the range library is not in the code, rather in concepts that are defined there.
I'm really surprised to see this posted. As I've complained to Thorsten on multiple occasions, the concepts are completely broken and mis-documented. The evidence is plain: if you follow the directions for building a conforming range, it doesn't work. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
While investigating test failures with Boost.Foreach, I found this in Boost.Range's documentation for extending the Range library:
http://boost.org/libs/range/doc/boost_range.html#minimal_interface
This seems to suggest that the only way to extend Boost.Range is for user-defined types to implement a std-container-like interface, with nested iterator and const_iterator types, and begin() and end() member functions. Is this really the intention?
no
I seem to recall some discussion about a more accomodating extensibility mechanism that makes allowances for non-std-container-like user-defined range types. In fact, Boost.Foreach assumes such an interface, with disastrous results. What became of Boost.Range's extensibility mechanism?
it's still there, just not very much documented.
Is it documented anywhere, and I'm just not seeing it?
I think the best documentation was provided by you in your for-each docs. If you don't mind, I would love to steal that some day :-) In a perfect world, that would have been part of the range docs from the beginning. br Thorsten

Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
Is it documented anywhere, and I'm just not seeing it?
I think the best documentation was provided by you in your for-each docs. If you don't mind, I would love to steal that some day :-)
In a perfect world, that would have been part of the range docs from the beginning.
Don't. As I'm learning from this thread and from the foreach regression tests, the recommendations in the foreach docs for extending Boost.Range are wrong. I'm deeply confused. Please enlighten me as to the proper way to extend your library. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
Is it documented anywhere, and I'm just not seeing it?
I think the best documentation was provided by you in your for-each docs. If you don't mind, I would love to steal that some day
In a perfect world, that would have been part of the range docs from the beginning.
Don't. As I'm learning from this thread and from the foreach regression tests, the recommendations in the foreach docs for extending Boost.Range are wrong. I'm deeply confused. Please enlighten me as to the proper way to extend your library.
way 1: (a) provide member functions - begin() - end() - size() (b) provide member types - iterator - const_iterator - value_type - size_type - difference_type way 2: (lacking from docs) (a) provide free-standing member in namespace of the UDT: - begin() - end() - size() (b) provide specializations for - boost::range_itetator - boost::range_const_iterator - boost::range_value - boost::range_difference - boost::range_size A library might choose not to call boost::begin/end/size unqualified...in which case that library does not support UDTs. Does it make sense? Have I forgotten something? br -Thorsten

Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
Please enlighten me as to the proper way to extend your library.
<snip>
way 2: (lacking from docs)
(a) provide free-standing member in namespace of the UDT: - begin() - end() - size()
(b) provide specializations for
- boost::range_itetator - boost::range_const_iterator - boost::range_value - boost::range_difference - boost::range_size
A library might choose not to call boost::begin/end/size unqualified...in which case that library does not support UDTs.
Does it make sense? Have I forgotten something?
It does *not* make sense, and I think you forgot the long discussion we had on this topic with Dave A. and Peter D. on the boost.users list. (Thread starts here: http://lists.boost.org/boost-users/2005/03/10242.php) See http://lists.boost.org/boost-users/2005/03/10391.php where you wrote: "So this changes the extension protocol to overloading adl_end() from overloading end()." You also seem to be forgetting the Range concepts that you yourself documented here: http://boost.org/libs/range/doc/range.html. In the concepts, the calls to begin()/end() are REQUIRED to be qualified. So I'll ask again, what is the proper way to extend your library? While we're on the topic, what is the point of range_value, range_difference and range_size? Will range_value<Range>::type ever be different than std::iterator_traits<range_iterator<Range>::type>::value_type? If the answer is no, then why do we need range_value? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
Please enlighten me as to the proper way to extend your library.
<snip>
way 2: (lacking from docs)
(a) provide free-standing member in namespace of the UDT: - begin() - end() - size()
(b) provide specializations for
- boost::range_itetator - boost::range_const_iterator - boost::range_value - boost::range_difference - boost::range_size
A library might choose not to call boost::begin/end/size unqualified...in which case that library does not support UDTs.
Does it make sense? Have I forgotten something?
It does *not* make sense,
Ok. What part does not make sense?
and I think you forgot the long discussion we had on this topic with Dave A. and Peter D. on the boost.users list. (Thread starts here: http://lists.boost.org/boost-users/2005/03/10242.php)
See http://lists.boost.org/boost-users/2005/03/10391.php where you wrote:
"So this changes the extension protocol to overloading adl_end() from overloading end()."
I recall some of the discussion, but I haven't read up on it. The adl_end() stuff never got further than the discussion.
You also seem to be forgetting the Range concepts that you yourself documented here: http://boost.org/libs/range/doc/range.html. In the concepts, the calls to begin()/end() are REQUIRED to be qualified.
well, the boost:: qualification was added for 1.33 because Dave wanted that to happen. I belive the problem was that without boost:: qualification, standard containers were not conforming to a range concept. Somebody suggested the requirement using boost:begin; begin(r); instead. Dave rejected it. If that is wrong afterall, then the whole area of specifying concepts for free- standing functions is a mess.
So I'll ask again, what is the proper way to extend your library?
Do you agree that the above change would help?
While we're on the topic, what is the point of range_value, range_difference and range_size? Will range_value<Range>::type ever be different than std::iterator_traits<range_iterator<Range>::type>::value_type? If the answer is no, then why do we need range_value?
as a help, a shorter way of writing it. br Thorsten

Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
and I think you forgot the long discussion we had on this topic with Dave A. and Peter D. on the boost.users list. (Thread starts here: http://lists.boost.org/boost-users/2005/03/10242.php)
See http://lists.boost.org/boost-users/2005/03/10391.php where you wrote:
"So this changes the extension protocol to overloading adl_end() from overloading end()."
I recall some of the discussion, but I haven't read up on it. The adl_end() stuff never got further than the discussion.
Thorsten. You *participated* in the discussion. You cannot plead ignorance. It was this message on 3/8/2005 where Dave A. recommended you use the name boost_range_end(): http://lists.boost.org/boost-users/2005/03/10402.php. It was in this diff from 3/24/2005 that you applied his recommended change to range/end.hpp: http://tinyurl.com/7tzco. Do you still want to argue that this stuff "never got further than the discussion"?
You also seem to be forgetting the Range concepts that you yourself documented here: http://boost.org/libs/range/doc/range.html. In the concepts, the calls to begin()/end() are REQUIRED to be qualified.
well, the boost:: qualification was added for 1.33 because Dave wanted that to happen. I belive the problem was that without boost:: qualification, standard containers were not conforming to a range concept. Somebody suggested the requirement
using boost:begin; begin(r);
instead. Dave rejected it.
If that is wrong afterall, then the whole area of specifying concepts for free- standing functions is a mess.
No. What Dave A. and Peter D. were arguing for was that boost::end() be implemented in terms of a non-qualified call to an ADL hook called boost_range_end(). From the CVS history, that is what was implemented. But that is not what was documented, and it's not what your concept specifications require. It's also not mentioned anywhere in the proposal you made to the C++ standardization committee, AFAICT. Customization points are *crucially* important to the Range concepts, and we spent a long time discussing it. You have ignored that. Boost.Range is a mess, the concepts are wrong, and so is your proposal. So? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
boost_range_end(). From the CVS history, that is what was implemented. But that is not what was documented, and it's not what your concept specifications require.
I have no recollection of this. To give you all a proper answer, I will have to reread the entire discussion. I don't intend to use my weekend for that, but I'll return monday or tuesday with a proper answer.
It's also not mentioned anywhere in the proposal you made to the C++ standardization committee, AFAICT. Customization points are *crucially* important to the Range concepts,
right.
and we spent a long time discussing it. You have ignored that. Boost.Range is a mess, the concepts are wrong, and so is your proposal.
Thanks. The proposal uses the same extension mechanism as the one in boost. Libraries must call the functions unqualified. I see no problem with that. My recollection is that having boost::foo() do ADL was *firmly* rejected, and so that is why I don't see the boost_range_end() being that important. The docs might be wrong right now, but that is of minor importance. Anyway, I'll respond properly later. -Thorsten

Thorsten Ottosen wrote:
Eric Niebler <eric <at> boost-consulting.com> writes:
boost_range_end(). From the CVS history, that is what was implemented. But that is not what was documented, and it's not what your concept specifications require.
I have no recollection of this.
To give you all a proper answer, I will have to reread the entire discussion. I don't intend to use my weekend for that, but I'll return monday or tuesday with a proper answer.
OK, but if we want the Range concepts fixed for 1.33.1 (we do, right?) then we have a bit of time pressure.
It's also not mentioned anywhere in the proposal you made to the C++ standardization committee, AFAICT. Customization points are *crucially* important to the Range concepts,
right.
and we spent a long time discussing it. You have ignored that. Boost.Range is a mess, the concepts are wrong, and so is your proposal.
Thanks.
I seem to have let my frustration get the better of me. Sorry for the strong wording.
The proposal uses the same extension mechanism as the one in boost.
Libraries must call the functions unqualified. I see no problem with that.
Once you reread the thread, you'll recall the problems with that. The whole discussion started because gcc's bizarre interpretation of the name look-up rules meant that unqualified calls to "end" led to ambiguities -- it was colliding with mpl::end.
My recollection is that having boost::foo() do ADL was *firmly* rejected, and so that is why I don't see the boost_range_end() being that important.
Quite the opposite, actually. It was decided that boost::end() *should* make a call to a hook that is found by ADL.
The docs might be wrong right now, but that is of minor importance.
I respectfully disagree about the importance of improperly specified concepts. -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> wrote in message news:4319D632.40905@boost-consulting.com...
Thorsten Ottosen wrote:
To give you all a proper answer, I will have to reread the entire discussion. I don't intend to use my weekend for that, but I'll return monday or tuesday with a proper answer.
OK, but if we want the Range concepts fixed for 1.33.1 (we do, right?) then we have a bit of time pressure.
[still no proper answer...] I must admit I have been a bit stressed lately, and so really needed a week with just 40 hours of work. I have asked Doug for the 1.33.1 timetable...I wasn't paying attention to that discussion. When you say fixing the concept, then I assume don't just mean the documentation? If the change turns out to require refactoring of the implementation, then it might be risky to rush into something. best regards Thorsten

Thorsten Ottosen wrote:
"Eric Niebler" <eric@boost-consulting.com> wrote in message
OK, but if we want the Range concepts fixed for 1.33.1 (we do, right?) then we have a bit of time pressure.
When you say fixing the concept, then I assume don't just mean the documentation? If the change turns out to require refactoring of the implementation, then it might be risky to rush into something.
I'm talking about the documentation. For the point release, I think it's very important that the documentation is an accurate reflection of the code, and that some mention is made of the customization hooks (boost_range_begin() et al.). In addition to cleaning up the concepts specifications, the section on extending boost.range should say something about the ADL customization points. After the point release, I plan on taking a deeper look at the extensibility mechanism to see if it works on the broken compilers that boost supports. Also, I notice there is no Range test for the boost_range_* ADL hooks. That needs to be addressed as well. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
Thorsten Ottosen wrote:
"Eric Niebler" <eric <at> boost-consulting.com> wrote in message
OK, but if we want the Range concepts fixed for 1.33.1 (we do, right?) then we have a bit of time pressure.
When you say fixing the concept, then I assume don't just mean the documentation? If the change turns out to require refactoring of the implementation, then it might be risky to rush into something.
I'm talking about the documentation. For the point release, I think it's very important that the documentation is an accurate reflection of the code,
right. I have been reading up on the old threads and also looked in the code. I had forgotten about the fact, that I already had implemented the ADL hooks. I'm sorry about the confusion my memory (or lack thereof) has brought.
and that some mention is made of the customization hooks (boost_range_begin() et al.). In addition to cleaning up the concepts specifications, the section on extending boost.range should say something about the ADL customization points.
I agree.
After the point release, I plan on taking a deeper look at the extensibility mechanism to see if it works on the broken compilers that boost supports. Also, I notice there is no Range test for the boost_range_* ADL hooks. That needs to be addressed as well.
Also right. I'll work on it for a little time now and then I'll commit something to the RC_XX branch. I hope you will give some feedback on it then. As fot the wg21 proposal, then this is certainly a thorny issue. I can think of these two sources that can be important to the proposal: http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#218 and http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1792.pdf If I understand Sutter's proposal correct, no namespaces of template arguments will be considered. This would make the problem which caused multi_index_container to break in FOREACH. So, for example, namespace boost { namespace mpl { struct end { }; struct foo { }; } template< class T > int end( T t ) { return 0; } } template< class T > struct bar { }; int main() { using boost::end; bar<boost::mpl::foo> f; return end(f); } will now (in Sutter's wording) disregard the namespace boost::mpl::foo. (Of course we cannot rely on this currently, and it is of course not sure we can rely on it for C++0x either). Thanks for straightening this out. best regards Thorsten

"Thorsten Ottosen" <nesotto@cs.aau.dk> wrote in message news:loom.20050905T202714-75@post.gmane.org...
Eric Niebler <eric <at> boost-consulting.com> writes:
and that some mention is made of the customization hooks (boost_range_begin() et al.). In addition to cleaning up the concepts specifications, the section on extending boost.range should say something about the ADL customization points.
I agree.
would the following suffice? As for the concepts, what changes did you have in mind there?
boost supports. Also, I notice there is no Range test for the boost_range_* ADL hooks. That needs to be addressed as well.
Also right.
added. best regards Thorsten

Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
would the following suffice?
hm... no attachments via web-interface. Please see https://www.cs.aau.dk/~nesotto/C++/doc/boost_range.html#minimal_interface instead. br Thorsten

Thorsten Ottosen wrote:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
would the following suffice?
hm... no attachments via web-interface.
Please see
https://www.cs.aau.dk/~nesotto/C++/doc/boost_range.html#minimal_interface
instead.
This looks good. Thanks for taking care of this. I still have a problem with range_value<> and friends. To satisfy the Range concept, users are required to specialize boost::range_value<> in spite of the fact that it will always be equivalent to std::iterator_traits<Range>::value_type. This makes Range harder to extend for no benefit. The Range library can provide range_value<> without requiring users to specialize it. Regarding the concepts, it is correct to say, as you do, that the calls to begin(), end(), et al., must be qualified by boost::. But they also must say that the following is also well formed and has the same meaning: using namespace unspecified-namespace; boost_range_begin(x); I think you should also say somewhere (but not necessarily in the Concepts section) that unspecified-namespace contains the implementations of boost_range_begin(), et al., for the std containers, std::pair, arrays and null-terminated strings. I think that should do it. Does anybody have a better suggestion? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric <at> boost-consulting.com> writes:
This looks good. Thanks for taking care of this. I still have a problem with range_value<> and friends. To satisfy the Range concept, users are required to specialize boost::range_value<> in spite of the fact that it will always be equivalent to std::iterator_traits<Range>::value_type. This makes Range harder to extend for no benefit. The Range library can provide range_value<> without requiring users to specialize it.
right. I have comitted a revision that deduces - range_size - range_difference - range_value based on the iterator types. I don't know how much code this will break, but I hope it won't break much. We'll see when the next regression pop up tomorrow. Btw, my range_size uses this code template< class T > struct add_unsigned; template<> struct add_unsigned<short> { typedef unsigned short type; }; template<> struct add_unsigned<int> { typedef unsigned int type; }; template<> struct add_unsigned<long> { typedef unsigned long type; }; #ifdef BOOST_HAS_LONG_LONG template<> struct add_unsigned<long long> { typedef unsigned long long type; }; #endif Can anybody spot weaknesses in that approach?
Regarding the concepts, it is correct to say, as you do, that the calls to begin(), end(), et al., must be qualified by boost::. But they also must say that the following
<quote>
is also well formed and has the same meaning:
using namespace unspecified-namespace; boost_range_begin(x); </quote>
I think you should also say somewhere (but not necessarily in the Concepts section) that unspecified-namespace contains the implementations of boost_range_begin(), et al., for the std containers, std::pair, arrays and null-terminated strings. I think that should do it. Does anybody have a better suggestion?
The Range concepts have been a fruitful source of confusion. I'm still myself a bit puzzled sometimes :-) We want to say a range r supports the expressions boost::begin(r) boost::end(r) boost::size(r) That is true fo certain types if we do include a certain header, otherwise it is not. That has really irritated me: in one translation-unit T could be conforming to a range, in others it need not to. If we use your quote above, it seems to me that the to expressions are not equivalent: you can't exchange one with the other. A Range concept is composed of more than the concept defining boost_range_begin()/boost_range_end(). We might want to say the following: T models a Range if it models one of the following concepts

Thorsten Ottosen wrote:
Btw, my range_size uses this code
template< class T > struct add_unsigned;
template<> struct add_unsigned<short> { typedef unsigned short type; };
template<> struct add_unsigned<int> { typedef unsigned int type; };
template<> struct add_unsigned<long> { typedef unsigned long type; };
#ifdef BOOST_HAS_LONG_LONG
template<> struct add_unsigned<long long> { typedef unsigned long long type; }; #endif
Can anybody spot weaknesses in that approach?
Well, you haven't defined a primary template, so add_unsigned<unsigned> will not compile. IMO, your primary template should "typedef T type;". But depending on what you're trying to do, this may work, and it's a lot shorter: template< typename Int > struct add_unsigned { typedef typename boost::uint_t< sizeof(Int) * CHAR_BIT >::least type; };
Regarding the concepts, it is correct to say, as you do, that the calls to begin(), end(), et al., must be qualified by boost::. But they also must say that the following
<quote>
is also well formed and has the same meaning:
using namespace unspecified-namespace; boost_range_begin(x);
</quote>
I think you should also say somewhere (but not necessarily in the Concepts section) that unspecified-namespace contains the implementations of boost_range_begin(), et al., for the std containers, std::pair, arrays and null-terminated strings. I think that should do it. Does anybody have a better suggestion?
The Range concepts have been a fruitful source of confusion. I'm still myself a bit puzzled sometimes :-)
We want to say a range r supports the expressions
boost::begin(r) boost::end(r) boost::size(r)
That is true fo certain types if we do include a certain header, otherwise it is not. That has really irritated me: in one translation-unit T could be conforming to a range, in others it need not to.
If you want to treat a type as a range, you need to include the file that makes that type a conforming range. I don't see any problem with that.
If we use your quote above, it seems to me that the to expressions are not equivalent: you can't exchange one with the other.
Why not?
A Range concept is composed of more than the concept defining boost_range_begin()/boost_range_end().
Of course. And I'm saying that for a type to conform to the range concept, it must do more than ensure that boost::begin(), boost::end(), and boost::size() are well-formed and have the proper semantics. I'm suggesting that you *add* the following requirements _in addition to_ the ones already listed: using namespace unspecified-namespace; boost_range_begin(r); is well-formed and has the same semantics as boost::begin(r). And the same for boost_range_end() and boost_range_size().
We might want to say the following:
T models a Range if it models one of the following concepts
??? -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
Regarding the concepts, it is correct to say, as you do, that the calls to begin(), end(), et al., must be qualified by boost::. But they also must say that the following
<quote>
is also well formed and has the same meaning:
using namespace unspecified-namespace; boost_range_begin(x);
</quote>
That doesn't look right to me. First of all, it seems to me that telling users that using namespace unspecified-namespace; ^^^^^^^^^^^^^^^^^^^^^ is well-formed is next to useless. How is a user supposed to satisfy that requirement? Am I missing something? Secondly, and I could be wrong, but I don't think that statement can be true for some of the types modeling range.
I think you should also say somewhere (but not necessarily in the Concepts section) that unspecified-namespace contains the implementations of boost_range_begin(), et al., for the std containers, std::pair, arrays and null-terminated strings. I think that should do it. Does anybody have a better suggestion?
The Range concepts have been a fruitful source of confusion. I'm still myself a bit puzzled sometimes :-)
We want to say a range r supports the expressions
boost::begin(r) boost::end(r) boost::size(r)
That is true fo certain types if we do include a certain header, otherwise it is not. That has really irritated me: in one translation-unit T could be conforming to a range, in others it need not to.
One way to solve that is to bite the bullet and always #include the necessary headers to make the statements of conformance consistent. Another possible option is to do that on compilers you don't know about, and on others intrude on namespace std and inject the forward declarations. As long as you make sure they match the real declarations, and you test it to make sure the compiler will accept it, you can get away with it. I know it's technically illegal but for some reason that doesn't rise to the level of a concern for me.
If you want to treat a type as a range, you need to include the file that makes that type a conforming range. I don't see any problem with that.
I do. Does the type satisfy the concept or doesn't it? Normally, that question is (and should be) answerable based on the visibility of the type alone.
If we use your quote above, it seems to me that the to expressions are not equivalent: you can't exchange one with the other.
Why not?
A Range concept is composed of more than the concept defining boost_range_begin()/boost_range_end().
For what it's worth to anyone, I agree with Thorsten here.
Of course. And I'm saying that for a type to conform to the range concept, it must do more than ensure that boost::begin(), boost::end(), and boost::size() are well-formed and have the proper semantics.
Aside from defining the correct traits to deduce the return types of these functions, that's all it must do. Concepts are for describing the requirements of generic algorithms.
I'm suggesting that you *add* the following requirements _in addition to_ the ones already listed:
using namespace unspecified-namespace; boost_range_begin(r);
is well-formed and has the same semantics as boost::begin(r). And the same for boost_range_end() and boost_range_size().
I know what you're trying to accomplish, but I think the correct approach is to document what boost::begin() et. al do. After all, for a specific concrete type the user could explicitly specialize boost::begin() et al. There's no need to provide an ADL overload.
We might want to say the following:
T models a Range if it models one of the following concepts
???
Yeah I don't understand that either. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Eric Niebler" <eric@boost-consulting.com> writes:
Regarding the concepts, it is correct to say, as you do, that the calls to begin(), end(), et al., must be qualified by boost::. But they also must say that the following
<quote>
is also well formed and has the same meaning:
using namespace unspecified-namespace; boost_range_begin(x);
</quote>
That doesn't look right to me. First of all, it seems to me that telling users that
using namespace unspecified-namespace; ^^^^^^^^^^^^^^^^^^^^^
is well-formed is next to useless. How is a user supposed to satisfy that requirement? Am I missing something?
It's not useless. It's saying that there exists a namespace such that after you make its symbols visible via a using declaration, an unqualified call to boost_range_begin(r) will, through the magic of ADL, be equivalent to boost::begin(r), for all types R that satisfy the Range concepts -- even if R has no associated namespaces, like int[5]. The implication of the requirement is that in order to satisfy the Range concept, users must define a boost_range_begin() overload that can be found via ADL. I put the using declaration in the requirement because without it, users might rightly wonder how a type such as int[5] could fulfill this particular requirement. The using declaration gives implementers the leeway to put boost_range_begin() someplace besides global scope, and it also gives them the leeway to call that namespace whatever they like without having to tell you. Now, if we want to allow users to make a type like unsigned short * satisfy the Range concept, we might want to change "unspecified-namespace" to "implementation-defined-namespace" so that they don't have to put their overloads at global scope. *shrug*
Secondly, and I could be wrong, but I don't think that statement can be true for some of the types modeling range.
Really? <snip>
If you want to treat a type as a range, you need to include the file that makes that type a conforming range. I don't see any problem with that.
I do. Does the type satisfy the concept or doesn't it? Normally, that question is (and should be) answerable based on the visibility of the type alone.
Why? We've already committed ourselves to letting users satisfy the Range concept non-intrusively through the use of free functions and ADL. The user can put those free functions anywhere she feels like. If they're visible, bingo! the type is a range. If not, it isn't. That leads directly to the situation Thorsten describes -- that in one translation unit, a type may satisfy the concept and in another it may not. If you have a problem with that, then you have a problem with non-intrusively satisfying a concept. Frankly, I still don't see it as a problem. <snip>
Of course. And I'm saying that for a type to conform to the range concept, it must do more than ensure that boost::begin(), boost::end(), and boost::size() are well-formed and have the proper semantics.
Aside from defining the correct traits to deduce the return types of these functions, that's all it must do. Concepts are for describing the requirements of generic algorithms.
Ah, now I understand the disconnect. From the perspective of someone writing a generic algorithm, you're right, that's all that's necessary. But what of the person trying to /satisfy/ the Range concept for their type? We're telling them that they need to overload boost_range_begin() et al., but they're looking at the Range concept and wondering how that gets them any closer. You're saying a Concept should not describe how it should be satisfied, just how it should be used. Is that right? <snip>
I think the correct approach is to document what boost::begin() et. al do. After all, for a specific concrete type the user could explicitly specialize boost::begin() et al. There's no need to provide an ADL overload.
As the Perl guys say, "There's more than one way to do it!" But I don't like Perl. :-P I like there to be one sanctioned way to do something. And I was going on the assumption that the Concept's requirements would be the final word on how to use *and* satisfy the Concept. But maybe I'm wrong. -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes:
David Abrahams wrote:
"Eric Niebler" <eric@boost-consulting.com> writes:
<quote>
is also well formed and has the same meaning:
using namespace unspecified-namespace; boost_range_begin(x);
</quote>
That doesn't look right to me. First of all, it seems to me that telling users that
using namespace unspecified-namespace; ^^^^^^^^^^^^^^^^^^^^^
is well-formed is next to useless. How is a user supposed to satisfy that requirement? Am I missing something?
It's not useless. It's saying that there exists a namespace such that after you make its symbols visible via a using declaration, an unqualified call to boost_range_begin(r) will, through the magic of ADL, be equivalent to boost::begin(r), for all types R that satisfy the Range concepts -- even if R has no associated namespaces, like int[5]. The implication of the requirement is that in order to satisfy the Range concept, users must define a boost_range_begin() overload that can be found via ADL.
Or, they can somehow put it in unspecified-namespace, whose identity we're not telling them. Or they can put it in the global namespace, as long as it's been seen before the point of use, but I don't think we want to encourage them to try that, for the same reason we don't want to encourage them to put it in boost::. I understand where you're going with this, but: 1. I think the thread of implication is a too hard to derive without your guidance 2. I'm afraid it might tempt users to try an incantation other than boost::begin(x) to get the begin iterator of x. 3. It might be too strict if explicit specialization of boost::begin works (whether it will work probably depends on whether boost::begin itself is overloaded). 4. It overemphasizes a detail that most of the audience for the concept definition doesn't need.
I put the using declaration in the requirement because without it, users might rightly wonder how a type such as int[5] could fulfill this particular requirement.
Yeah, that's the same reason I have been saying we need to tell them in detail about boost::begin() in a way that makes it clear. If we do it the way you're suggesting, even it were possible to divine that int[5] is a range just by reading the reference docs, we'd still need to somehow describe the effects of boost::begin on it so that a reader knows what its begin iterator is (in principle it could be anywhere in the array). So why not simply document that there exists in namespace boost: template <class T, std::size_t N> T* begin(T(&x)[N]) with semantics return &x[0]; ??
The using declaration gives implementers the leeway to put boost_range_begin() someplace besides global scope, and it also gives them the leeway to call that namespace whatever they like without having to tell you.
That makes sense now, but still doesn't seem right for reasons listed above.
Now, if we want to allow users to make a type like unsigned short * satisfy the Range concept, we might want to change "unspecified-namespace" to "implementation-defined-namespace" so that they don't have to put their overloads at global scope. *shrug*
In that case we would have to simply define the namespace and put _that_ in the docs. After all, we can't count on the C++ implementation to tell us which one to use ;-) And if you're thinking of how it would look in a standards proposal, why wouldn't we want users to be able to make that short* model Range portably? I think the namespace had better be defined by the spec.
Secondly, and I could be wrong, but I don't think that statement can be true for some of the types modeling range.
Really?
Maybe I was indeed wrong ;-)
If you want to treat a type as a range, you need to include the file that makes that type a conforming range. I don't see any problem with that.
I do. Does the type satisfy the concept or doesn't it? Normally, that question is (and should be) answerable based on the visibility of the type alone.
Why? We've already committed ourselves to letting users satisfy the Range concept non-intrusively through the use of free functions and ADL. The user can put those free functions anywhere she feels like. If they're visible, bingo! the type is a range. If not, it isn't.
Exactly.
That leads directly to the situation Thorsten describes -- that in one translation unit, a type may satisfy the concept and in another it may not. If you have a problem with that, then you have a problem with non-intrusively satisfying a concept. Frankly, I still don't see it as a problem.
I am uncomfortable with documenting that the standard containers already model Range if it is really dependent on whether a particular header has been #included. That's a different question. In that case, the library is supposedly delivering "Range-ness."
<snip>
Of course. And I'm saying that for a type to conform to the range concept, it must do more than ensure that boost::begin(), boost::end(), and boost::size() are well-formed and have the proper semantics.
Aside from defining the correct traits to deduce the return types of these functions, that's all it must do. Concepts are for describing the requirements of generic algorithms.
Ah, now I understand the disconnect. From the perspective of someone writing a generic algorithm, you're right, that's all that's necessary. But what of the person trying to /satisfy/ the Range concept for their type? We're telling them that they need to overload boost_range_begin() et al., but they're looking at the Range concept and wondering how that gets them any closer.
We can hold their hands in the tutorial documentation and explain everything in some detail. The reference docs should be almost- minimal and complete. If you find out that boost::begin() has to work for your type, you can go look at the description of that function and see what's required. This is no different from the iterator concepts (incomplete C++98 iterator documentation aside) saying that std::iterator_traits<T>::value_type must yield the value type of the iterator. Then you go look at the docs for iterator_traits and find out that you can put value_type in your iterator class' body. In case you're going to object that the user could always specialize iterator_traits without looking at its docs, don't forget that it could in principle have default template arguments that make any given specialization fail. So you really do need to look at its docs.
You're saying a Concept should not describe how it should be satisfied, just how it should be used. Is that right?
It should describe both, but it's okay if the former is not spelled out all in one place, and I find the approach I'm suggesting to be more understandable than yours.
I think the correct approach is to document what boost::begin() et. al do. After all, for a specific concrete type the user could explicitly specialize boost::begin() et al. There's no need to provide an ADL overload.
As the Perl guys say, "There's more than one way to do it!" But I don't like Perl. :-P
Me too.
I like there to be one sanctioned way to do something.
Me too. "Minimal constraints on models" is a fundamental principle of generic programming, but I wouldn't be surprised if, once we think through the implications of explicit specialization of begin et. al., we will find that allowing explicit specializations to work will unduly constrain implementations of the Range library.
And I was going on the assumption that the Concept's requirements would be the final word on how to use *and* satisfy the Concept. But maybe I'm wrong.
You're not wrong; this is a question of how to approach delivering that information. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
"Eric Niebler" <eric <at> boost-consulting.com> writes:
I put the using declaration in the requirement because without it, users might rightly wonder how a type such as int[5] could fulfill this particular requirement.
So why not simply document that there exists in namespace boost:
template <class T, std::size_t N> T* begin(T(&x)[N])
with semantics
return &x[0];
??
The reference docs state exactly what the functions returns. In the RC branch, I've added the following line: boost_range_begin(x) otherwise
You're saying a Concept should not describe how it should be satisfied, just how it should be used. Is that right?
It should describe both, but it's okay if the former is not spelled out all in one place, and I find the approach I'm suggesting to be more understandable than yours.
I like this distinction. What it amounts to is that there might be several ways to satisfy a concept and that can be explained elsewhere. -Thorsten

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
"Eric Niebler" <eric <at> boost-consulting.com> writes:
I put the using declaration in the requirement because without it, users might rightly wonder how a type such as int[5] could fulfill this particular requirement.
So why not simply document that there exists in namespace boost:
template <class T, std::size_t N> T* begin(T(&x)[N])
with semantics
return &x[0];
??
The reference docs state exactly what the functions returns.
Yes, but the presentation is unprecedented. It's also a bit confusing, because until you understand how to read the tables, each function appears to have multiple return values. Normally, the table format should be used for describing concept requirements, and individual functions should be documented in the usual way, e.g.: template <class T, std::size_t N> T* begin(T(&x)[N]) Returns: &x[0] Complexity: Constant time If you want to invent a new documentation style you need to make sure to document the documentation style :)
In the RC branch, I've added the following line:
boost_range_begin(x) otherwise
That only adds to the confusion when there's no conditional ("if ...") present. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
The reference docs state exactly what the functions returns.
Yes, but the presentation is unprecedented. It's also a bit confusing, because until you understand how to read the tables, each function appears to have multiple return values.
then what about the following: Returns: x.first if x is an std::pair x if x is an array range_begin(x) if range_begin() can be found by ADL x.begin() otherwise ? Thorsten

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
The reference docs state exactly what the functions returns.
Yes, but the presentation is unprecedented. It's also a bit confusing, because until you understand how to read the tables, each function appears to have multiple return values.
then what about the following:
Returns:
x.first if x is an std::pair x if x is an array range_begin(x) if range_begin() can be found by ADL x.begin() otherwise
?
that would be an improvement. I think you need "otherwise," at the beginning of all lines of the 2nd column but the first. It's not clear to me that this is better than expressing range_begin as three separate overloads (I don't think it matters whether you actually implement it that way), but it works in principle. Nitpicking, range_begin(x) if range_begin() can be found by ADL is not quite right. I think it's more like range_begin(x) if range_begin(x) is well-formed in an arbitrary namespace. but I'm not certain I have that right either. Are you really detecting whether range_begin can be found via ADL? I wrote code to do that trick, but I don't remember seeing it in the range lib. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
then what about the following:
Returns:
x.first if x is an std::pair x if x is an array range_begin(x) if range_begin() can be found by ADL x.begin() otherwise
?
that would be an improvement. I think you need "otherwise," at the beginning of all lines of the 2nd column but the first.
like so x.first if x is an std::pair, otherwise x if x is an array, otherwise range_begin(x) if range_begin() can be found by ADL, otherwise x.begin() ? I personally think having one "otherwise" is ok. lot's of math do it like that.
Nitpicking,
range_begin(x) if range_begin() can be found by ADL
is not quite right. I think it's more like
range_begin(x) if range_begin(x) is well-formed in an arbitrary namespace.
but I'm not certain I have that right either.
hm...what about if range_begin() can be found in namespaces associated with x. OTOH, isn't that just ADL?
Are you really detecting whether range_begin can be found via ADL?
No.
I wrote code to do that trick, but I don't remember seeing it in the range lib.
Cool trick, though. The suggested spec. says that the return-type depends on 4 cases, it doesn't state how these cases are implemented. I don't think they need to state that. -Thorsten

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
then what about the following:
Returns:
x.first if x is an std::pair x if x is an array range_begin(x) if range_begin() can be found by ADL x.begin() otherwise
?
that would be an improvement. I think you need "otherwise," at the beginning of all lines of the 2nd column but the first.
like so
x.first if x is an std::pair, otherwise x if x is an array, otherwise range_begin(x) if range_begin() can be found by ADL, otherwise x.begin()
?
I personally think having one "otherwise" is ok. lot's of math do it like that.
I think I changed my mind. I agree with you.
Nitpicking,
range_begin(x) if range_begin() can be found by ADL
is not quite right. I think it's more like
range_begin(x) if range_begin(x) is well-formed in an arbitrary namespace.
but I'm not certain I have that right either.
hm...what about
if range_begin() can be found in namespaces associated with x.
Not quite right, because range_begin() denotes a function taking no arguments. You really should say range_begin(x) because it's an expression; it accounts for default arguments, function templates, derived->base conversions, etc.
OTOH, isn't that just ADL?
Yes, but the way you described it is just not correct; that's all. Oh, and I suppose an overload of range_begin in the global namespace would be found, provided it was declared before its point of use. That's why something like range_begin(x) if range_begin(x) is well-formed is more accurate.
Are you really detecting whether range_begin can be found via ADL?
No.
Then how are you arranging for the semantics to fall back to x.begin() when range_begin(x) can't be found? If you're not really doing that, you need to fix the docs.
I wrote code to do that trick, but I don't remember seeing it in the range lib.
Cool trick, though.
Yes; it's partly due to Doug Gregor. see boost/detail/is_incrementable.hpp
The suggested spec. says that the return-type depends on 4 cases, it doesn't state how these cases are implemented. I don't think they need to state that.
I don't understand what you're trying to say. Specific implementation details are not important, but the precise semantics of each case is important. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> writes:
Are you really detecting whether range_begin can be found via ADL?
No.
Then how are you arranging for the semantics to fall back to x.begin() when range_begin(x) can't be found? If you're not really doing that, you need to fix the docs.
Duh; it's obvious -- you provide the default implementation of begin(x) in boost. Sorry 'bout that mental lapse :) -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
hm...what about
if range_begin() can be found in namespaces associated with x.
Not quite right, because range_begin() denotes a function taking no arguments. You really should say
range_begin(x)
because it's an expression; it accounts for default arguments,
right, this is true.
function templates,
specializations are found too, but we don't document what to specialize (eg. boost::range_detail::range_begin())
derived->base conversions, etc.
these won't happen as the default implementation is the one in namespace boost::range_detail which is a function template.
OTOH, isn't that just ADL?
Yes, but the way you described it is just not correct; that's all. Oh, and I suppose an overload of range_begin in the global namespace would be found, provided it was declared before its point of use.
no, it wouldn't: the global namespace is never searched and the default is chosen then, so a bit the same reason that a base class version won't work either. (arguably the base class version should be found, but that's a different story)
That's why something like
range_begin(x) if range_begin(x) is well-formed
is more accurate.
I thinks ADL is the only mechanism that will valid here. So with the change if range_begin(x) can be found by ADL I think we nailed it. This should implicitly account for default arguments, but I don't think we need to mention that. As a related issue, how was everyone feeling about range_begin() instead of boost_range_begin()? -Thorsten

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
hm...what about
if range_begin() can be found in namespaces associated with x.
Not quite right, because range_begin() denotes a function taking no arguments. You really should say
range_begin(x)
because it's an expression; it accounts for default arguments,
right, this is true.
function templates,
specializations are found too, but we don't document what to specialize (eg. boost::range_detail::range_begin())
That's not what I mean. I mean that if the user writes a function template called range_begin in his Range's namespace it will be found.
derived->base conversions, etc.
these won't happen as the default implementation is the one in namespace boost::range_detail which is a function template.
I see, because of the default, as you say later.
OTOH, isn't that just ADL?
Yes, but the way you described it is just not correct; that's all. Oh, and I suppose an overload of range_begin in the global namespace would be found, provided it was declared before its point of use.
no, it wouldn't: the global namespace is never searched and the default is chosen then
Ah, okay.
so a bit the same reason that a base class version won't work either. (arguably the base class version should be found, but that's a different story)
That's why something like
range_begin(x) if range_begin(x) is well-formed
is more accurate.
I thinks ADL is the only mechanism that will valid here.
I think you're probably right.
So with the change
if range_begin(x) can be found by ADL
I think we nailed it.
What you _mean_ is correct, but that's not phrased right. ADL find function overloads, not expressions. That's why I wrote range_begin(x) if range_begin(x) is well-formed How about range_begin(x) if it would invoke a function found by argument dependent lookup. ?
This should implicitly account for default arguments, but I don't think we need to mention that.
As a related issue, how was everyone feeling about range_begin() instead of boost_range_begin()?
I'm ambivalent. There are no really good answers to this dispatching question, only compromises. By dropping boost_ you increase the chance of a semantic collision, but you make the name more general. I don't think it makes sense to make that change unless you're also going to change the concept requirements from boost::begin(x) is valid to range_begin(x) is valid and just tell people that the standard containers and builtin arrays only model ranges in the presence of some range_begin overloads provided by the library. As long as the concept is coupled to namespace boost there's little point in removing boost_ from the customization point. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
specializations are found too, but we don't document what to specialize (eg. boost::range_detail::range_begin())
That's not what I mean. I mean that if the user writes a function template called range_begin in his Range's namespace it will be found.
right.
So with the change
if range_begin(x) can be found by ADL
I think we nailed it.
What you _mean_ is correct, but that's not phrased right. ADL find function overloads, not expressions. That's why I wrote
range_begin(x) if range_begin(x) is well-formed
How about
range_begin(x) if it would invoke a function found by argument dependent lookup.
?
well, it might sound really like someting in the standard, but it also appears fairly cryptic to me. If the only problem was that ADL applies to function overloads (or names in general), then why not just strip the "(x)": range_begin(x) if range_begin can be found by ADL ? Wouldn't this account for all names (not just function overloads)?
This should implicitly account for default arguments, but I don't think we need to mention that.
As a related issue, how was everyone feeling about range_begin() instead of boost_range_begin()?
I'm ambivalent. There are no really good answers to this dispatching question, only compromises. By dropping boost_ you increase the chance of a semantic collision, but you make the name more general. I don't think it makes sense to make that change unless you're also going to change the concept requirements from
boost::begin(x) is valid
to
range_begin(x) is valid
and just tell people that the standard containers and builtin arrays only model ranges in the presence of some range_begin overloads provided by the library.
this precense is already needed with the boost::begin() requirement. then we also need to revise how the using declaration is used and it becomes impossible, IIUIC, to have a fall-back mechanism, in this case t.begin() (because one does not have to include the header where this fall-back is provided)
As long as the concept is coupled to namespace boost there's little point in removing boost_ from the customization point.
well, it means that the customization point can be use in other context independent of boost. It no longer belongs to boost. Quite important. -Thorsten

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
specializations are found too, but we don't document what to specialize (eg. boost::range_detail::range_begin())
That's not what I mean. I mean that if the user writes a function template called range_begin in his Range's namespace it will be found.
right.
So with the change
if range_begin(x) can be found by ADL
I think we nailed it.
What you _mean_ is correct, but that's not phrased right. ADL find function overloads, not expressions. That's why I wrote
range_begin(x) if range_begin(x) is well-formed
How about
range_begin(x) if it would invoke a function found by argument dependent lookup.
?
well, it might sound really like someting in the standard,
That's not what I'm trying to achieve at all.
but it also appears fairly cryptic to me.
What's cryptic about it? Here are a few other options that are similarly precise: range_begin(x) if it would result in range_begin being found by argument dependent lookup. range_begin(x) if there is a matching range_begin in a namespace associated with the type of x.
If the only problem was that ADL applies to function overloads (or names in general), then why not just strip the "(x)":
range_begin(x) if range_begin can be found by ADL
? Wouldn't this account for all names (not just function overloads)?
It lacks precision; it doesn't force that range_begin that's found to be in a namespace associated with the single argument x. Just because there's a range_begin that *can* be found via ADL doesn't mean it will be found in that expression. This is nitpicky, but I think the language is slightly clearer.
This should implicitly account for default arguments, but I don't think we need to mention that.
As a related issue, how was everyone feeling about range_begin() instead of boost_range_begin()?
I'm ambivalent. There are no really good answers to this dispatching question, only compromises. By dropping boost_ you increase the chance of a semantic collision, but you make the name more general. I don't think it makes sense to make that change unless you're also going to change the concept requirements from
boost::begin(x) is valid
to
range_begin(x) is valid
and just tell people that the standard containers and builtin arrays only model ranges in the presence of some range_begin overloads provided by the library.
this precense is already needed with the boost::begin() requirement. then we also need to revise how the using declaration is used and it becomes impossible, IIUIC, to have a fall-back mechanism, in this case t.begin() (because one does not have to include the header where this fall-back is provided)
As long as the concept is coupled to namespace boost there's little point in removing boost_ from the customization point.
well, it means that the customization point can be use in other context independent of boost. It no longer belongs to boost. Quite important.
Maybe you're right. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
How about
range_begin(x) if it would invoke a function found by argument dependent lookup.
?
but it also appears fairly cryptic to me.
What's cryptic about it?
the compiler doesn't care about invoking something...that happens at runtime IMO.
Here are a few other options that are similarly precise:
range_begin(x) if it would result in range_begin being found by argument dependent lookup.
range_begin(x) if there is a matching range_begin in a namespace associated with the type of x.
If the only problem was that ADL applies to function overloads (or names in general), then why not just strip the "(x)":
range_begin(x) if range_begin can be found by ADL
? Wouldn't this account for all names (not just function overloads)?
It lacks precision; it doesn't force that range_begin that's found to be in a namespace associated with the single argument x.
hm...AFAICT, only the last of your three versions mentions the namespace associated with x.
Just because there's a range_begin that *can* be found via ADL doesn't mean it will be found in that expression.
ok, I think I'm getting it...for example, the a base class version of begin can be found, but derived to base classs reference will not happen and so t.begin() is called instead. I like this version best: range_begin(x) if it would result in range_begin being found by ADL
This is nitpicky, but I think the language is slightly clearer.
right. somehow we still don't say when it wouldn't be found. The question is, maybe, is the overload actually found, but then later, during overload resolution, discarded as a best match? In that light I think range_begin(x) if there is an exactly matching range_begin in a namespace associated with x. is better. we then need to state that "exactly matching" means without no implicit conversions required. -Thorsten

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
How about
range_begin(x) if it would invoke a function found by argument dependent lookup.
?
but it also appears fairly cryptic to me.
What's cryptic about it?
the compiler doesn't care about invoking something...that happens at runtime IMO.
Of course it does. The _determination_ of whether an expression would invoke something found by ADL is one of the compiler's jobs.
Here are a few other options that are similarly precise:
range_begin(x) if it would result in range_begin being found by argument dependent lookup.
range_begin(x) if there is a matching range_begin in a namespace associated with the type of x.
If the only problem was that ADL applies to function overloads (or names in general), then why not just strip the "(x)":
range_begin(x) if range_begin can be found by ADL
? Wouldn't this account for all names (not just function overloads)?
It lacks precision; it doesn't force that range_begin that's found to be in a namespace associated with the single argument x.
hm...AFAICT, only the last of your three versions mentions the namespace associated with x.
Namespaces, plural. The others mention them by implication; if you carefully think through each one you'll see that.
Just because there's a range_begin that *can* be found via ADL doesn't mean it will be found in that expression.
ok, I think I'm getting it...for example, the a base class version of begin can be found, but derived to base classs reference will not happen and so t.begin() is called instead.
No, in fact the namespace of a base class is an associated namespace of the derived class, so it works. I mean, for example, that there might be range_begin that applies to some unrelated type Y (with instance y) such that range_begin(y) finds a range_begin overload via ADL. As you see, range_begin *can* be found via ADL... when called with an argument of type y.
I like this version best:
range_begin(x) if it would result in range_begin being found by ADL
Fine with me.
This is nitpicky, but I think the language is slightly clearer.
right. somehow we still don't say when it wouldn't be found.
I don't follow.
The question is, maybe, is the overload actually found, but then later, during overload resolution, discarded as a best match?
I think you are saying quite clearly that it will not be discarded... but after a little investigation I can see that it could, so...
In that light I think
range_begin(x) if there is an exactly matching range_begin in a namespace associated with x.
is better. we then need to state that "exactly matching" means without no implicit conversions required.
I think you are right... almost. 0. The standard uses the term "an exact match," which is already clear that there are no conversions. 1. The namespace is associated with the _type_ of x, not with x itself. 2. isn't template <class T> int range_begin(T) in an associated namespace an exact match? Because, if so, that will cause an ambiguity with the one in the range library Maybe the thing to do is just expose all the details: show the semantics of begin(x) as return range_begin(x), and document the range_begin overloads that are in the library. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Thorsten Ottosen wrote:
David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
The reference docs state exactly what the functions returns.
Yes, but the presentation is unprecedented. It's also a bit confusing, because until you understand how to read the tables, each function appears to have multiple return values.
then what about the following:
Returns:
x.first if x is an std::pair x if x is an array range_begin(x) if range_begin() can be found by ADL x.begin() otherwise
?
I have exactly the same situation in Iostreams. I give each function its own page of documentation, with tables demonstrating how the semantics depends on the properties of the template parameters. For example: www.boost.org/libs/iostreams/doc/?page=functions/read.html%23reference Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> writes:
I have exactly the same situation in Iostreams. I give each function its own page of documentation, with tables demonstrating how the semantics depends on the properties of the template parameters. For example:
www.boost.org/libs/iostreams/doc/?page=functions/read.html%23reference
not bad. A nit: The semantics of read depends on the category of T as follows ^ ^ "semantics" is plural, so drop the "s" here. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> writes:
I have exactly the same situation in Iostreams. I give each function its own page of documentation, with tables demonstrating how the semantics depends on the properties of the template parameters. For example:
www.boost.org/libs/iostreams/doc/?page=functions/read.html%23reference
not bad. A nit:
The semantics of read depends on the category of T as follows ^ ^ "semantics" is plural, so drop the "s" here.
Are you sure? I'd say it's a mass noun. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> writes:
David Abrahams wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> writes:
I have exactly the same situation in Iostreams. I give each function its own page of documentation, with tables demonstrating how the semantics depends on the properties of the template parameters. For example:
www.boost.org/libs/iostreams/doc/?page=functions/read.html%23reference
not bad. A nit:
The semantics of read depends on the category of T as follows ^ ^ "semantics" is plural, so drop the "s" here.
Are you sure? I'd say it's a mass noun.
What's a mass noun? e.g., "Water?" http://dictionary.reference.com/search?q=semantics seems to indicate it could go either way, if I am reading it correctly. Oh, well. Sorry to bother you. I guess it would read more easily if you'd replace "semantics" with "effect." -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
"Jonathan Turkanis" <technews@kangaroologic.com> writes:
David Abrahams wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> writes:
I have exactly the same situation in Iostreams. I give each function its own page of documentation, with tables demonstrating how the semantics depends on the properties of the template parameters. For example:
www.boost.org/libs/iostreams/doc/?page=functions/read.html%23reference
not bad. A nit:
The semantics of read depends on the category of T as follows ^ ^ "semantics" is plural, so drop the "s" here.
Are you sure? I'd say it's a mass noun.
What's a mass noun? e.g., "Water?"
I'd never heard of "mass noun" before, either. (If I did, it was in elementary or middle school and I don't recall learning it. I don't recall a lot from back then!) I looked at http://en.wikipedia.org/wiki/Mass_noun to get more information (I'm going to have to edit that page; there are some awkward things on it!). I certainly know about mass nouns, but never knew what they were called. Thanks for the grammatical lesson Jonathan!
http://dictionary.reference.com/search?q=semantics seems to indicate it could go either way, if I am reading it correctly. Oh, well.
I think "semantics" being "the study..." means that it should be treated as a mass noun. I can't grok M-W's "noun plural but singular or plural in construction."
I guess it would read more easily if you'd replace "semantics" with "effect."
Indeed. That would resolve the matter handily. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

On 9/9/05 1:24 PM, "Eric Niebler" <eric@boost-consulting.com> wrote: [SNIP]
Well, you haven't defined a primary template, so add_unsigned<unsigned> will not compile. IMO, your primary template should "typedef T type;". But depending on what you're trying to do, this may work, and it's a lot shorter:
template< typename Int > struct add_unsigned { typedef typename boost::uint_t< sizeof(Int) * CHAR_BIT
::least type; }; [TRUNCATE]
Your formula will include any unused junk bits a type may have. You can use "std::numeric_limits<Int>::digits" for the number of bits used. Note that the "uint_t" template itself uses that "numeric_limits" expression for its internals, so you better use it to avoid a mismatch. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

Eric Niebler <eric <at> boost-consulting.com> writes:
Thorsten Ottosen wrote:
Btw, my range_size uses this code
template< class T > struct add_unsigned;
Can anybody spot weaknesses in that approach?
Well, you haven't defined a primary template, so add_unsigned<unsigned> will not compile. IMO, your primary template should "typedef T type;". But depending on what you're trying to do, this may work, and it's a lot shorter:
template< typename Int > struct add_unsigned { typedef typename boost::uint_t< sizeof(Int) * CHAR_BIT >::least type; };
right, it shorter. I wanted to remove the need for specializing range_size<> but apparently this relation does not hold for all implementations: sizeof( container::size_type ) == sizeof( container::iterator::difference_type ) :-( what a shame. -Thorsten

Eric Niebler <eric <at> boost-consulting.com> writes:
We might want to say the following:
T models a Range if it models one of the following concepts
???
I had a whole lot of stuff here, which was never posted due to the breakdown of the web-interface. However, the following discussion has made it redundant now. -Thorsten

Thorsten Ottosen wrote:
My recollection is that having boost::foo() do ADL was *firmly* rejected, and
I think I am at least partially responsible for this. And so far I am still unconvinced that making a qualified call basically a shortcut for an unqualified call is a good idea. Thomas

Thomas Witt <witt@acm.org> writes:
Thorsten Ottosen wrote:
My recollection is that having boost::foo() do ADL was *firmly* rejected, and
I think I am at least partially responsible for this. And so far I am still unconvinced that making a qualified call basically a shortcut for an unqualified call is a good idea.
It's a shortcut for an unqualified call plus a using declaration. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Thomas Witt <witt@acm.org> writes:
Thorsten Ottosen wrote:
My recollection is that having boost::foo() do ADL was *firmly* rejected, and
I think I am at least partially responsible for this. And so far I am still unconvinced that making a qualified call basically a shortcut for an unqualified call is a good idea.
It's a shortcut for an unqualified call plus a using declaration.
After reviewing my earlier posts on this topic I still don't believe in the proposed resolution(boost_range_begin etc.) and in the specific use of customization points in general. And on a related note I am unconvinced that it is fit for standardization (this is not meant as critique regarding Thorstens proposal). AFAICS the underlying problem is retrofitting an interface to a component that is not owned by us. The problem usually arises in us being unable to invade the components namespace. As for the case at hand std::vector<int> v; begin(v); // error begin is member of boost not std! does not work. In order to have our cake and eat it we move our interface in our own namespace where we are free to do whatever we see fit. The problem being that we cannot provide the interface for all types out there but we still want this to be extensible. Furtheremore we sure don't want anybody to invade our namespace, so we provide an ADL customization point so that other libraries can hook into our interface. So what does that mean for the other libraries out there? They have to clutter their interface with these hooks that are meant only for communication with the boost library and that are useless if the user of the other library does not use boost. Apart from the fact that the hook strategy is unlikely to scale well (how many potentially useless hooks do you want in your library?) it also seems to put the burden on the wrong side. In order to make the use of a finite set of types easier we complicate the use and design of an infinite set of types. There is one more point I like to make. How is the range concept going to look like. As far as I can see it will "degrade" to something that is only used in the interface between the "public" interface that is in boost and the component that wants to expose the boost interface. Thomas

Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
Thomas Witt <witt@acm.org> writes:
Thorsten Ottosen wrote:
My recollection is that having boost::foo() do ADL was *firmly* rejected, and
I think I am at least partially responsible for this. And so far I am still unconvinced that making a qualified call basically a shortcut for an unqualified call is a good idea.
It's a shortcut for an unqualified call plus a using declaration.
After reviewing my earlier posts on this topic I still don't believe in the proposed resolution(boost_range_begin etc.) and in the specific use of customization points in general.
The second half of that sentence sounds rather sweeping and extreme.
AFAICS the underlying problem is retrofitting an interface to a component that is not owned by us.
It me took about 10 re-readings of this email to guess that us == the library developer and the component is, e.g. a pre-existing component like std::vector or builtin arrays. Did I get that right?
The problem usually arises in us being unable to invade the components namespace. As for the case at hand
std::vector<int> v; begin(v); // error begin is member of boost not std!
does not work.
Right.
In order to have our cake and eat it we move our interface in our own namespace where we are free to do whatever we see fit.
I don't see it that way. If we're using the strategy discussed here -- which I'm not claiming is ideal -- we do that part just to keep other authors of generic code from having to remember to write ugly using declarations. We move the _implementation_ of the function for those pre-existing 3rd party types into our own namespace where we do anything we want. In other words, aside from that ugly using declaration, which is needed to make the interface truly generic, the interface lives in the "global ADL space." I guess if you view the using declaration as a kind of namespace qualification, you could see this as moving the interface into our namespace.
The problem being that we cannot provide the interface for all types out there but we still want this to be extensible. Furtheremore we sure don't want anybody to invade our namespace
I don't think we care about that last part, provided they do it in a way prescribed by us. Certainly, specialization is a kind of invasion, and that's no problem. The problem becomes that inviting the user to invade our namespace with _overloads_ just doesn't work because ordinary lookup is limited to what's visible at the point-of-definition. As I recently posted for Robert Ramey, 14.6.4 Dependent name resolution [temp.dep.res] In resolving dependent names, names from the following sources are considered: — Declarations that are visible at the point of definition of the template. — Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context. which makes overloading in our namespace fragile due to header order dependency.
so we provide an ADL customization point so that other libraries can hook into our interface.
Right.
So what does that mean for the other libraries out there? They have to clutter their interface with these hooks that are meant only for communication with the boost library and that are useless if the user of the other library does not use boost.
Not if you buy Peter Dimov's argument that many customization points become "public domain" and are no longer the property of any library. Read that part of the thread carefully. Once people start supplying begin() hooks, other library authors may start making interfaces that work with Boost.Range compatible types.
Apart from the fact that the hook strategy is unlikely to scale well (how many potentially useless hooks do you want in your library?)
In my library? Useless? I'm confused. Am "I" the author of a library component who is trying to get it to work with other libraries like Boost.Range? Something like begin(x), if I'm the author of a container type, seems useful to me!
it also seems to put the burden on the wrong side.
So... the author of Boost.Range should figure out how to get the begin iterator out of a type he doesn't know about? Who should have the burden?
In order to make the use of a finite set of types easier we complicate the use and design of an infinite set of types.
More confused. Which finite set? Which infinite set?
There is one more point I like to make. How is the range concept going to look like. As far as I can see it will "degrade" to something that is only used in the interface between the "public" interface that is in boost and the component that wants to expose the boost interface.
It might be used by anyone wanting to write composable generic algorithms over sequences. One of the problems with the STL interface is that composing the algorithms is more cumbersome than it should be. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
The second half of that sentence sounds rather sweeping and extreme.
Please don't read to much into it. It's just that I have serious doubts.
It me took about 10 re-readings of this email to guess that us == the library developer and the component is, e.g. a pre-existing component like std::vector or builtin arrays. Did I get that right?
Sorry for not being more clear. You are basically right. Us means boost or boost::range in particular.
In order to have our cake and eat it we move our interface in our own namespace where we are free to do whatever we see fit.
I don't see it that way. If we're using the strategy discussed here -- which I'm not claiming is ideal -- we do that part just to keep other authors of generic code from having to remember to write ugly using declarations.
I would not want to describe using declarations as ugly. They clearly describe the intent of the programmer. Though I do agree that forgetting about them is a very likely cause of error.
In other words, aside from that ugly using declaration, which is needed to make the interface truly generic, the interface lives in the "global ADL space."
The point that I am concerned with is _which_ interface adds up in the "global ADL space". IIUC the ADL hooks boost_range_begin etc. end up there. AFAICS these are not meant to be used by a user of a library, are they?
I guess if you view the using declaration as a kind of namespace qualification, you could see this as moving the interface into our namespace.
I was talking about functions like begin(), end() those end up in the boost namespace, don't they.
The problem being that we cannot provide the interface for all types out there but we still want this to be extensible. Furtheremore we sure don't want anybody to invade our namespace
I don't think we care about that last part, provided they do it in a way prescribed by us. Certainly, specialization is a kind of invasion, and that's no problem. The problem becomes that inviting the user to invade our namespace with _overloads_ just doesn't work because ordinary lookup is limited to what's visible at the point-of-definition.
I got carried away in my writing. I am aware of the issue, if only for the reason of getting bitten by ignorance in the past.
So what does that mean for the other libraries out there? They have to clutter their interface with these hooks that are meant only for communication with the boost library and that are useless if the user of the other library does not use boost.
Not if you buy Peter Dimov's argument that many customization points become "public domain" and are no longer the property of any library. Read that part of the thread carefully. Once people start supplying begin() hooks, other library authors may start making interfaces that work with Boost.Range compatible types.
I've read through his argument at least twice in the past. That being said I might still not get the point. I do agree with the names becomming public domain. What I disagree with is that names like boost_range_begin are suitable for this. They will likely always be warts in an interface.
Apart from the fact that the hook strategy is unlikely to scale well (how many potentially useless hooks do you want in your library?)
In my library? Useless? I'm confused. Am "I" the author of a library component who is trying to get it to work with other libraries like Boost.Range?
Just assume you are.
Something like begin(x), if I'm the author of a container type, seems useful to me!
Yes begin() but not boost_range_begin()
It might be used by anyone wanting to write composable generic algorithms over sequences. One of the problems with the STL interface is that composing the algorithms is more cumbersome than it should be.
I am all for a range concept. I am at odds with the proposed solution for that. Thomas

Thomas Witt <witt <at> acm.org> writes:
David Abrahams wrote:
Not if you buy Peter Dimov's argument that many customization points become "public domain" and are no longer the property of any library. Read that part of the thread carefully. Once people start supplying begin() hooks, other library authors may start making interfaces that work with Boost.Range compatible types.
I've read through his argument at least twice in the past. That being said I might still not get the point. I do agree with the names becomming public domain. What I disagree with is that names like boost_range_begin are suitable for this. They will likely always be warts in an interface.
I feel the same way. boost_range_begin() is gonna look like a wart in the interface. Anyway, I've given up on the idea of having backwards compatibility between boost.range and the proposed std version. If we fix ADL for C++0x, I would favor what the proposal currently says (I think the fact that you can forget using std::begin; is of minor importance). If we don't fix ADL in C++0x, then we might consider (1) ADL customization points via range_begin()/range_end()/range_size() or (2) go for a class like range_traits<T>::begin()...ect. -Thorsten

From: Thorsten Ottosen <nesotto@cs.aau.dk>
(2) go for a class like range_traits<T>::begin()...ect.
Given the set of things that must be specialized to make a type conformant, as documented in https://www.cs.aau.dk/~nesotto/C++/doc/boost_range.html#minimal_interface, that seems like a beneficial thing to do. You could then document a SinglePassRange traits class and a ForwardRange traits class. I don't think that the usual arguments against traits blobs apply here. With the traits classes, the concepts can be defined in terms of proper specialization of the traits classes. (Your support for Standard containers can be provided via library-supplied specializations so the containers can be documented as satisfying the Range concepts because of those specializations.) The existing interface--boost::begin(), etc.--can be rewritten to use the traits class interface. The naming of the traits classes, which become the customization points, remains something of an issue however. They could be single_pass_range_traits and forward_range_traits in the boost namespace, but that hardly fits the goal of enabling their adoption as widely accepted, public domain, customization points. Putting them in the global namespace may be too presumptuous. I don't know what the right answer is. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
If we fix ADL for C++0x, I would favor what the proposal currently says (I think the fact that you can forget using std::begin; is of minor importance).
If we don't fix ADL in C++0x, then we might consider (1) ADL customization points via range_begin()/range_end()/range_size() or (2) go for a class like range_traits<T>::begin()...ect.
ADL is going to get "fixed" by concept support, and not by Herb's idea, FWIW. Just my opinion, but well-founded and I have no time to go into details now (sorry). So if you think the shape of C++0x should be a factor in this discussion, you should be thinking about how all this relates to the concepts proposals. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Thorsten Ottosen <nesotto <at> cs.aau.dk> writes:
If we fix ADL for C++0x,
[snip]
ADL is going to get "fixed" by concept support, and not by Herb's idea, FWIW. Just my opinion, but well-founded and I have no time to go into details now (sorry). So if you think the shape of C++0x should be a factor in this discussion, you should be thinking about how all this relates to the concepts proposals.
All I can gather from the concept proposals on this issue is that ADL will *not* happen at all for the new non-dependent parameters. br -Thorsten

From: Thomas Witt <witt@acm.org>
David Abrahams wrote:
Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
Something like begin(x), if I'm the author of a container type, seems useful to me!
Yes begin() but not boost_range_begin()
It might be used by anyone wanting to write composable generic algorithms over sequences. One of the problems with the STL interface is that composing the algorithms is more cumbersome than it should be.
I am all for a range concept. I am at odds with the proposed solution for that.
I understand your point that exposing "boost_range_begin" as a customization point is rather ugly and won't be a public domain customization point. If it were named "range_begin," it could well become public domain. The problem arises if there are competing ideas for the interface or usage of such a name. I like the idea that boost::begin() does the work. It's easy to get in the habit of writing boost::begin() (or std::begin() should it be standardized), whereas it is harder to be in the habit of writing the using declaration plus an unqualified begin(). Thus, the current solution, in the abstract at least, seems like a good one. How, then, do you propose to provide these points of customization such that they help the library user avoid mistakes, have reasonable chance to be used more widely than Boost, but don't have the problems you attribute to "boost_range_begin()?" -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
The second half of that sentence sounds rather sweeping and extreme.
Please don't read to much into it. It's just that I have serious doubts.
It me took about 10 re-readings of this email to guess that us == the library developer and the component is, e.g. a pre-existing component like std::vector or builtin arrays. Did I get that right?
Sorry for not being more clear. You are basically right. Us means boost or boost::range in particular.
In order to have our cake and eat it we move our interface in our own namespace where we are free to do whatever we see fit.
I don't see it that way. If we're using the strategy discussed here -- which I'm not claiming is ideal -- we do that part just to keep other authors of generic code from having to remember to write ugly using declarations.
I would not want to describe using declarations as ugly. They clearly describe the intent of the programmer.
Yes, at a very low level. That's sort of like saying that node<T>* n = new node<T>(x, &mylist.front()); mylist.set_head_ptr(n); clearly expresses the intent of the programmer. IMO mylist.push_front(x); is a lot clearer and more expressive.
Though I do agree that forgetting about them is a very likely cause of error.
In other words, aside from that ugly using declaration, which is needed to make the interface truly generic, the interface lives in the "global ADL space."
The point that I am concerned with is _which_ interface adds up in the "global ADL space". IIUC the ADL hooks boost_range_begin etc. end up there. AFAICS these are not meant to be used by a user of a library, are they?
Unfortunately, no. But what can you do when such a major compiler as GCC makes it impossible to use a simpler name without stomping on another Boost library?
I guess if you view the using declaration as a kind of namespace qualification, you could see this as moving the interface into our namespace.
I was talking about functions like begin(), end() those end up in the boost namespace, don't they.
Yeah, they sure do, and -- at least from one valid perspective -- appropriately so. The begin semantics you're interested in when you call boost::begin(x) are those that have been defined by Boost.Range, and not some other idea of what "begin" means. I've been consulting with some very smart quant guys this week in a Swiss bank, and when we talked about the fact that swap has become blessed as a global ADL customization point they got very worried, because in the financial world, "swap" has a very different meaning.
So what does that mean for the other libraries out there? They have to clutter their interface with these hooks that are meant only for communication with the boost library and that are useless if the user of the other library does not use boost.
Not if you buy Peter Dimov's argument that many customization points become "public domain" and are no longer the property of any library. Read that part of the thread carefully. Once people start supplying begin() hooks, other library authors may start making interfaces that work with Boost.Range compatible types.
I've read through his argument at least twice in the past. That being said I might still not get the point. I do agree with the names becomming public domain. What I disagree with is that names like boost_range_begin are suitable for this.
What should the name be?
They will likely always be warts in an interface.
Why do you think of them as part of the interface, if, as you say, they're not intended to be used directly by end-users? The fact that they appear in the header file, or even in the same namespace with public interface elements, doesn't automatically make them interface elements. To be an interface element, a function must be documented as such. These hooks have the same role as a class template specialization in the library namespace would.
Apart from the fact that the hook strategy is unlikely to scale well (how many potentially useless hooks do you want in your library?)
In my library? Useless? I'm confused. Am "I" the author of a library component who is trying to get it to work with other libraries like Boost.Range?
Just assume you are.
Well, it's not useless if it makes my component work with BOOST_FOREACH. So I still don't understand your question.
Something like begin(x), if I'm the author of a container type, seems useful to me!
Yes begin() but not boost_range_begin()
Something that allows generic code to use my class as a range doesn't seem useless to me either.
It might be used by anyone wanting to write composable generic algorithms over sequences. One of the problems with the STL interface is that composing the algorithms is more cumbersome than it should be.
I am all for a range concept. I am at odds with the proposed solution for that.
Clearly. All I'm hearing from you are complaints, without a proposal for an alternative that works. The problem of building customization points for generic C++ libraries has no really satisfying solution at the moment. Any approach we choose will be a compromise. It isn't clear to me why this particular compromise should be deemed worse than any of the others. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
The problem of building customization points for generic C++ libraries has no really satisfying solution at the moment. Any approach we choose will be a compromise. It isn't clear to me why this particular compromise should be deemed worse than any of the others.
I have a similar choice to make regarding customization points for Iostreams. Currently, the user can customize boost::iostreams::read(), boost::iostreams::write(), etc. for a type T by specializing the class template boost::iostreams::operations. I'm considering switching to an ADL-based mechanism in which users define functions boost_iostreams_read, boost_iostreams_write, etc. I haven't documented the customization mechanism yet because I'm waiting for agreement on the design of Boost.Range and I think it would be beneficial to have a uniform policy. Personally, I don't really care which solution is adopted, but I'm eager for the problem to be resolved soon so library users can take advantage of customization. Jonathan

David Abrahams wrote:
Unfortunately, no. But what can you do when such a major compiler as GCC makes it impossible to use a simpler name without stomping on another Boost library?
Point taken.
Yeah, they sure do, and -- at least from one valid perspective -- appropriately so. The begin semantics you're interested in when you call boost::begin(x) are those that have been defined by Boost.Range, and not some other idea of what "begin" means.
Given this "narrow" (no belittlement intended) view I tend to agree. Is it correct to say that this view does not lead to the idea of "public domain" customization points as expressed below?
Not if you buy Peter Dimov's argument that many customization points become "public domain" and are no longer the property of any library. Read that part of the thread carefully. Once people start supplying begin() hooks, other library authors may start making interfaces that work with Boost.Range compatible types.
I've read through his argument at least twice in the past. That being said I might still not get the point. I do agree with the names becomming public domain. What I disagree with is that names like boost_range_begin are suitable for this.
What should the name be?
A suitable name for a "public domain" customization point as I see it would be begin(). That being said it's not necessarily the right solution in this case.
They will likely always be warts in an interface.
Why do you think of them as part of the interface, if, as you say, they're not intended to be used directly by end-users? The fact that they appear in the header file, or even in the same namespace with public interface elements, doesn't automatically make them interface elements. To be an interface element, a function must be documented as such. These hooks have the same role as a class template specialization in the library namespace would.
As much as I sympathize with this view. My experience shows this is not the way it works in practice.
Apart from the fact that the hook strategy is unlikely to scale well (how many potentially useless hooks do you want in your library?) In my library? Useless? I'm confused. Am "I" the author of a library component who is trying to get it to work with other libraries like Boost.Range?
Just assume you are.
Well, it's not useless if it makes my component work with BOOST_FOREACH. So I still don't understand your question.
When I say 'useless' I mean useless in this specific usage scenario. I.e. somebody uses library A but not boost. In this case boost hooks in library A are 'useless'.
Clearly. All I'm hearing from you are complaints, without a proposal for an alternative that works.
My main concern is that the proposed solution using specific ADL hooks does not scale. I would prefer an approach where a Range concept is specified that any type can model without ever referring to boost. The role of boost.range would be to provide a range interface for often used types that otherwise won't have one.
The problem of building customization points for generic C++ libraries has no really satisfying solution at the moment. Any approach we choose will be a compromise.
Agreed.
It isn't clear to me why this particular compromise should be deemed worse than any of the others.
The reason I think it's worse is that I think it does not scale. That being said, I'll stop complaining now and we'll see what time will tell. It might well prove me wrong. Thomas

Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
Yeah, they sure do, and -- at least from one valid perspective -- appropriately so. The begin semantics you're interested in when you call boost::begin(x) are those that have been defined by Boost.Range, and not some other idea of what "begin" means.
Given this "narrow" (no belittlement intended) view I tend to agree. Is it correct to say that this view does not lead to the idea of "public domain" customization points as expressed below?
Yes. Whether or not that is the best way to look at things, I am still undecided. Interestingly I was just consulting with some people doing high-performance statistical simulation for a bank, and they were very concerned about the impact of ADL on the name "swap," since that has a very specific meaning in the world of finance.
Not if you buy Peter Dimov's argument that many customization points become "public domain" and are no longer the property of any library. Read that part of the thread carefully. Once people start supplying begin() hooks, other library authors may start making interfaces that work with Boost.Range compatible types. I've read through his argument at least twice in the past. That being said I might still not get the point. I do agree with the names becomming public domain. What I disagree with is that names like boost_range_begin are suitable for this.
What should the name be?
A suitable name for a "public domain" customization point as I see it would be begin().
If you want that as you've written it, without qualification, the language puts you in conflict with your own desire to avoid adding "potentially useless" "warts" into the interface. Maybe you don't view that as a wart, but begin(x) is useless to anyone who isn't aware of the concept it is "hooking."
That being said it's not necessarily the right solution in this case.
Right.
They will likely always be warts in an interface.
Why do you think of them as part of the interface, if, as you say, they're not intended to be used directly by end-users? The fact that they appear in the header file, or even in the same namespace with public interface elements, doesn't automatically make them interface elements. To be an interface element, a function must be documented as such. These hooks have the same role as a class template specialization in the library namespace would.
As much as I sympathize with this view. My experience shows this is not the way it works in practice.
I know that some people just look at the source anyway. At least functions found through ADL can be separated from the class declaration and explicitly marked in comments as "not for user consumption."
Apart from the fact that the hook strategy is unlikely to scale well (how many potentially useless hooks do you want in your library?) In my library? Useless? I'm confused. Am "I" the author of a library component who is trying to get it to work with other libraries like Boost.Range?
Just assume you are.
Well, it's not useless if it makes my component work with BOOST_FOREACH. So I still don't understand your question.
When I say 'useless' I mean useless in this specific usage scenario. I.e. somebody uses library A but not boost. In this case boost hooks in library A are 'useless'.
So you'd prefer ADL hooks without 'boost' in their names? How does one get the default behavior for builtin arrays without including boost?
Clearly. All I'm hearing from you are complaints, without a proposal for an alternative that works. My main concern is that the proposed solution using specific ADL hooks does not scale.
Can you say more about that? I don't understand what you mean.
I would prefer an approach where a Range concept is specified that any type can model without ever referring to boost. The role of boost.range would be to provide a range interface for often used types that otherwise won't have one.
Can you suggest such a concept definition? Certainly, something like supports begin(x) is never going to be valid by itself for builtins. You need the using declaration, and then you need to know where to get it. Hmm, I suppose one possibility is that the requirement is supports begin(x) and then the builtins and the standard containers are in principle not Ranges. Of course, the user can make them into ranges by declaring the appropriate begin(s) in his own namespace, either by writing his own definitions or by #including the right boost headers and adding using declarations.
The problem of building customization points for generic C++ libraries has no really satisfying solution at the moment. Any approach we choose will be a compromise.
Agreed.
It isn't clear to me why this particular compromise should be deemed worse than any of the others.
The reason I think it's worse is that I think it does not scale.
Worse than what?
That being said, I'll stop complaining now and we'll see what time will tell. It might well prove me wrong.
I'd rather hear some real suggestions ;-) -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Thomas Witt <witt@acm.org> writes:
David Abrahams wrote:
It isn't clear to me why this particular compromise should be deemed worse than any of the others.
The reason I think it's worse is that I think it does not scale.
Worse than what?
That's precisely what I wondered when I read his reply. Unless I missed something, Thomas has yet to offer an alternative, yet uses comparative language. It's easy to decry the real in light of the unspoken ideal.
That being said, I'll stop complaining now and we'll see what time will tell. It might well prove me wrong.
I'd rather hear some real suggestions ;-)
Yes. If there are alternatives that have been forgotten or that can be presented for consideration, we need to hear about them. Both Boost.Range and Boost.Iostream will benefit. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart <at> sig.com> writes:
From: David Abrahams <dave <at> boost-consulting.com>
Thomas Witt <witt <at> acm.org> writes:
That being said, I'll stop complaining now and we'll see what time will tell. It might well prove me wrong.
I'd rather hear some real suggestions
Yes. If there are alternatives that have been forgotten or that can be presented for consideration, we need to hear about them. Both Boost.Range and Boost.Iostream will benefit.
I tend to favor the shorter, less wart-like names range_begin range_end range_size It seems to play nicely with range_iterator etc Could we agree to move the boost_ prefix? I think the amount of costomization points that are needed is so small that boost_ qualification is not needed. -Thorsten

I've been thinking about this issue and wonder if the following wouldn't solve the problem neatly for both conforming and non-conforming compilers: // whatever/foo.h namespace whatever { class foo { ... }; // for conforming compilers unspecified begin(foo const &) { ... }; ... } // for non-conforming compilers namespace boost { namespace range { using whatever::begin; } } // boost/range/begin.hpp namespace boost { inline template <T> unspecified begin(T const & range_i) { using boost::range::begin; return begin(range_i); } } If the author of foo has no other need for begin(), then whatever/foo.h could be done like this instead: // whatever/foo.h namespace whatever { class foo { ... }; } namespace boost { namespace range { unspecified begin(foo const &) { ... }; } } If whatever::foo belongs to a user of Boost.Range that only uses conforming compilers, then foo.h only needs to be: // whatever/foo.h namespace whatever { class foo { ... }; unspecified begin(foo const &) { ... }; ... } If whatever::foo provides get_begin() instead, for whatever reason, then foo.h becomes: // whatever/foo.h namespace whatever { class foo { ... }; unspecified get_begin(foo const &) { ... }; ... } namespace boost { namespace range { inline unspecified begin(foo const & foo_i) { return get_begin(foo_i); } } } This approach provides a number of benefits: - we can use the most obvious name, boost::range::begin, for the point of customization - benefits from ADL when supported - Types in namespace std or built-in types (pointers) can be retrofitted in boost::range and will work like everything else (though you must include the appropriate header to get the required boost::range::begin() overload) - flexibility for those wishing to support Boost.Range - non-conformance code can be excised easily later Did I miss something? -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart@sig.com> writes:
I've been thinking about this issue and wonder if the following wouldn't solve the problem neatly for both conforming and non-conforming compilers:
// whatever/foo.h namespace whatever { class foo { ... };
// for conforming compilers unspecified begin(foo const &) { ... }; ... }
// for non-conforming compilers namespace boost { namespace range { using whatever::begin; } }
// boost/range/begin.hpp namespace boost { inline template <T> unspecified begin(T const & range_i) { using boost::range::begin; return begin(range_i); } }
Careful. Many of the problems you can run into with dispatching only show up in the case where the definition of begin you want to be selected *follows* the template that uses it.
If the author of foo has no other need for begin(), then whatever/foo.h could be done like this instead:
// whatever/foo.h namespace whatever { class foo { ... }; }
namespace boost { namespace range { unspecified begin(foo const &) { ... }; } }
No it can't. That begin() will only be found by ordinary lookup, looks *backwards* from the point of definition of the template in which it is used. That's the problem I referred to above.
If whatever::foo belongs to a user of Boost.Range that only uses conforming compilers, then foo.h only needs to be:
// whatever/foo.h namespace whatever { class foo { ... };
unspecified begin(foo const &) { ... }; ... }
If whatever::foo provides get_begin() instead, for whatever reason, then foo.h becomes:
// whatever/foo.h namespace whatever { class foo { ... };
unspecified get_begin(foo const &) { ... }; ... }
namespace boost { namespace range { inline unspecified begin(foo const & foo_i) { return get_begin(foo_i); } } }
Once again, no, for the same reason cited above.
This approach provides a number of benefits:
- we can use the most obvious name, boost::range::begin, for the point of customization - benefits from ADL when supported - Types in namespace std or built-in types (pointers) can be retrofitted in boost::range and will work like everything else (though you must include the appropriate header to get the required boost::range::begin() overload) - flexibility for those wishing to support Boost.Range - non-conformance code can be excised easily later
Did I miss something?
Two-phase name lookup. 14.6.4 Dependent name resolution [temp.dep.res] In resolving dependent names, names from the following sources are considered: — Declarations that are visible at the point of definition of the template. — Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context. Unless you arrange for boost::range to be an associated namespace of foo, you're setting up dangerous #include order dependencies. -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
I've been thinking about this issue and wonder if the following wouldn't solve the problem neatly for both conforming and non-conforming compilers:
// whatever/foo.h namespace whatever { class foo { ... }; // for conforming compilers unspecified begin(foo const &) { ... }; // for non-conforming compilers namespace boost { namespace range { using whatever::begin; } } // boost/range/begin.hpp namespace boost { inline template <T> unspecified begin(T const & range_i) { using boost::range::begin; return begin(range_i); } }
Careful. Many of the problems you can run into with dispatching onlyshow up in the case where the definition of begin you want to beselected *follows* the template that uses it.
Right. That was the case you'd been discussing and I missed its application here. However, if the author of whatever::foo defines, as a matter of convention or coercion,* begin() in namespace boost::range in the same header as foo, and doesn't invoke it before it's definition, then the problem order won't occur.
Did I miss something?
Two-phase name lookup. 14.6.4 Dependent name resolution [temp.dep.res]
LOL It seems you like quoting that now that you've found it! I do know about two-phase lookup, but I don't have to deal with it often so I don't always recognize the trouble it can cause a solution like this. Nevertheless, won't the problem only occur for non-conforming compilers and only when the definition of boost::range::begin(foot const &) is in a separate header (provided the author/maintainer of foo follows the prescribed course when it's in the same header)?
Unless you arrange for boost::range to be an associated namespace offoo, you're setting up dangerous #include order dependencies.
Of course. * It would be documented as the only correct thing to do were this an accepted workaround for non-conforming compilers. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
I've been thinking about this issue and wonder if the following wouldn't solve the problem neatly for both conforming and non-conforming compilers:
// whatever/foo.h namespace whatever { class foo { ... }; // for conforming compilers unspecified begin(foo const &) { ... }; // for non-conforming compilers namespace boost { namespace range { using whatever::begin; } } // boost/range/begin.hpp namespace boost { inline template <T> unspecified begin(T const & range_i) { using boost::range::begin; return begin(range_i); } }
Careful. Many of the problems you can run into with dispatching onlyshow up in the case where the definition of begin you want to beselected *follows* the template that uses it.
Right. That was the case you'd been discussing and I missed its application here. However, if the author of whatever::foo defines, as a matter of convention or coercion,* begin() in namespace boost::range in the same header as foo, and doesn't invoke it before it's definition, then the problem order won't occur.
Did I miss something?
Two-phase name lookup. 14.6.4 Dependent name resolution [temp.dep.res]
LOL It seems you like quoting that now that you've found it!
It's easy to find when it's in a recent Boost posting :)
I do know about two-phase lookup, but I don't have to deal with it often so I don't always recognize the trouble it can cause a solution like this.
Nevertheless, won't the problem only occur for non-conforming compilers and only when the definition of boost::range::begin(foot const &) is in a separate header
No, it has nothing to do with *definitions* and everything to do with declarations. That function could be defined in a source file.
(provided the author/maintainer of foo follows the prescribed course when it's in the same header)?
No, it will occur anytime boost/range/begin.hpp is included before whatever/foo.h.
Unless you arrange for boost::range to be an associated namespace offoo, you're setting up dangerous #include order dependencies.
Of course.
* It would be documented as the only correct thing to do were this an accepted workaround for non-conforming compilers.
It's not much good if it breaks the conforming compilers. -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
Nevertheless, won't the problem only occur for non-conforming compilers and only when the definition of boost::range::begin(foot const &) is in a separate header
No, it has nothing to do with *definitions* and everything to do with declarations. That function could be defined in a source file.
D'oh, sorry.
(provided the author/maintainer of foo follows the prescribed course when it's in the same header)?
No, it will occur anytime boost/range/begin.hpp is included before whatever/foo.h.
I just looked at boost/range/begin.hpp and I see what you mean. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Thorsten Ottosen <nesotto@cs.aau.dk> writes:
You also seem to be forgetting the Range concepts that you yourself documented here: http://boost.org/libs/range/doc/range.html. In the concepts, the calls to begin()/end() are REQUIRED to be qualified.
Let me clarify. Eric, you seem to be saying that it's inconsistent for the "begin" extension mechanism to be "add a begin to be found via ADL in the same namespace as the Range type" and for the interface for calling those begin to be "boost::begin(x)." There's no inherent inconsistency there. It's possible to implement such a mechanism. However, I don't think that's what Thorsten implemented.
well, the boost:: qualification was added for 1.33 because Dave wanted that to happen.
Please, take responsibility for your own actions. If I didn't convince you that there was a problem with the previously specified requirement and that the change was more consistent with the existing code, you shouldn't have made it.
I belive the problem was that without boost:: qualification, standard containers were not conforming to a range concept. Somebody suggested the requirement
using boost:begin; begin(r);
instead. Dave rejected it.
I'm pretty sure I didn't "reject" it. I probably pointed out that it's inconvenient and difficult for users. I'd appreciate any pointer you have to what I did say. Anyway, how this is done is not up to me alone.
If that is wrong afterall, then the whole area of specifying concepts for free- standing functions is a mess. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What does that phrase mean? -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Thorsten Ottosen <nesotto@cs.aau.dk> writes:
You also seem to be forgetting the Range concepts that you yourself documented here: http://boost.org/libs/range/doc/range.html. In the concepts, the calls to begin()/end() are REQUIRED to be qualified.
Let me clarify. Eric, you seem to be saying that it's inconsistent for the "begin" extension mechanism to be "add a begin to be found via ADL in the same namespace as the Range type" and for the interface for calling those begin to be "boost::begin(x)." There's no inherent inconsistency there. It's possible to implement such a mechanism.
*blink* It is? If boost::begin() makes an unqualified call to begin(), then wouldn't it just end up recursing forever? You must have some subtlety in mind that is eluding me. <thinking...> Oh, wait. Are you referring to something like this: namespace super_private { template<typename T> some-return-type begin_impl(T &t) { return begin(t); } } namespace boost { template<typename T> some-return-type begin(T &t) { return super_private::begin_impl(t); } } The unqualified call to begin() in namespace super_private will not find the one in namespace boost, hence no infinite recursion. But this doesn't quite work because if type T has boost as an associated namespace, it *will* consider the begin() in namespace boost, and we're back to infinite recursion. Regardless, it was gcc's interpretation of name-lookup that put the kibosh on unqualified calls to begin() and end(). -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric@boost-consulting.com> writes:
Oh, wait. Are you referring to something like this:
namespace super_private { template<typename T> some-return-type begin_impl(T &t) { return begin(t); } }
namespace boost { template<typename T> some-return-type begin(T &t) { return super_private::begin_impl(t); } }
The unqualified call to begin() in namespace super_private will not find the one in namespace boost, hence no infinite recursion. But this doesn't quite work because if type T has boost as an associated namespace, it *will* consider the begin() in namespace boost, and we're back to infinite recursion.
Something like the following is what I had in mind. Your thing is probably almost equivalent, but a little harder to read, and doesn't support a default implementation: namespace boost { namespace default_ // where the default implementation of begin { // lives. template <class S> typename range_result_iterator<S>::type begin(S& s) { return s.begin() } } namespace adl // where begin can be called without qualification { // thus avoiding infinite recursion template <class S> typename range_result_iterator<S>::type begin_(S& x) { using default_::begin; return begin(x); } } template <class T> typename range_result_iterator<S>::type begin(S& x) { return adl::begin_(x); } template <class T> typename range_result_iterator<S const>::type begin(S const& x) { return adl::begin_(x); } }
Regardless, it was gcc's interpretation of name-lookup that put the kibosh on unqualified calls to begin() and end().
Right, now I remember. What a shame. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (9)
-
Daryle Walker
-
David Abrahams
-
Eric Niebler
-
Joel Eidsath
-
Jonathan Turkanis
-
Pavol Droba
-
Rob Stewart
-
Thomas Witt
-
Thorsten Ottosen