Alias type with different initialiser.

John Colvin via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Feb 14 06:08:31 PST 2017


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.


More information about the Digitalmars-d-learn mailing list