Thread overview
Small suggestion for default constructors
Jan 17
JN
January 17

Something I find quite annoying to do is to create a constructor that only sets the given properties. For example:

class Foo {
	int x;
	int y;
	int z;
	this(int x, int y, int z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
}

For structs, it automatically generates a constructor, and it'd be nice if you could autogenerate constructors for classes too, kinda like this:

class Foo {
	int x;
	int y;
	int z;
	default this(int x, int y, int z);
}

which would just be syntactic sugar for the former.

this could also be used for structs too if you've already specified a constructor:

struct Vector3 {
	float x;
	float y;
	float z;
	this(Vector2 v) {
		x = v.x;
		y = v.y;
	}
	default this(float x, float y, float z);
}

I feel like this would avoid a lot of boilerplate and make things more concise.

January 17

On Tuesday, 17 January 2023 at 02:45:30 UTC, TheZipCreator wrote:

>

For structs, it automatically generates a constructor, and it'd

There's a distinction. The default initializer for a struct is the .init value of each member. T() is not a default constructor, but a struct literal that's equivalent to T.init.

The default initializer for classes is always null and we don't have class literals.

I'm not arguing against your suggestion here. Just wanted to point out the distinction.

January 17
On Tuesday, 17 January 2023 at 02:45:30 UTC, TheZipCreator wrote:
> 	default this(float x, float y, float z);

Try this out:

class Foo {
        int x;
        int y;
        int z;

        // one little function
        this(typeof(this.tupleof) t) {
                this.tupleof = t;
        }
}

void main() {
        auto foo = new Foo(1, 2, 3);
        assert(foo.x == 1);
        assert(foo.y == 2);
        assert(foo.z == 3);
}
January 17

On Tuesday, 17 January 2023 at 02:45:30 UTC, TheZipCreator wrote:

>

I feel like this would avoid a lot of boilerplate and make things more concise.

Yeah, it's something I miss greatly from D (named constructors are another feature I miss). In Dart you can do it like this:

class Point
{
    num x;
    num y;

    Point(this.x, this.y);
}

var p = Point(10.0, 20.0);
January 17
On Tuesday, 17 January 2023 at 03:12:09 UTC, Adam D Ruppe wrote:
> On Tuesday, 17 January 2023 at 02:45:30 UTC, TheZipCreator wrote:
>> 	default this(float x, float y, float z);
>
> Try this out:
>
> class Foo {
>         int x;
>         int y;
>         int z;
>
>         // one little function
>         this(typeof(this.tupleof) t) {
>                 this.tupleof = t;
>         }
> }
>
> void main() {
>         auto foo = new Foo(1, 2, 3);
>         assert(foo.x == 1);
>         assert(foo.y == 2);
>         assert(foo.z == 3);
> }

ooh, that's an interesting usage of `tupleof`. I didn't even know it could be used as an lvalue. This is a bit unclear if you don't know the internals of D though. also, this wouldn't work if you just want to initialize one or two values and not all of them, but still could be useful.
January 17

On Tuesday, 17 January 2023 at 02:45:30 UTC, TheZipCreator wrote:

>

For structs, it automatically generates a constructor, and it'd be nice if you could autogenerate constructors for classes too, kinda like this:

You might like monkyyy's withMe() mixin template...

Source Link: https://forum.dlang.org/post/ipgwqcnfxltoijavflro@forum.dlang.org
Sample Print: S!(Letter)(ab, [10, 20], true)

auto withMe(string elements, Me, T...)(Me me, T arg){
    with(me) mixin(elements);
		return me;
}

enum { A = 97, B, C, D }
enum a = Letter(A);
enum b = Letter(B);

struct Letter {
    char letter = 96;
    alias letter this;
}

import std.stdio;
void main()
{
  struct S(T)
  {
    T[] word;
    int[] nums;
    bool view;
  }

  auto test = S!Letter().withMe!q{
	     word = [a, b];
	     nums = [10, 20];
	     view = true;
  };
  test.writeln;
}

SDB@79

January 17
On Tue, Jan 17, 2023 at 03:12:09AM +0000, Adam D Ruppe via Digitalmars-d wrote: [...]
> class Foo {
>         int x;
>         int y;
>         int z;
> 
>         // one little function
>         this(typeof(this.tupleof) t) {
>                 this.tupleof = t;
>         }
> }
[...]

Very nice!  Gotta use this in my own code sometime. :-D


T

-- 
Life is too short to run proprietary software. -- Bdale Garbee
January 17
On 1/16/23 19:12, Adam D Ruppe wrote:
>          // one little function
>          this(typeof(this.tupleof) t) {
>                  this.tupleof = t;
>          }

Which can be mixed-in and can have default arguments:

mixin template memberAssigningConstructor() {
    // one little function
    this(typeof(this.tupleof) t = typeof(this.tupleof).init) {
        this.tupleof = t;
    }
}

import std.stdio;

class Foo {
    int x;
    int y;
    int z;

    mixin memberAssigningConstructor!();

    override string toString() const {
        import std.format : format;
        return format!"%s %s %s"(x, y, z);
    }
}

void testWith(Args...)(Args args) {
    auto f = new Foo(args);
    writeln(f);
}

void main() {
    testWith();
    testWith(1);
    testWith(1, 2);
    testWith(1, 2, 3);
}

Outputs:

0 0 0
1 0 0
1 2 0
1 2 3

However, I recommend testing compilation speeds because I used mixed-in tupleof for opCmp implementations in a project about 3 years ago. There were only 6 instances of that usage but the compilation times were noticeably affected. (Disclaimer: There were 6 instances but I think those modules were imported very many times by other modules.)

Replacing the tupleof idiom with trivial functions fixed the compilation speed issue for me.

Ali

January 19

On Tuesday, 17 January 2023 at 03:02:46 UTC, Mike Parker wrote:

>

On Tuesday, 17 January 2023 at 02:45:30 UTC, TheZipCreator wrote:

>

For structs, it automatically generates a constructor, and it'd

There's a distinction. The default initializer for a struct is the .init value of each member. T() is not a default constructor, but a struct literal that's equivalent to T.init.

Almost. There is one situation where T.init and T() are different, and that's when T is a nested struct.

void main()
{
    int n;
    struct S
    {
        int fun() { return ++n; }
    }
    auto s1 = S();
    assert(s1.fun() == 1); // ok
    auto s2 = S.init;
    assert(s2.fun() == 2); // segmentation fault
}

In the above code, using S() initializes s1's context pointer at runtime to point to main's stack frame, allowing s1.fun to access the variable n. Using S.init to initialize s2, however, sets the context pointer to null, so when s2.fun is called, the program crashes.