www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Regarding emplace, arrays, and helper functions

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
The emplace docs state that the chunk where to store the class object
instance needs to be aligned to the class type alignment. But it
doesn't say much on how to get this alignment from a class (we could
add a note about using the classInstanceAlignment template), or even
how to use it to create e.g. an array of class objects (not an array
of references).

Here's how one can run into a problem:

-----
import std.conv;

class C
{
    this(ubyte b) { _b = b; }
    ubyte _b;
}

void main()
{
    // 9 bytes (x32), it's not going to align itself properly
    enum Size = __traits(classInstanceSize, C);

    ubyte[Size][2] buffer;

    // off-topic: cast needed -> yet another emplace bug
    auto obj1 = emplace!C(buffer[0], cast(ubyte)20);
    assert(obj1._b == 20);

    auto obj2 = emplace!C(buffer[1], cast(ubyte)20);  // Boom!
    assert(obj2._b == 20);
}
-----

On the second emplace call, an exception is thrown:

-----
std.conv.ConvException std\conv.d(3832): emplace: Misaligned memory
block (0x18FD41): it must be 4-byte aligned for type C
-----

So one has to figure out how to do alignment properly, but there's no
real documentation on how to do it. I had a look at the scoped()
implementation for reference, but it's quite complex, here's a
snippet:

-----
template scoped(T)
    if (is(T == class))
{
    // _d_newclass now use default GC alignment (looks like
(void*).sizeof * 2 for
    // small objects). We will just use the maximum of filed alignments.
    alias classInstanceAlignment!T alignment;
    alias _alignUp!alignment aligned;

    static struct Scoped
    {
        // Addition of `alignment` is required as `Scoped_store` can
be misaligned in memory.
        private void[aligned(__traits(classInstanceSize, T) +
size_t.sizeof) + alignment] Scoped_store = void;

         property inout(T) Scoped_payload() inout
        {
            void* alignedStore = cast(void*) aligned(cast(size_t)
Scoped_store.ptr);
            // As `Scoped` can be unaligned moved in memory class
instance should be moved accordingly.
            immutable size_t d = alignedStore - Scoped_store.ptr;
            size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof];
            if(d != *currD)
            {
                import core.stdc.string;
                memmove(alignedStore, Scoped_store.ptr + *currD,
__traits(classInstanceSize, T));
                *currD = d;
            }
            return cast(inout(T)) alignedStore;
        }
        alias Scoped_payload this;

         disable this();
         disable this(this);

        ~this()
        {
            // `destroy` will also write .init but we have no
functions in druntime
            // for deterministic finalization and memory releasing for now.
            .destroy(Scoped_payload);
        }
    }

    /// Returns the scoped object
     system auto scoped(Args...)(auto ref Args args)
    {
        Scoped result = void;
        void* alignedStore = cast(void*) aligned(cast(size_t)
result.Scoped_store.ptr);
        immutable size_t d = alignedStore - result.Scoped_store.ptr;
        *cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d;
        emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args);
        return result;
    }
}
----

It uses a private _alignUp helper template as well:

-----
private size_t _alignUp(size_t alignment)(size_t n)
    if(alignment > 0 && !((alignment - 1) & alignment))
{
    enum badEnd = alignment - 1; // 0b11, 0b111, ...
    return (n + badEnd) & ~badEnd;
}
-----

There is a lot of magic here that's hard to understand.

Anyway, since we already have emplace() as a public Phobos function, I
thought it might be good if we had another overload which created an
array of objects, or even a static array of objects wrapped in a
convenient random access range on top. Otherwise guaranteeing
alignment by hand seems to be complicated (judging from the scoped
template implementation).

I'm just pseudocoding here:

-----
class Point
{
    this(int x, int y)
    {
        _x = x;
        _y = y;
    }

    int _x;
    int _y;
}

void main()
{
    // arr is a struct instance which holds the aligned static array,
    // which itself holds the Point objects.
    // The struct has opIndex, alias this, etc, to simulate
    // an array of references.
    auto arr = emplaceStaticArray!(Point, 2);  // 2 items

    // note: one /could/ also allow calling the ctors in the above call,
    // although the syntax could be tricky

    arr[0].initialize(1, 2);  // destroys existing state (if any),
calls the ctor
    arr[1].initialize(3, 4);  // ditto

    Point point = arr[0];  // get the object reference via alias this
(just like in scoped)
    assert(point.x == 1 && point.y == 2);
}
-----

An equivalent emplaceArray for dynamic (resizable) arrays could also
be implemented, where the memory can expand and shrink as necessary.

Thoughts? I know bearophile is probably interested. :)
Aug 29 2013
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
A little related:
http://d.puremagic.com/issues/show_bug.cgi?id=8873

