Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-09-02 12:39:51

Gregory Colvin <gregory.colvin_at_[hidden]> writes:

> On Tuesday, Sep 2, 2003, at 09:22 America/Denver, David Abrahams wrote:
>> Gregory Colvin <gregory.colvin_at_[hidden]> writes:
>> ...
>>> Dave:
>>>> I think I would rather see a MPL lambda expression or metafunction
>>>> class interface for allocator type parameters. It makes little sense
>>>> for the allocator's user to be choosing its value_type.
>>>> Something like:
>>>> some_allocator<_1>
>>>> or
>>>> struct select_allocator
>>>> {
>>>> template <class T>
>>>> struct apply
>>>> {
>>>> typedef some_allocator<T> type;
>>>> };
>>>> };
>>>> with some_allocator's interface being like what's required for
>>>> std::allocator but not including misplaced interface bits such as
>>>> address/construct/destroy, and possibly max_size -- these can be
>>>> added
>>>> by a std::allocator facade wrapper if neccessary.
>>>> I'm not sure we need a simple version and a complicated version.
>>> I'm not clear how you intend the above to be used, or what you
>>> intend it to be a replacement for.
>> I intend it to be the sort of type parameter that gets passed to our
>> objects which need custom allocation in place of a standard allocator.
>> It's ridiculous, IMO, to pass allocator<T> to a node-based container
>> which is *never* going to allocate a T object.
> But given rebind() it doesn't really matter.

Well, it's rebind<...> actually, and it does matter. See below.

> We could just as well have specified that all containers take
> allocator<void> arguments.

It's just horribly decomposed (gross! ;-> ). It says "pass me a
complicated class which I might never actually use, but which also
functions as a nested metafunction which I might use to get the class
which I will use. The class you pass has to meet all kinds of
irrelevant requirements like having a nested value_type that matches
my element type, and the class I will actually use also has to meet
all kinds of requirements irrelevant to its use like having a nested
rebind template."

>> The container itself
>> should decide which type the allocator template gets instantiated on,
>> via:
>> mpl::apply<mpl::lambda<S>, Node>::type
>> [
>> this is approximately the same as:
>> S::template apply<U>::type == some_allocator<U>
>> except that it works when S is the lambda expression
>> some_allocator<_1> as well as when it's the select_allocator
>> metafunction class below it.
>> ]
> Sorry, but I'm still not following this, but that may be
> because I don't know much MPL, so I can only guess at what
> you are up to. I probably need a detailed example of how to
> write and use one of these thingys to make any sense of your
> proposal.

I showed one which requires no MPL knowledge

S::template apply<U>::type is the correct (non-standard) 'allocator'
type for U given an AllocatorSelector S (see the allocator_selector
class from my previous post)

This is essentially equivalent to what normally happens with
A::template rebind<U>, except that it happens uniformly, and classes
are doing one job instead of two or three. There are reasons for
using apply<U>::type instead of simply rebind<U>, but those are not
too important to my argument.

> Does your proposal support stateful allocators?

It could, pretty easily. I'd use an allocator_state traits template
which generates the type used to initialize the allocator. The
standard model is that allocator_state<A>::type is just A, but it
doesn't make any sense for allocators which need rebind to require
the allocator type.

>> What the rebind requirement in the allocator means for pool
>> allocation, for example, is that a pool_allocator<T> object must
>> either be stateless (in which case allocator inequality is
>> meaningless)
> Yes, all stateless allocators compare equal, and stateless
> allocators are the easiest kind to make compare equal, as
> the standard currently requires allocators to do.
>> or effectively be able to allocate blocks of *any* size
>> and alignment, rather than just as appropriate for T.
> Yes, because node-based containers need to allocate nodes,
> perhaps of various kinds, but they don't expose their
> node types in their interface. So you have to pass in
> something, and we went with T rather than void or whatever.

I think you're missing my point. There's no reason that a stateful
allocator<T> should have access to the state data required to
allocate U objects, but that's the status quo.

> It might have been better to have different allocator
> interfaces for array-based versus node-based containers


> since array-based containers have no need of rebind()

Not neccessarily. My fixed-size single allocation array needed it.

> and node-based containers have no need of allocator<T>.

Maybe. I have my doubts. The only thing that keeps users of
vector<T> safe from mistakenly supplying allocator<U> is the default

>> It's a conceptual mess.
> Alex didn't have MPL when he invented allocators. So they are
> messier than they need to be, but I still say they are not so
> bad as you claim

Are you saying my factual claims are wrong, or just that all those
issues don't amount to a very important problem?

> and that it would be easier for the next standard to repair them
> than to replace them.

That may be, but we're here at Boost, talking about the interface we
should be using in Boost components.

Dave Abrahams
Boost Consulting

Boost list run by bdawes at, gregod at, cpdaniel at, john at