Thread overview
Template mixins and struct constructors
Mar 02, 2016
Adrian Matoga
Mar 02, 2016
Daniel Kozak
Mar 02, 2016
Adrian Matoga
Mar 02, 2016
Daniel Kozak
Mar 02, 2016
Adrian Matoga
Mar 02, 2016
Daniel Kozak
Mar 02, 2016
Daniel Kozak
Mar 02, 2016
Adam D. Ruppe
Mar 02, 2016
Daniel Kozak
Mar 03, 2016
Adrian Matoga
March 02, 2016
I can do this:

struct Foo {
	int a;
	string b;
	this(int a) { this.a = a; }
	this(Args...)(string b, auto ref Args args) { this.b = b; this(args); }
}

unittest {
	auto foo1 = Foo(5);
	auto foo2 = Foo("foo", 15);
}

However, the following code is invalid:

mixin template AddField(T) {
	T b;
	this(Args...)(T b, auto ref Args args)
	{
		this.b = b;
		this(args);
	}
}

struct Bar {
	mixin AddField!string;
	int a;
	this(int a) { this.a = a; }
}

unittest {
	auto bar1 = Bar(5);
	auto bar2 = Bar("bar", 15);  // line 31
}

sctor.d(31): Error: constructor sctor.Bar.this (int a) is not callable using argument types (string, int)

Is it by design or is it a bug?
And, if it is by design, what is the reason for that?

March 02, 2016
On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
> I can do this:
>
> struct Foo {
> 	int a;
> 	string b;
> 	this(int a) { this.a = a; }
> 	this(Args...)(string b, auto ref Args args) { this.b = b; this(args); }
> }
>
> unittest {
> 	auto foo1 = Foo(5);
> 	auto foo2 = Foo("foo", 15);
> }
>
> However, the following code is invalid:
>
> mixin template AddField(T) {
> 	T b;
> 	this(Args...)(T b, auto ref Args args)
> 	{
> 		this.b = b;
> 		this(args);
> 	}
> }
>
> struct Bar {
> 	mixin AddField!string;
> 	int a;
> 	this(int a) { this.a = a; }
> }
>
> unittest {
> 	auto bar1 = Bar(5);
> 	auto bar2 = Bar("bar", 15);  // line 31
> }
>
> sctor.d(31): Error: constructor sctor.Bar.this (int a) is not callable using argument types (string, int)
>
> Is it by design or is it a bug?
> And, if it is by design, what is the reason for that?

You can use string mixins:

template AddField(T) {
        enum AddField = T.stringof ~ `  b;
        this(Args...)(` ~ T.stringof ~ ` b, auto ref Args args)
        {
                this.b = b;
                this(args);
        }`;
}

struct Bar {
        mixin(AddField!string);
        int a;
        this(int a) { this.a = a; }
}

unittest {
        auto bar1 = Bar(5);
        auto bar2 = Bar("bar", 15);  // line 31
}

March 02, 2016
On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
> I can do this:
>
> struct Foo {
> 	int a;
> 	string b;
> 	this(int a) { this.a = a; }
> 	this(Args...)(string b, auto ref Args args) { this.b = b; this(args); }
> }
>
> unittest {
> 	auto foo1 = Foo(5);
> 	auto foo2 = Foo("foo", 15);
> }
>
> However, the following code is invalid:
>
> mixin template AddField(T) {
> 	T b;
> 	this(Args...)(T b, auto ref Args args)
> 	{
> 		this.b = b;
> 		this(args);
> 	}
> }
>
> struct Bar {
> 	mixin AddField!string;
> 	int a;
> 	this(int a) { this.a = a; }
> }
>
> unittest {
> 	auto bar1 = Bar(5);
> 	auto bar2 = Bar("bar", 15);  // line 31
> }
>
> sctor.d(31): Error: constructor sctor.Bar.this (int a) is not callable using argument types (string, int)
>
> Is it by design or is it a bug?
> And, if it is by design, what is the reason for that?

I would say it is by design. What this will generate is something like:

struct Bar {
    static struct _scope {
        string b;
        this(Args...)(string b, auto ref Args args) {
            this.b = b; this(args);
        }
    }
    int a;
    this(int a) { this.a = a; }
}


March 02, 2016
On Wednesday, 2 March 2016 at 12:48:47 UTC, Daniel Kozak wrote:
> On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
>> (...)
>
> You can use string mixins:
>
> template AddField(T) {
>         enum AddField = T.stringof ~ `  b;
>         this(Args...)(` ~ T.stringof ~ ` b, auto ref Args args)
>         {
>                 this.b = b;
>                 this(args);
>         }`;
> }
>
> struct Bar {
>         mixin(AddField!string);
>         int a;
>         this(int a) { this.a = a; }
> }
>
> unittest {
>         auto bar1 = Bar(5);
>         auto bar2 = Bar("bar", 15);  // line 31
> }

