On Tuesday, 14 February 2017 at 11:34:22 UTC, Bastiaan Veelo wrote:
On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote:
Why not use a constructor instead of static opCall?

I don't know, this comes from http://dlang.org/spec/struct.html#dynamic_struct_init. Your constructor looks a lot better. Am I missing a test case where static opCall would be called, but not the constructor? Why do the docs use static opCall?

The docs are just trying to illustrate that opCall is only used for initialisation if the initialiser is of a different type. Constructors are always used for initialisation if they are there, they completely hide static opCall in that context. static opCall can return anything of any type, constructors implicitly return a reference to `this`. A constructor is the way to initialise a type and some code - such as std.conv.emplace - actually calls the constructor manually (it's accessible via .__ctor) to do so. static opCall doesn't necessarily represent an initialiser function, so generic code won't necessarily know to use it.

Overall, static opCall is rarely the solution you need.

Also, it's generally a bad idea to define `.init` for any type as code generally expects this to be the compiler-generated property (e.g. a value of type Initial!(int, 1) not of type int).

Thanks. I can't remember what confused me to think that typeof(int1.init) had to be int.

    enum initial = val;
[...]
static assert(int1.initial == 1); // typeof(int1.initial) == int

These lines have no purpose beyond illustration, right?

they check that the implementation does have a member .initial and that it is equal to the value we requested as the initialiser. It also enforces that it is accessible at compile-time.


I now have the following, featuring the novel Scherkl-Nielsen self-important lookup:

/**
Creates an type that is mostly $(PARAM T), only with a different initial value of $(PARAM val).
*/
struct Initial(T, T val)
{
    private T _payload = val;
    alias _payload this;

    this(T v)
    {
        _payload = v;
    }

    // https://dlang.org/blog/2017/02/13/a-new-import-idiom/
    private template from(string moduleName)
    {
      mixin("import from = " ~ moduleName ~ ";");
    }

void toString(scope void delegate(const(char)[]) sink, from!"std.format".FormatSpec!char fmt)
    {
        import std.array : appender;
        import std.format : formatValue;
        auto w = appender!string();
        formatValue(w, _payload, fmt);
        sink(w.data);
    }
}

unittest
{
    alias int1 = Initial!(int, 1);
    static assert(int1.init == 1); // typeof(int1.init) == int1

    int1 i;
    assert(i == 1);
    int1 ii = 2;
    assert(ii == 2);
    assert(ii.init == 1);
    assert(int1.init == 1);

    void f(int val)
    {
        assert(val == 1);
    }
    f(i);

    int i0;
    assert(i0 == 0);
    i = i0;
    assert(i == 0);
    assert(i.init == 1);
    i0 = ii;
    assert(i0 == 2);
    assert(i0.init == 0);

    import std.string;
    assert(format("%6d", ii) == "     2");
}

I would recommend making `template from(string moduleName)` global (maybe in a utils module?), there's no reason for it to be a member of Initial.

Reply via email to