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

Maybe.

> 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
argument.

>> 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
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk