Jump to page: 1 2 3
Thread overview
automem v0.0.7 - C++ style smart pointers using std.experimental.allocator
5 days ago
Kagamin
Apr 09
Basile B.
Apr 09
mogu
Apr 11
Nordlöw
April 09
Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:



http://code.dlang.org/packages/automem

Example:

I think the code in the README should be enough to understand what's going on. Alpha stuff here but I think the main things missing are weak pointers and a ref counted array. Given that I've never had to use std::weak_ptr in C++, I'm not in a hurry to implement the former.

Notable design decisions / features:

. The smart pointers are responsible for allocating the memory for the container object using the allocator of choice. This is to guarantee that one can't allocate and deallocate using different allocators.
. The allocator has to be specified as part of the type: this means the user can choose how to store it in the smart pointer, which for singletons (e.g. Mallocator) or stateless allocators means they can take up zero space. If a singleton (or the default theAllocator), the allocator doesn't need to be passed in to the constructor, otherwise it does. Specifying, e.g. Mallocator also means the relevant code can be marked @nogc.
. RefCounted only increments/decrements the ref count atomically if the contained type is `shared`
. RefCounted!(shared T) can be sent to other threads.
. UniqueArray behaves nearly like a normal array. You can even append to it, but it won't use GC memory (unless, of course, you chose to use GCAllocator)!

I benchmarked RefCounted against C++'s std::shared_ptr comparing ldc to clang using both shared and non-shared payloads in D. std::shared_ptr is faster (I've never written a smart pointer before), but the advantage of non-atomic operations makes my D implementation just a bit faster when non-shared. I think with some work it can be significantly faster without any loss of thread safety.

Atila
April 09
On Sunday, 9 April 2017 at 08:56:52 UTC, Atila Neves wrote:
> I benchmarked RefCounted against C++'s std::shared_ptr comparing ldc to clang using both shared and non-shared payloads in D. std::shared_ptr is faster (I've never written a smart pointer before), but the advantage of non-atomic operations makes my D implementation just a bit faster when non-shared. I think with some work it can be significantly faster without any loss of thread safety.

Nice!
You know you can overload `this(this) shared` do you? My plan was to use that for atomic RC, so that ppl. can use `shared(RefCounted)` when necessary.

April 09
On Sunday, 9 April 2017 at 09:36:53 UTC, Martin Nowak wrote:
> On Sunday, 9 April 2017 at 08:56:52 UTC, Atila Neves wrote:
>> I benchmarked RefCounted against C++'s std::shared_ptr comparing ldc to clang using both shared and non-shared payloads in D. std::shared_ptr is faster (I've never written a smart pointer before), but the advantage of non-atomic operations makes my D implementation just a bit faster when non-shared. I think with some work it can be significantly faster without any loss of thread safety.
>
> Nice!
> You know you can overload `this(this) shared` do you? My plan was to use that for atomic RC, so that ppl. can use `shared(RefCounted)` when necessary.

I did not. Thanks for telling me!

The way I wrote it RefCounted!(shared T) works - RefCounted doesn't have to be shared itself, but I guess it could be.

Atila
April 09
On 4/9/17 4:56 AM, Atila Neves wrote:
> Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:
> 
> 
> 
> http://code.dlang.org/packages/automem
> 
> Example:
> 
> I think the code in the README should be enough to understand what's going on. Alpha stuff here but I think the main things missing are weak pointers and a ref counted array. Given that I've never had to use std::weak_ptr in C++, I'm not in a hurry to implement the former.

Nice work!

> Notable design decisions / features:
> 
> . The smart pointers are responsible for allocating the memory for the container object using the allocator of choice. This is to guarantee that one can't allocate and deallocate using different allocators.

Nice!

> . The allocator has to be specified as part of the type: this means the user can choose how to store it in the smart pointer, which for singletons (e.g. Mallocator) or stateless allocators means they can take up zero space. If a singleton (or the default theAllocator), the allocator doesn't need to be passed in to the constructor, otherwise it does. Specifying, e.g. Mallocator also means the relevant code can be marked @nogc.

After extensively studying how C++ allocator framework works, I got to the notion that making the allocator part of the type is an antipattern.

> . RefCounted only increments/decrements the ref count atomically if the contained type is `shared`

Great. Can RefCounted itself be shared? I learned this is important for composition, i.e. you want to make a RefCounted a field in another object that is itself shared, immutable etc.

> . RefCounted!(shared T) can be sent to other threads.

Awes.

> . UniqueArray behaves nearly like a normal array. You can even append to it, but it won't use GC memory (unless, of course, you chose to use GCAllocator)!

This may be a great candidate for the standard library.


Andrei
April 09
On 09/04/2017 2:59 PM, Andrei Alexandrescu wrote:
> On 4/9/17 4:56 AM, Atila Neves wrote:

snip

