"Peter Dimov"
David Abrahams wrote:
"Peter Dimov"
writes: David Abrahams wrote:
"Peter Dimov"
writes: 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.
You can do it with a macro??
Yes, the famous "#define _1 arg1()" trick by Daniel Wallin.
I thought it might be something like that.
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.
IIUC, you're saying that if exact type matching is not _essential_, you'd never use specialization for customization? Wow. That would make it pretty rare. That leaves only cases where derived classes commonly need to use the default (primary template) customization when customizations have been made for their bases.
More likely, when derived classes need to fail compilation when a base is customized but a derived is not.
Okay. Those are two of a kind.
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).
Okay, I understand that in theory, but have we seen it in practice?
We haven't seen identifier clashes in practice either. ;-) No, g++ doesn't count.
So maybe we should put everything in the global namespace?
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. ;-)
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.
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.
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."
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.
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.
Maybe. You have examples?
get_pointer? intrusive_ptr_*?
Okay.
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. get_pointer, in particular, seems like it could easily collide someday.
Example: I can use intrusive_ptr_add_ref to increment the reference count of a type without ever including
. 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. Great, but it depends on the type implementing a particular protocol introduced by your library.
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?"
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.
Yes. It's better if they all choose a common interface. That interface should be associated with a namespace.
If they all choose a common interface, why should it be protected by a namespace? The identifier is now a de-facto standard. It doesn't need protection.
Because people devlop in different domains. I can _easily_ imagine some person writing a boost::addressof with the name get_pointer. If they develop a big application in a single namespace with their own smart pointers and put this get_pointer in that namespace, they might have problems using Boost. Of course then there's the argument that people really ought to be making nice little granular namespaces to encapsulate types and their ADL customizations. At least then, a case like the above won't occur (a customization point colliding with a general utility function). That only leaves customization points to collide with one another, and only when a type is explicitly adapted to two libraries. In that case I guess the author can resort to some kind of wrapper.
Ah, I see what you're getting at now. Maybe ADL customization points prefixed by the library name are the way to go, at least until the language gets better.
On a more practical note, I think that making the identifier "sufficiently unique" is enough. boost_add_ref would be fine too, standardization notwithstanding.
Yeah, probably.
Is that better or worse than the use of "domain tags?" You poked a hole in that technique a few years ago, but I don't remember how big a hole. I mean:
namespace lib { struct tag {};
template <class T> T zero(T a) { zero(tag,a); } // interface };
namespace my { struct X {};
X zero(lib::tag, X); // customization };
my::X z = lib::zero(my::X());
I realize it's not bulletproof, but it does narrow the possibile problems.
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.
I only use such tags when I need to inject a namespace for ADL purposes via the tag.
Example, please? -- Dave Abrahams Boost Consulting www.boost-consulting.com