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, ...).


Reply via email to