>> . UniqueArray behaves nearly like a normal array. You can even append
>> to it, but it won't use GC memory (unless, of course, you chose to use
>> GCAllocator)!
>
> This may be a great candidate for the standard library.

To further this, I should rewrite my managed memory concept, it would be very useful for e.g. collections if at the same quality as Atila's as it gives the creator (of the memory) a heck a lot of control over it.

[0] https://github.com/rikkimax/alphaPhobos/blob/master/source/std/experimental/memory/managed.d
April 09
On Sunday, 9 April 2017 at 08:56:52 UTC, Atila Neves wrote:
> Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:
>
>
>
> http://code.dlang.org/packages/automem
>

I think that the Array misses
- a reservation strategy, something like reserve() and allocBy().
- dup / idup that return new distinct and deep copies.
- maybe .ptr at least for reading with pointer arithmetic.
- opBinary for "~" . Also you have bugs with operators:

```d
import std.experimental.allocator.mallocator;
UniqueArray!(int, Mallocator) a;
a ~= [0,1];
```

crashes directly.
April 09
On Sunday, 9 April 2017 at 08:56:52 UTC, Atila Neves wrote:
> Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:
>
> [...]

Nice!

Should UniqueArray be implemented as a overloaded version of Unique? Unique!(Object[]) instead of UniqueArray!(Object).
April 10
On Sunday, 9 April 2017 at 13:59:14 UTC, Andrei Alexandrescu wrote:
> On 4/9/17 4:56 AM, Atila Neves wrote:
>> Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:
>> 
>> 
>> 
>> http://code.dlang.org/packages/automem
>> 
>> Example:
>> 
>> I think the code in the README should be enough to understand what's going on. Alpha stuff here but I think the main things missing are weak pointers and a ref counted array. Given that I've never had to use std::weak_ptr in C++, I'm not in a hurry to implement the former.
>
> Nice work!
>
>> Notable design decisions / features:
>> 
>> . The smart pointers are responsible for allocating the memory for the container object using the allocator of choice. This is to guarantee that one can't allocate and deallocate using different allocators.
>
> Nice!
>
>> . The allocator has to be specified as part of the type: this means the user can choose how to store it in the smart pointer, which for singletons (e.g. Mallocator) or stateless allocators means they can take up zero space. If a singleton (or the default theAllocator), the allocator doesn't need to be passed in to the constructor, otherwise it does. Specifying, e.g. Mallocator also means the relevant code can be marked @nogc.
>
> After extensively studying how C++ allocator framework works, I got to the notion that making the allocator part of the type is an antipattern.

I was aware of this, but here we have a crucial workaround - theAllocator, which is the default anyway. It's probably the best of both worlds, since you can still specify the type if needed, which also means the guarantee of @nogc if needed.

>
>> . RefCounted only increments/decrements the ref count atomically if the contained type is `shared`
>
> Great. Can RefCounted itself be shared? I learned this is important for composition, i.e. you want to make a RefCounted a field in another object that is itself shared, immutable etc.

Since it has a destructor, no:

http://forum.dlang.org/post/sqazguejrcdtjimtjxtz@forum.dlang.org

The only way to do that would be to split it into two. Which I guess I could with a template mixin implementing the guts.

>
>> . RefCounted!(shared T) can be sent to other threads.
>
> Awes.
>
>> . UniqueArray behaves nearly like a normal array. You can even append to it, but it won't use GC memory (unless, of course, you chose to use GCAllocator)!
>
> This may be a great candidate for the standard library.

I think this needs to be used in production first, and having it as a dub package makes it easy for people to do so.


Atila
April 10
On Sunday, 9 April 2017 at 15:52:50 UTC, Basile B. wrote:
> On Sunday, 9 April 2017 at 08:56:52 UTC, Atila Neves wrote:
>> Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:
>>
>>
>>
>> http://code.dlang.org/packages/automem
>>
>
> I think that the Array misses
> - a reservation strategy, something like reserve() and allocBy().
> - dup / idup that return new distinct and deep copies.
> - maybe .ptr at least for reading with pointer arithmetic.
> - opBinary for "~" . Also you have bugs with operators:

Thanks for the suggestions.

>
> ```d
> import std.experimental.allocator.mallocator;
> UniqueArray!(int, Mallocator) a;
> a ~= [0,1];
> ```
>
> crashes directly.

Fixed now, thanks.

Atila
April 10
On Sunday, 9 April 2017 at 19:04:22 UTC, mogu wrote:
> On Sunday, 9 April 2017 at 08:56:52 UTC, Atila Neves wrote:
>> Using std.experimental.allocator? Tired of writing `scope(exit) allocator.dispose(foo);` in a language with RAII? Me too:
>>
>> [...]
>
> Nice!
>
> Should UniqueArray be implemented as a overloaded version of Unique? Unique!(Object[]) instead of UniqueArray!(Object).

I started like that, but after many a `static if` realised they had very little in common.

Atila
« First   ‹ Prev
1 2 3