What if T is private type in some other module or, even worse, a Voldemort type?
March 02, 2016
On Wednesday, 2 March 2016 at 13:18:23 UTC, Adrian Matoga wrote:
> On Wednesday, 2 March 2016 at 12:48:47 UTC, Daniel Kozak wrote:
>> On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
>>> (...)
>>
>> You can use string mixins:
>>
>> template AddField(T) {
>>         enum AddField = T.stringof ~ `  b;
>>         this(Args...)(` ~ T.stringof ~ ` b, auto ref Args args)
>>         {
>>                 this.b = b;
>>                 this(args);
>>         }`;
>> }
>>
>> struct Bar {
>>         mixin(AddField!string);
>>         int a;
>>         this(int a) { this.a = a; }
>> }
>>
>> unittest {
>>         auto bar1 = Bar(5);
>>         auto bar2 = Bar("bar", 15);  // line 31
>> }
>
> What if T is private type in some other module or, even worse, a Voldemort type?

OK maybe this one:

template AddField(T) {
    T b;
    this(Args...)(T b, auto ref Args args)
    {
           this.b = b;
       this(args);
    }
    this(int a) {
        this.a = a;
    }
}

struct Bar {
    int a;
    mixin AddField!(string);
}

unittest {
    auto bar1 = Bar(5);
    auto bar2 = Bar("bar", 15);
}
March 02, 2016
On Wednesday, 2 March 2016 at 14:36:59 UTC, Daniel Kozak wrote:
> OK maybe this one:
>
> template AddField(T) {
>     T b;
>     this(Args...)(T b, auto ref Args args)
>     {
>            this.b = b;
>        this(args);
>     }
>     this(int a) {
>         this.a = a;
>     }
> }
>
> struct Bar {
>     int a;
>     mixin AddField!(string);
> }
>
> unittest {
>     auto bar1 = Bar(5);
>     auto bar2 = Bar("bar", 15);
> }

Then it's limited to structs in which only "int a" is to be initialized. Not very useful and the templated ctor is not needed now.

struct Baz {
    mixin AddField!string; // fail, no "int a" in Baz.
    ulong d;
    double x;
    string foo;
}
March 02, 2016
On Wednesday, 2 March 2016 at 14:50:15 UTC, Adrian Matoga wrote:
> On Wednesday, 2 March 2016 at 14:36:59 UTC, Daniel Kozak wrote:
>> OK maybe this one:
>>
>> template AddField(T) {
>>     T b;
>>     this(Args...)(T b, auto ref Args args)
>>     {
>>            this.b = b;
>>        this(args);
>>     }
>>     this(int a) {
>>         this.a = a;
>>     }
>> }
>>
>> struct Bar {
>>     int a;
>>     mixin AddField!(string);
>> }
>>
>> unittest {
>>     auto bar1 = Bar(5);
>>     auto bar2 = Bar("bar", 15);
>> }
>
> Then it's limited to structs in which only "int a" is to be initialized. Not very useful and the templated ctor is not needed now.
>
> struct Baz {
>     mixin AddField!string; // fail, no "int a" in Baz.
>     ulong d;
>     double x;
>     string foo;
> }

OK then:

mixin template AddField(T) {
    T b;
    auto ref constructor(Args...)(T b, auto ref Args args)
    {
        typeof(this) r;
        r.b = b;
        r.__ctor(args);
        return r;
    }
}

struct Bar {
    mixin AddField!string;
    int a;
    this(int a) { this.a = a; }
    alias __ctor = constructor;
}

unittest {
    import std.stdio;

    auto bar1 = Bar(5);
    auto bar2 = Bar("bar", 15);
    writeln(bar1);
    writeln(bar2);
}
March 02, 2016
On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
> Is it by design or is it a bug?
> And, if it is by design, what is the reason for that?

That's by design. It allows you to override names from a template mixin like inheritance but no runtime cost.

Read my tip of the week here to see how it works and how you can combine ctors from a mixin:

http://arsdnet.net/this-week-in-d/2016-feb-07.html
March 02, 2016

Dne 2.3.2016 v 21:39 Adam D. Ruppe via Digitalmars-d-learn napsal(a):
> On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
>> Is it by design or is it a bug?
>> And, if it is by design, what is the reason for that?
>
> That's by design. It allows you to override names from a template mixin like inheritance but no runtime cost.
>
> Read my tip of the week here to see how it works and how you can combine ctors from a mixin:
>
> http://arsdnet.net/this-week-in-d/2016-feb-07.html
I know it :). I need to upgrade my memory. I spent all day looking for it in D cookbook, because I was sure it was you who make this tip somewhere.
March 03, 2016
On Wednesday, 2 March 2016 at 20:39:57 UTC, Adam D. Ruppe wrote:
> On Wednesday, 2 March 2016 at 12:27:04 UTC, Adrian Matoga wrote:
>> Is it by design or is it a bug?
>> And, if it is by design, what is the reason for that?
>
> That's by design. It allows you to override names from a template mixin like inheritance but no runtime cost.
>
> Read my tip of the week here to see how it works and how you can combine ctors from a mixin:
>
> http://arsdnet.net/this-week-in-d/2016-feb-07.html

Ah, I was on vacation at that time so that's why I don't remember reading it. :)

Thanks! It's a nice workaround but it's, well, a workaround. It leaks the internals of the template mixin and a potential API user would have to remember two additional quirks when using it. I guess I'll try a different approach, initializing the mixed in members later with a function.