Bye,
bearophile
Aug 29 2013
prev sibling next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/29/2013 05:20 AM, Andrej Mitrovic wrote:

 The emplace docs state that the chunk where to store the class object
 instance needs to be aligned to the class type alignment. But it
 doesn't say much on how to get this alignment from a class (we could
 add a note about using the classInstanceAlignment template), or even
 how to use it to create e.g. an array of class objects (not an array
 of references).

 Here's how one can run into a problem:

 -----
 import std.conv;

 class C
 {
      this(ubyte b) { _b = b; }
      ubyte _b;
 }

 void main()
 {
      // 9 bytes (x32), it's not going to align itself properly
      enum Size = __traits(classInstanceSize, C);

      ubyte[Size][2] buffer;

      // off-topic: cast needed -> yet another emplace bug
      auto obj1 = emplace!C(buffer[0], cast(ubyte)20);
      assert(obj1._b == 20);

      auto obj2 = emplace!C(buffer[1], cast(ubyte)20);  // Boom!
      assert(obj2._b == 20);
 }
 -----

 On the second emplace call, an exception is thrown:

 -----
 std.conv.ConvException std\conv.d(3832): emplace: Misaligned memory
 block (0x18FD41): it must be 4-byte aligned for type C
 -----

 So one has to figure out how to do alignment properly, but there's no
 real documentation on how to do it.
I had experimented with this in a chapter (not in English yet): http://ddili.org/ders/d/bellek_yonetimi.html Here are two functions that I have just translated from that page (I see that alignedAddress should better be alignUp): T * alignedAddress(T)(T * candidateAddress) out (result) { assert((cast(size_t)result % T.alignof) == 0); } body { return cast(T*)((cast(size_t)candidateAddress + T.alignof - 1) / T.alignof * T.alignof); } size_t paddedSize(T)() { static if (is (T == class)) { size_t size = __traits(classInstanceSize, T); } else { size_t size = T.sizeof; } return cast(size_t)alignedAddress(cast(T*)size); } Now your program works with a single change: enum Size = paddedSize!C(); Ali
Aug 29 2013
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/30/13, Ali =C7ehreli <acehreli yahoo.com> wrote:
 Now your program works with a single change:

      enum Size =3D paddedSize!C();
Excellent. However will the compiler align all static arrays so their memory begins at a proper offset? Maybe a more appropriate question is: Is all stack data guaranteed to be properly aligned, and on all platforms? For example: enum Size =3D paddedSize!C; ubyte[1] preBuffer; ubyte[Size][2] buffer; Is 'buffer' guaranteed to be aligned so its memory begins at a good offset? There could be any number of bytes before 'buffer', such as the preBuffer above it. Tests show that they are indeed aligned, but I wonder if this is something you can guarantee on all platforms?
Aug 30 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/30/2013 07:02 AM, Andrej Mitrovic wrote:

 However will the compiler align all static arrays so their memory
 begins at a proper offset?
The compiler considers only the element type of the static array.
 Maybe a more appropriate question is: Is all stack data guaranteed to
 be properly aligned, and on all platforms?
I don't know the answer but I don't think it is specified even for C++.
 For example:

      enum Size = paddedSize!C;

      ubyte[1] preBuffer;

      ubyte[Size][2] buffer;

 Is 'buffer' guaranteed to be aligned so its memory begins at a good
 offset?
I don't think so because the elements are ubytes and they can be placed any odd address. Perhaps it works on the stack but the following align(1) struct demonstrates that your buffer can be at an odd address: import std.stdio; align(1) struct S { ubyte[1] preBuffer; ubyte[16][2] buffer; } void main() { writeln(S.preBuffer.offsetof); // prints 0 writeln(S.buffer.offsetof); // prints 1 } Ali
Aug 30 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
I hate this "magic" library fix.. Why cannot we use 'scope' for 
this?
I know currently it is an unsafe feature that was also proposed 
for rejection, but it would look better, feel better, and the 
compiler could take the dirty work for us. And maybe better 
optimized.
Aug 30 2013
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/30/13, Namespace <rswhite4 googlemail.com> wrote:
 I hate this "magic" library fix.. Why cannot we use 'scope' for
 this?
 I know currently it is an unsafe feature that was also proposed
 for rejection, but it would look better, feel better, and the
 compiler could take the dirty work for us. And maybe better
 optimized.
It would also avoid this nasty issue: http://d.puremagic.com/issues/show_bug.cgi?id=10921
Aug 30 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
On Friday, 30 August 2013 at 14:45:40 UTC, Andrej Mitrovic wrote:
 On 8/30/13, Namespace <rswhite4 googlemail.com> wrote:
 I hate this "magic" library fix.. Why cannot we use 'scope' for
 this?
 I know currently it is an unsafe feature that was also proposed
 for rejection, but it would look better, feel better, and the
 compiler could take the dirty work for us. And maybe better
 optimized.
It would also avoid this nasty issue: http://d.puremagic.com/issues/show_bug.cgi?id=10921
Yes. :)
Aug 30 2013