September 24, 2013
25-Sep-2013 00:39, Andrei Alexandrescu пишет:
> On 9/24/13 1:02 PM, Dmitry Olshansky wrote:
>> 24-Sep-2013 19:56, Andrei Alexandrescu пишет:
>>> It could, but as I mentioned to Manu - at this level any cost is
>>> significant. Even changing from a compile-time constant to a global
>>> static dynamically-initialized constant has a cost. Making alignment an
>>> instance variable turns stateless allocators into stateful ones.
>>
>> Hm most could just get away with an enum. Am I missing something?
>
> Right now I do things like:
>
> struct DopeAllocator(Allocator, Dope)
> {
>      static assert(Allocator.alignment >= Dope.alignof,
>          "DopeAllocator does not work with allocators offering a smaller"
>          " alignment than the dope alignment.");
>      static assert(!hasElaborateDestructor!Dope,
>          "DopeAllocator: ellaborate destructors for Dope not
> implemented.");
>
>      enum alignment = Dope.alignof;
>
>      ...
> }
>
But it's a boiler plate regardless.
What if we are to provide the tools to manipulate said alignment composition (some imaginary templates in action):

alias alignment = requireAlignment!(Allocator, Dope.alignof).overrideAlignment!(Dope.alignof);

Which in this case would unfold to a proper primitive that tests alignment of Allocator at RT or CT, and then return fixed value as new alignment.

> Such code would be significantly more involved if Allocator had the
> freedom to define alignment as either an enum, a static method, or a
> member function.

Well you solved static vs member function issue already (via singleton).

>>> I'm hoping to get away with a static function "static uint alignment()"
>>> that (a) is CTFEable for most allocators/platforms, (b) uses a
>>> dynamically-initialized static constant for a few. Then derived
>>> allocators will inherit CTFEability.
>>
>> A check for CTFE-ablity might do the trick. The run-time dependent ones
>> usually would immediately hit a wall that is a sys-call or WinAPI.
>
> It does. It's just some more work. Guess I'd need to put it in.
>
>>> Would a per-allocator-type alignment suffice?
>>
>> I see alignment as an inherent property of the way an allocator acquires
>> memory.
>
> The question remains, is it a property of the allocator _object_ or a
> property of the allocator _type_? I.e. do we need to support allocators
> that store alignment as a nonstatic member variable?
>
>> Sad but true one can't determine the actual alignment of some
>> important ones at CTFE. Even worse adapters would in turn depend on
>> these system-specific run-time values such as half a page, 2 cache lines
>> etc.
>>
>> auto my2PageAligner = AligningAllocator(page_size*2, rootAllocator)
>>
>> where page_size is calculated elsewhere.
>
> That can be made to work, it's just that page_size would be a static
> variable that the user must set (not a template argument).

Right. So another angle to the problem is that maybe we could just provide a set of run-time "base" values for alignment. Then one can use say x-page alignment defining x at compile-time.

The only problem I foresee with this is that: std.allocator is ill suited to determine all base alignments it may need.
Say a certain kind of disk sector size (some 3rd party code), or DMA block size (or what it's called) on some system. Maybe it's that 1% that is not worth it but I dunno.

>> expand should do the trick. I think that nicely saves on primitives
>> count. But we may have to add a 'shrink' if you are so bent on never
>> decreasing size in expand :)
>> And ... reallocate doesn't cut it as long as there is no big red tape on
>> it that says - if decreasing the size reallocation it is ALWAYS in-place
>> (it might not be true for some allocators - e.g. realloc doesn't
>> guarantee even this IIRC).
>
> OK, I'm willing to add the optional method shrink(void[], size_t
> newSize) if expand() and realloc() are insufficient.

Okay let it be shrink. If the @safety of expand is that important and the fact it may make no sense to shrink on a call to expand. Honestly I could see a lot of code doing things like

if(newSize < oldSize) shrink(...)
else if(newSize > oldSize) expand(...)

Maybe it's fine.

> Indeed realloc()
> does not guarantee immovability, and we also shouldn't because
> Mallocator must be compatible the C API.

Spawn of Satan :)

-- 
Dmitry Olshansky
September 24, 2013
On 9/24/13 3:11 PM, Namespace wrote:
>> I would rather want new to be overloadable and have 2 sets of parameters
>>
>> new (allocator)(arg1, arg2)
>>
>> Where "allocator" would go to the overloaded version of new and "arg1"
>> and "arg2" will be passed to the constructor.
>
> +1

We're banning that syntax of new.

Andrei
September 25, 2013
On Tuesday, 24 September 2013 at 15:57:00 UTC, Andrei Alexandrescu wrote:
> Also, I hope we'll be able to allow allocators to define Pointer(T), Ref(T) etc. that supplant replacements for the built-in notions of pointer, reference etc. Then, user code that uses these notions instead of the built-in ones will be guaranteed some nice properties (e.g. automatic reference counting). Unfortunately I don't see a way for an allocator to enforce that its users don't do illegal things such as escaping addresses etc. So it would be a discipline-backed scheme. Notable beneficiaries will be containers.
>

It will be fun without tail const.

> The global GC does offer manual deallocation. It's the presence of collect() that indicates tracing abilities. If deallocateAll() is present, user code must assume it will be called during destruction.
>

