Thread overview
Emplace using private constructor
Jun 07, 2013
develop32
Jun 07, 2013
develop32
Jun 07, 2013
Ali Çehreli
Jun 07, 2013
develop32
June 07, 2013
In a project I'm working on there are classes that are available publicly but I want to disable their construction outside of their modules.

class Display
{
   const string name;

   private this(string name)
   {
      this.name = name;
   }

   static Display[] all()
   {
      // Returns all available displays, the only place where Display objects
      // are to be constructed.
   }
}

This is easy when using "new". But I'm using std.conv.emplace which does not work with private constructors.

"Don't know how to initialize an object of type Display with arguments (string)"

Is there any solution to this problem? I know I can just make the constructor public but it just feels wrong, I want to keep public API as small as possible and it bugs me that language/library limits me here.
June 07, 2013
Nevermind, problem was not worth the question. I just copied code from Phobos std.conv.emplace and placed it directly in my code, it works since it is in the same module as the private constructor.
June 07, 2013
tldr; I had the crazy idea of mixing-in a specialization of emplace but failed probably because mixin does not respect template constraints.

On 06/07/2013 06:50 AM, develop32 wrote:

> Nevermind, problem was not worth the question. I just copied code from
> Phobos std.conv.emplace and placed it directly in my code, it works
> since it is in the same module as the private constructor.

But it is too heavy-handed. Your problem exposes a real weakness. Other library code will make assumptions like emplace does below and they will fail if the constructor is not accessible. From std.conv.emplace:

    // Call the ctor if any
    static if (is(typeof(result.__ctor(args))))
    {
        // T defines a genuine constructor accepting args
        // Go the classic route: write .init first, then call ctor
        result.__ctor(args);
    }

I tried making your code better but not copying emplace's implementation:

module display;

import std.conv;

class Display
{
private:
    const string name;

    this(string name)
    {
        this.name = name;
    }

public:

    static Display emplace(Args...)(void[] chunk, auto ref Args args)
    {
        return std.conv.emplace!(Display)(chunk, args);
    }
}

Here is a test program:

import display;

void main()
{
    enum roomNeeded = __traits(classInstanceSize, Display);
    ubyte[roomNeeded] buffer;
    auto d = Display.emplace(buffer, "hello");
}

But I failed with the same error:

Error: static assert  "Don't know how to initialize an object of type Display with arguments (string)"

Then I thought about mixing in the template but that did not work either:

class Display
{
// ...

    static Display emplace(Args...)(void[] chunk, auto ref Args args)
    {
        mixin std.conv.emplace!(Display, string);
    }
}

The error message indicated that mixin was picking the wrong specialization, because it had a template constraint for 'struct':

T* emplace(T, Args...)(T* chunk, auto ref Args args)
    if (is(T == struct))
{
    // ...
}

If it picked the one that matched is(T == class) then we would have achieved our goal without copy-pasting.

Still, if it worked, it also feels hacky because we would be mixing-in a specialization right inside our function and hope that it would somehow work in our environment. For example, the specialization of emplace has a return statement:

    return result;

What if my function should not return? I guess then we would mix-in inside a wrapper function, call that function, and ignore the return value.

I have a feeling that bearophile already has a bug report about all of this but I haven't checked yet. :p

Not the same, but it look like it is related to this bug:

  http://d.puremagic.com/issues/show_bug.cgi?id=9235

Ali

June 07, 2013
On Friday, 7 June 2013 at 16:14:25 UTC, Ali Çehreli wrote:
> But it is too heavy-handed. Your problem exposes a real weakness. Other...

Well, its not *that* heavy.

// Inside Display class.
auto display = alloc!Display;
display.__ctor(name);

Where alloc(T) is a bigger part of the original emplace code and is widely used.

But generally, yeah. It would be nice to fix mixins.