www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Using private constructor with std.experimental.allocater:make

reply earthfront <earthfront safetymail.info> writes:
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.
May 01 2016
next sibling parent reply Lass Safin <lasssafin gmail.com> writes:
On Sunday, 1 May 2016 at 11:17:27 UTC, earthfront wrote:
 Hello!
 [...]
  class A
    { int b; private this(int a){b=a;} }
 [...]
I don't think classes are supposed to be able to have a private constructor...
May 01 2016
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sun, 01 May 2016 18:27:51 +0000
Lass Safin via Digitalmars-d-learn <digitalmars-d-learn puremagic.com>
wrote:

 On Sunday, 1 May 2016 at 11:17:27 UTC, earthfront wrote:
 Hello!
 [...]
  class A
    { int b; private this(int a){b=a;} }
 [...]
I don't think classes are supposed to be able to have a private constructor...
There's no reason why you shouldn't be able to. Other, public constructors could call it, and other code within the module could call it. You can even have entire classes which are private. So, private constructors need to be possible. Now, whether that will ever work with something like emplace or the allocators, I don't know, since they're going to have access to the constructor to do their thing, but they're in different modules, which wouldn't normally have access to any constructors which aren't private (including protected and package, not just private). - Jonathan M Davis
May 01 2016
prev sibling parent reply Basile B <b2.temp gmx.com> writes:
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, ...).
May 01 2016
parent reply earthfront <earthfront safetymail.info> writes:
On Sunday, 1 May 2016 at 19:18:38 UTC, Basile B wrote:
 CT make(CT, Alloc, A...)(auto ref Alloc al, A a)
This works. Thank you. Good point about __ctor alone not being sufficient.
     auto memory = al.allocate(size);
... <snip>
     GC.addRange(memory.ptr, size, typeid(CT));
Nit: "GC.addRange..." -- this attempts to clean memory allocated manually.
 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
I guess this is the best workaround at the moment. There's a discontinuity between the GC and std.allocators. "new A(1)" works, because the GC has implicit private access to classes. I would love to see allocators in wider use. Workarounds are an obstacle to this. Possible solutions: * Emulate c++'s "friend" keyword somehow. D's rationale eschew's this. * Somehow designate friendship via [selective] module import? * Secret upcoming solution from Walter/Andrei. Weren't they discussing some sort of built in ref counting system?
May 01 2016
parent Basile B <b2.temp gmx.com> writes:
On Monday, 2 May 2016 at 04:19:48 UTC, earthfront wrote:
 On Sunday, 1 May 2016 at 19:18:38 UTC, Basile B wrote:
 CT make(CT, Alloc, A...)(auto ref Alloc al, A a)
This works. Thank you. Good point about __ctor alone not being sufficient.
     auto memory = al.allocate(size);
... <snip>
     GC.addRange(memory.ptr, size, typeid(CT));
Nit: "GC.addRange..." -- this attempts to clean memory allocated manually.
 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
I guess this is the best workaround at the moment. There's a discontinuity between the GC and std.allocators. "new A(1)" works, because the GC has implicit private access to classes. I would love to see allocators in wider use. Workarounds are an obstacle to this. Possible solutions: * Emulate c++'s "friend" keyword somehow. D's rationale eschew's this. * Somehow designate friendship via [selective] module import? * Secret upcoming solution from Walter/Andrei. Weren't they discussing some sort of built in ref counting system?
Other solution: give "super powers" to the __traits()... used to inspect, like suggested in this bugzilla ticket: https://issues.dlang.org/show_bug.cgi?id=15371. The worst with this problem is that it's extremely easy to fix in the compiler source code. But it's extremely hard to make people understand that this is a real problem.
May 01 2016