Boost logo

Boost Users :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2005-03-06 19:16:36


David Abrahams wrote:
> "Peter Dimov" <pdimov_at_[hidden]> writes:
>
>> We haven't seen identifier clashes in practice either. ;-) No, g++
>> doesn't count.
>
> So maybe we should put everything in the global namespace?

It is actually not _that_ unreasonable to put ADL customization point
defaults in the global namespace and dispense with the "using" idiom.
Moderately unreasonable, maybe. :-)

>> Consider get_pointer. It was once "the protocol that mem_fn uses
>> when faced with a smart pointer" and was not required to return a
>> raw pointer. Now it's "the way to obtain a raw pointer from a
>> pointer-like object" and is no longer used by mem_fn (in its TR1
>> incarnation). If this isn't an example of how the customization
>> point is not owned by the library, I don't know what is. ;-)
>
> I don't know if citing a library evolution for which you hold all the
> responsibility really counts. ;-)

I am not responsible for the evolution of get_pointer. tr1::mem_fn doesn't
even use it.

>>>> 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.
>>>
>>> So does the rest of the public interface.
>>
>> The rest of the public interface does not affect non-users.
>
> I don't get it. Even if author A has to glom a giant ugly fribsackle
> onto his type so that it can satisfy library B's concept requirements,
> author C can use A's type with B's functions without ever having to
> confront the fribsackle herself.

Um, I beg your pardon? ;-)

Library B defines requirements. Author A satisfies them by defining a
function or specializing a class template. Author C uses A's public
interface, which _includes_ the function or the specialization. Ergo, B's
customization points affect C, who does not use B and may not even have
heard of it.

>>> I'm not sure about the weight you're giving to these things.
>>> People supply these customization points at most once per
>>> component, and it's a "private matter between two consenting
>>> programmers." It doesn't need to affect other users of either
>>> programmers' code -- unless of course you have to worry about
>>> future name collisions ;-)
>>
>> A customization point is a public protocol that needs to be followed
>> by types in order to advertize a certain property or operation. I
>> don't see how it can be a private matter. Library A introduces
>> customization point f(x), library B defines f(y) for its type Y,
>> library C uses f(x) in order to benefit from the fact that there are
>> already types Y that support the protocol. These three libraries are
>> totally separate.
>
> You mean that the customization point is automatically up-for-grabs
> for any library to use, as soon as you advertise that it makes a type
> work with some particular library.
>
> Well, I guess that's the difference between specialization and
> overloading. With specialization, you sorta need to have one of that
> library's headers around to get the declaration.

It doesn't matter, really. If the customization point is useful, it will be
used. The customization points define the interface of a domain-specific
class, to use your terminology. This interface will then be picked up by
others.

Of course you _could_ define the customization point in such a way so that
its usefulness to others is minimized.

>>> Uh, wait. iterator_traits was not well designed, I'll grant you,
>>> but all the same, I'd call using iterator_traits<X>::reference to
>>> mean "the return type of *x" a definite no-no. Just think about the
>>> consequences of all those other type names in iterator_traits.
>>
>> Well, what else could it mean? :-)
>
> Leaving aside the fact that in real life it is underspecified and used
> inconsistently, it could mean "the return type of *x where x is an
> iterator."

Sure, it could mean that. An example of deliberately crippling the
customization interface so that only iterators can use it, with no visible
gain. Maybe, just maybe, you could use such a definition, combined with an
empty primary template and SFINAE, to drop overloads based on iterator-ness,
but the iterator category is better suited for this.

>>>> difference_type, the return type of the expression x - y, also
>>>> makes sense in a non-iterator context.
>>>
>>> Yeah, but this is clear abuse. If it wasn't clear, we'd have seen
>>> it done over and over. I haven't ever seen it.
>>
>> Why is it a clear abuse?
>
> Come, come, my man! Because of the way iterator_traits is named,
> first of all. Also because of the way the primary template is
> defined. The normal way to satisfy iterator_traits is to inject nested
> typedefs. If you want to use it to get difference_type for all
> subtractable types, you have to add a bunch of irrelevant typedefs,
> including iterator_category. And specializing iterator_traits and
> leaving out a type is almost as weird.

Eh, of course one wouldn't use iterator_traits<>::difference_type in such a
way, but I wouldn't expect you to define this kind of a customization point.
You would define difference_type<X>::type, probably, to which your
objections do not apply.

Anyway, my point was just that good customization points transcend a
specific library, if not the whole domain. iterator_traits isn't an example
of a good customization point.

>> Right. There is nothing that links get_pointer or swap to a
>> particular library. Whether get_pointer or swap makes sense for a
>> type X is determined solely by X, not by the library that happened
>> to introduce get_pointer or swap.
>
> No, it's also determined by the specification of requirements for
> get_pointer and swap. These specifications came with a particular
> library.

Yes, of course. But the specifications are no longer linked to that library.
The library could even go away, or drop the requirement. If the requirement
is useful, it will survive on its own.

This is ad-hoc interface building. We decide that it's useful for
pointer-like types to provide an uniform way to obtain a raw pointer, and we
define it. The library that defined it first does not matter anymore.

> get_pointer, in particular, seems like it could easily collide someday.

The surefire way to prevent such collisions is to standardize it. ;-)

>> Right. But "introduced by" and "owned by" are not the same thing.
>> Once it's introduced and adopted, the genie is out of the bottle.
>
> What is your meaning for "owned by?"

Controlled by. Something that you can change the specification of. Something
that you can change the name of. Something you can change the namespace of,
if you will.

>> I don't like domain tags much. Seems like a fairly high price to pay
>> for dubious returns.
>
> What's the cost, overall? You may "need" such an interface
> dispatching function anyway, to deal with builtin types.

The cost is the extra parameter and the dependency on the tag header, and
with the utility function infrastructure in place, a class template instead
of a tag isn't very far.

>> I only use such tags when I need to inject a namespace for
>> ADL purposes via the tag.
>
> Example, please?

I'll need to think about it. A quick scan doesn't reveal anything similar,
so I might have dropped this technique. I _think_ that it had to do with the
fact that one can't put overloads into std. I had an ADL interface f(x, y),
where the x'es came from different namespaces, and I needed to define f( _,
std::vector ). I see in the code that I have "solved" this problem by
putting all x'es in the same namespace. What a hack. ;-) The other option
was to add a tag and put the std:: related overloads in that tag's
namespace.

Maybe I'll just bite the bullet and start putting overloads in std instead
one day.


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