Boost logo

Boost Users :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2005-03-05 17:42:13


David Abrahams wrote:
> "Peter Dimov" <pdimov_at_[hidden]> writes:
>
>> The placeholders do have the same meaning; they are an example of a
>> de-facto standard.
>> The conflicts that exist are (a) resolvable, (b) caused by factors
>> that do not apply to our discussion.
>
> Not exactly. I'm thinking of MPL and bind actually. They don't have
> the same meaning, only a similar one, and they can't be unified. It's
> the very "standard-ness" that prompted MPL to use the same names in a
> very similar -- but not identical -- way.

Well, the problem here is that (a non-macro) _1 can't be a value and a type
at the same time, not that the two _1s express different notions.

>> And for the record, Lambda's placeholders cause exactly the same ODR
>> violations, although I'm not sure whether "cause" is the proper term.
>
> You're right, "lead to" would be more accurate. I hope you understand
> the following:
>
> 1. I'm arguing about this because I want to end up convinced of
> something, and soon -- I'll need to choose customization techniques
> imminently -- so thank you for engaging me on this.
>
> 2. I only brought up the ODR thing as a piece of evidence that
> problems don't neccessarily get resolved quickly

Yes, I understood (2). With regard to (1), I'm not sure that I will convince
you of something. My own position is that one should pick overloading or
specializations based on whether exact type matching better fits... no, make
that "is essential for" the design. A secondary concern that sometimes
outweighs the exact type matching advantage is the syntactic penalty
introduced by specialization-based customization points because (a)
functions can't be partially specialized, (b) specializations need to be
defined in their namespace.

Potential identifier conflicts come a distant third to me. I understand the
problem and the implications, I just don't give it that much weight.

>>> That's why generic library writers have developed a guideline to
>>> write concept requirements that don't depend on member functions.
>>
>> Maybe they did. My point is that I never see anti-v.begin()
>> campaigns;
>
> I do. Why do you think Boost.Range is using free functions? Not
> putting member functions in concept requirements is a well-known
> generic programming guideline. For years even Scott Meyers has been
> saying that "interfaces should be extended with free functions," which
> isn't viewed through the generic customization-point lens, but means
> the same thing.

Scott Meyers's point is that operations that can be expressed in terms of
the public interface should be non-friends, which is not the same thing at
all.

The primary problem with member functions (and types) as a concept
requirement is that they can't be retrofitted to a type. The fact that a
type can't have two members with the same name is a secondary concern, which
goes relatively unnoticed compared to the "ADL problem".

>> and the debate is whether not having to
>> worry is a good thing. Customization points are very important and
>> need to be treated with caution.
>
> What kind of caution, if they are associated with a namespace, and
> why? We could use std::iterator_traits as an example.

Once these customization points become well-known, more and more types start
to conform to them, and more and more other libraries start to depend on the
customization point. This effectively means that this particular
customization point can no longer be changed by the original author, because
this will break too much code. In some cases it even changes the meaning of
the customization point (usually to a subset of the original).

>> Once they become de-facto standards, the library no
>> longer owns them, even if they are in the library's own namespace.
>
> Even if I understood what you meant by "the library no longer owns
> them, even if they are in the library's own namespace," what are the
> implications of that? Is std::iterator_traits an example of a
> de-facto standard customization point in a namespace?

The implication of that is that customization points need to be minimized
and their semantics - carefully chosen. They are even more important than
the (rest of the) public interface of the library, because they can affect
and shape the code of people that don't even use this particular library.

I see potential identifier collisions as just one of the things can go
wrong, not as the only danger.

To take iterator_traits<X>::reference as an example, a carefully chosen
meaning for it would probably be "the return type of the expression *x",
which isn't really domain specific. Once libraries start using it you can no
longer turn back and redefine it as something that only makes sense for
iterators. difference_type, the return type of the expression x - y, also
makes sense in a non-iterator context.

> There are clearly some (just a very few, IMO) customization points
> like swap that are really intrinsic. They have to do with the
> semantics of what Stepanov and friends are calling "value types." It
> seems to me as though the rest are associated with a particular
> domain, and it's appropriate to name that domain.

I can agree that most customization points are associated with a particular
domain, but this doesn't mean that they are associated with a particular
library from that domain. In my experience - which is not that extensive -
well thought out customization points can be, and are, used outside of the
context of the library that created them. They are a part of a domain to the
extent that the type they are associated with is part of that domain, not
because of the originating library.

Example: I can use intrusive_ptr_add_ref to increment the reference count of
a type without ever including <boost/intrusive_ptr.hpp>. It is a way for a
type to advertize itself as intrusively counted in general, not as suitable
for use with boost::intrusive_ptr in particular. If every intrusively
counted smart pointer has a different customization interface, users are
protected from collisions, but need to mark their types as intrusively
counted multiple times, to satisfy each library. They would certainly prefer
the smart pointer authors getting together, so to speak, and settling on one
interface.

But you'll note that the function is not named add_ref, after all. ;-)

>> OK; so what alternatives do we have?
>>
>> 1. lib1::numeric_traits<X>::zero( x ); // somewhat unwieldy
>> 2. lib1::zero( x ); // syntactic sugar for the above
>> 3. lib1_zero( x );
>>
>> What, exactly, are the advantages of #2 over #3?
>
> #3 invokes ADL.

Yes, yes, but what are the advantages of #2 over #3? ;-)


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net