Using private constructor with std.experimental.allocater:make

Basile B via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun May 1 12:18:38 PDT 2016


On Sunday, 1 May 2016 at 11:17:27 UTC, earthfront wrote:
> Hello!
>
> This code fails:
> -------------------------
> void main(){
>  class A
>    { int b; private this(int a){b=a;} }
>    //{ int b; this(int a){b=a;} }
>
>  import std.conv:emplace;
>  import std.experimental.allocator.mallocator:Mallocator;
>  import std.experimental.allocator:make;
>
>  {
>    auto ptr = make!A(Mallocator.instance, 42);
>    assert (ptr.b == 42);
>  }
> }
> ---------------------------
>
> with error message:
> "/usr/include/dmd/phobos/std/conv.d(4115): Error: static assert
>  "Don't know how to initialize an object of type A with 
> arguments (int)"
> /usr/include/dmd/phobos/std/experimental/allocator/package.d(456):        instantiated from here: emplace!(A, int)
> ./helloworld.d(25):        instantiated from here: make!(A, 
> shared(Mallocator), int)"
>
>
> If I make the constructor public, no problem.
> It seems that emplace (specialized for classes) doesn't work if 
> the class has a private constructor.
>
>
> I added the following snippet to confirm:
> ----------------------
>  {
>    auto ptr = 
> Mallocator.instance.allocate(__traits(classInstanceSize, A));
>    auto aPtr = emplace(ptr,34);
>    assert( aPtr.b == 34 );
>  }
> ----------------------
> And I get the same error message.
>
>
> Google gave me:
> http://forum.dlang.org/post/kot0t1$uls$1@digitalmars.com
>
>
> That guy's ultimate fix was explicitly calling the class's 
> __ctor method, instead of emplace. The __ctor method is 
> undocumented, as far as I can tell.
>
>
> Is there a better solution now? More widespread use of 
> allocators will likely result in more of this problem.


__ctor is not enough, the "static layout" must be copied to get 
the status of the initialized variables. Also there is not always 
a __ctor. Anyway you can put this in the module where is located 
the class with a private ctor:

----
import std.traits;

CT make(CT, Alloc, A...)(auto ref Alloc al, A a)
if (is(CT == class) && !isAbstractClass!CT)
{
     auto size = typeid(CT).init.length;
     auto memory = al.allocate(size);
     memory[0 .. size] = typeid(CT).init[];
     static if (__traits(hasMember, CT, "__ctor"))
         (cast(CT) (memory.ptr)).__ctor(a);
     import core.memory: GC;
     GC.addRange(memory.ptr, size, typeid(CT));
     return cast(CT) memory.ptr;
}

void main(string[] args)
{
     class A {int b; private this(int a){b=a;} }

     import std.experimental.allocator.mallocator;
     auto ptr = make!A(Mallocator.instance, 42);
     assert (ptr.b == 42);
}
----

The problem is that the template make and emplace() are 
**elsewhere** so they cannot see A.this() (== A.__ctor).

A common way to fix this kind of problem is to make a template 
mixin with the template that has the visibility and to mix it in 
the current scope:

---- module stuff -----
mixin template fixProtection()
{
     import std.traits;
     CT make(CT, Alloc, A...)(auto ref Alloc al, A a)
     if (is(CT == class) && !isAbstractClass!CT)
     {
         auto size = typeid(CT).init.length;
         auto memory = al.allocate(size);
         memory[0 .. size] = typeid(CT).init[];
         static if (__traits(hasMember, CT, "__ctor"))
             (cast(CT) (memory.ptr)).__ctor(a);
         import core.memory: GC;
         GC.addRange(memory.ptr, size, typeid(CT));
         return cast(CT) memory.ptr;
     }
}
-----

----- module other stuff -----
mixin fixProtection;

void main(string[] args)
{
     class A {int b; private this(int a){b=a;} }

     import std.experimental.allocator.mallocator;
     auto ptr = make!A(Mallocator.instance, 42);
     assert (ptr.b == 42);
}
-----

Many things related to __traits are affected (getMember, 
getOverload, ...).




More information about the Digitalmars-d-learn mailing list