It doesn't make any sens at this level. These allocator do not know what a pointer is. And can't be @safe if they do know.
September 25, 2013
On Tuesday, 24 September 2013 at 19:27:13 UTC, Andrei Alexandrescu wrote:
> Second, there's no need for a defaulted argument; the aligned allocation can be an optional overload of the one-argument function. I'm looking into ways to compose with that overload.
>

This or something like

allocator.setAlignement(...);
allocator.allocate(...);
September 25, 2013
Andrei Alexandrescu:

> Also, I hope we'll be able to allow allocators to define Pointer(T), Ref(T) etc. that supplant replacements for the built-in notions of pointer, reference etc. Then, user code that uses these notions instead of the built-in ones will be guaranteed some nice properties (e.g. automatic reference counting). Unfortunately I don't see a way for an allocator to enforce that its users don't do illegal things such as escaping addresses etc. So it would be a discipline-backed scheme. Notable beneficiaries will be containers.

Adding some compiler help is not forbidden. (Also for reference counting).

Bye,
bearophile
September 25, 2013
On Tuesday, 24 September 2013 at 22:39:27 UTC, Andrei Alexandrescu wrote:
> On 9/24/13 3:11 PM, Namespace wrote:
>>> I would rather want new to be overloadable and have 2 sets of parameters
>>>
>>> new (allocator)(arg1, arg2)
>>>
>>> Where "allocator" would go to the overloaded version of new and "arg1"
>>> and "arg2" will be passed to the constructor.
>>
>> +1
>
> We're banning that syntax of new.
>

It seem to me like typed allocator should try to fit in rather than redefine everything.
September 25, 2013
On Tuesday, 24 September 2013 at 18:53:56 UTC, Bigsandwich wrote:
> Hi,
>
> I mostly just lurk around here, but occasionally I just can't resist putting in my two cents.  I really want to see D replace C++ for AAA games (my industry) and allocators are really critical to that.  I think there's an elephant here that most of the posts have been dancing around.
>
> In C++ when you roll your own allocators (whether STL allocators or some other interface) there is basically, only one set of *semantics* to worry about - the classic C++/C pattern of new/delete or malloc/free.  This is actually much more complicated in D where you have at least two different semantics:
>
> 1) C++/C style
> 2) Tracing/Garbage Collection
> 3) Region allocators and other oddballs
>
> Consequences:
> 1) These semantics aren't really interchangeable.  If, for instance, a library is implemented with one of them in mind, it can only be replaced by an allocator with the same semantics.
>
> 2) Tracing/Garbage Collection are required for whatever the default allocator is and replacement allocator must implement those semantics.
>
> 3) Its only possible to change (2) by hacking the runtime so that any language features that rely on the GC cause errors.
>
> The design Andrei has presents seems to *allow* the creation of allocators with all of these semantics, but it doesn't seem to answer the following questions:
>
> 1) Can semantics be enforced?
> 2) Can allocators with different semantics be composed?
> 3) Can different semantics for the default allocator be implemented?
> 4) If so, how can I reconcile that with libraries that expect different default semantics?  A use case that I foresee for something like this would be banning the use of GC in low level engine code, but allowing it in higher level gameplay code.
>
> 5) Are any of these questions even relevant for this part of the design or will we have to wait for the rest of it to know the answers?
>
> Thanks.

I'd like to see these question answered. This is one of the most insightful posts of the discussion.
September 25, 2013
On 9/24/13 6:42 PM, deadalnix wrote:
> On Tuesday, 24 September 2013 at 22:39:27 UTC, Andrei Alexandrescu wrote:
>> On 9/24/13 3:11 PM, Namespace wrote:
>>>> I would rather want new to be overloadable and have 2 sets of
>>>> parameters
>>>>
>>>> new (allocator)(arg1, arg2)
>>>>
>>>> Where "allocator" would go to the overloaded version of new and "arg1"
>>>> and "arg2" will be passed to the constructor.
>>>
>>> +1
>>
>> We're banning that syntax of new.
>>
>
> It seem to me like typed allocator should try to fit in rather than
> redefine everything.

The banning is orthogonal to allocators.

Andrei

September 25, 2013
On Tuesday, 24 September 2013 at 22:39:27 UTC, Andrei Alexandrescu wrote:
> On 9/24/13 3:11 PM, Namespace wrote:
>>> I would rather want new to be overloadable and have 2 sets of parameters
>>>
>>> new (allocator)(arg1, arg2)
>>>
>>> Where "allocator" would go to the overloaded version of new and "arg1"
>>> and "arg2" will be passed to the constructor.
>>
>> +1
>
> We're banning that syntax of new.
>
> Andrei

What's wrong with this syntax?
And: *That* syntax or complete 'new' as built-in feature?
September 25, 2013
Am 25.09.2013 00:39, schrieb Andrei Alexandrescu:
> On 9/24/13 3:11 PM, Namespace wrote:
>>> I would rather want new to be overloadable and have 2 sets of parameters
>>>
>>> new (allocator)(arg1, arg2)
>>>
>>> Where "allocator" would go to the overloaded version of new and "arg1"
>>> and "arg2" will be passed to the constructor.
>>
>> +1
>
> We're banning that syntax of new.
>
> Andrei

I always love it when people just plain ignore all the arguments I made ...

-- 
Kind Regards
Benjamin Thaut