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