Jump to page: 1 26  
Page
Thread overview
January 09
Another way to distinguish between constructors is needed.
Because it is possible to have two different constructors that take the same arguments.
Adding dummy arguments that are unused hurts code clarity.


January 09
On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
> Another way to distinguish between constructors is needed.
> Because it is possible to have two different constructors that take the same arguments.
> Adding dummy arguments that are unused hurts code clarity.

Couldn't this problem be solved by a factory method? I mean, either this, or something like this: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html

´´´
import std.stdio;
import std.math;

void main()
{
    auto pr = Point.rectangular(2,3);

    assert(approxEqual(pr.x_, 2));
    assert(approxEqual(pr.y_, 3));

    auto pp = Point.polar(1,PI);

    assert(approxEqual(pp.x_, -1));
    assert(approxEqual(pp.y_, 0));
}


struct Point {
public:
  static Point rectangular(real x, real y)      // Rectangular coord's
  {
      return Point(x,y);
  }
  static Point polar(real radius, real angle)   // Polar coordinates
  {
      return Point(radius * cos(angle), radius * sin(angle));
  }

private:
  this(real x, real y)
  {
      x_ = x;
      y_ = y;
  }
  real x_, y_;
}
´´´
January 09
On Wednesday, 9 January 2019 at 08:53:40 UTC, Alex wrote:
> On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
>> Another way to distinguish between constructors is needed.
>> Because it is possible to have two different constructors that take the same arguments.
>> Adding dummy arguments that are unused hurts code clarity.
>
> Couldn't this problem be solved by a factory method? I mean, either this, or something like this: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html
>
> ´´´
> import std.stdio;
> import std.math;
>
> void main()
> {
>     auto pr = Point.rectangular(2,3);
>
>     assert(approxEqual(pr.x_, 2));
>     assert(approxEqual(pr.y_, 3));
>
>     auto pp = Point.polar(1,PI);
>
>     assert(approxEqual(pp.x_, -1));
>     assert(approxEqual(pp.y_, 0));
> }
>
>
> struct Point {
> public:
>   static Point rectangular(real x, real y)      // Rectangular coord's
>   {
>       return Point(x,y);
>   }
>   static Point polar(real radius, real angle)   // Polar coordinates
>   {
>       return Point(radius * cos(angle), radius * sin(angle));
>   }
>
> private:
>   this(real x, real y)
>   {
>       x_ = x;
>       y_ = y;
>   }
>   real x_, y_;
> }
> ´´´

I like this approach to it and you're entirely correct.

It would be nice with support for this in the language without having to create factory methods.

Basically something like the following could be lowered to your code.

´´´
import std.stdio;
import std.math;

void main()
{
    auto pr = Point.rectangular(2,3);

    assert(approxEqual(pr.x_, 2));
    assert(approxEqual(pr.y_, 3));

    auto pp = Point.polar(1,PI);

    assert(approxEqual(pp.x_, -1));
    assert(approxEqual(pp.y_, 0));
}


struct Point {
public:
  this(real x, real y)
  {
      this.x = x;
      this.y = y;
  }

  this rectangular(real x, real y)
  {
      this(x,y);
  }

  this polar(real radius, real angle)
  {
      this(radius * cos(angle), radius * sin(angle));
  }

  real x, y;
}
´´´
January 09
On Wed, 09 Jan 2019 08:53:40 +0000, Alex wrote:
> On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
>> Another way to distinguish between constructors is needed. Because it
>> is possible to have two different constructors that take the same
>> arguments.
>> Adding dummy arguments that are unused hurts code clarity.
> 
> Couldn't this problem be solved by a factory method? I mean,
> either this, or something like this:
> http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html

A constructor can alter const fields, but a factory method can't, so they're not exactly equivalent. I was hoping you could add a template parameter to a constructor to give a "name" to it, like:

class Foo
{
  const int i;
  this(string name: "shift")(int i) { this.i = 1 << i; }
  this(string name: "direct")(int i) { this.i = i; }
}
new Foo!"shift"(3);

But that doesn't work; there is no way to explicitly provide template parameters to a constructor.
January 09
On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
> Another way to distinguish between constructors is needed.
> Because it is possible to have two different constructors that take the same arguments.
> Adding dummy arguments that are unused hurts code clarity.

Adding dummy arguments is unecessary. Just use the type system:

struct Person {
    this(FirstName firstName, LastName lastName) { /* ... */ }
}

struct FirstName { string value; }
struct LastName  { string value; }


C++ even has two libraries to do this, but D doesn't need that given that structs in D are much more useful (no need to write a constructor).
January 09
On Wed, Jan 09, 2019 at 05:30:32PM +0000, Atila Neves via Digitalmars-d wrote:
> On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
> > Another way to distinguish between constructors is needed.  Because it is possible to have two different constructors that take the same arguments.  Adding dummy arguments that are unused hurts code clarity.
> 
> Adding dummy arguments is unecessary. Just use the type system:
> 
> struct Person {
>     this(FirstName firstName, LastName lastName) { /* ... */ }
> }
> 
> struct FirstName { string value; }
> struct LastName  { string value; }

+1. That's what the type system is for.  Dummy arguments are NEVER a good idea unless there's a need for the API to be uniform.

It also makes the calling code self-documenting without needing language support for named arguments:

	auto myPerson = Person(FirstName("John"), LastName("Doe"));

rather than the opaque (and thus error-prone):

	// Bug 1234: hmm, is it first name first, or last name first?
	//auto myPerson = Person("Doe", "John");
	auto myPerson = Person("John", "Doe");


T

-- 
Bare foot: (n.) A device for locating thumb tacks on the floor.
January 09
On Wed, Jan 9, 2019 at 5:50 PM Neia Neutuladh via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wed, 09 Jan 2019 08:53:40 +0000, Alex wrote:
> > On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
> >> Another way to distinguish between constructors is needed. Because it
> >> is possible to have two different constructors that take the same
> >> arguments.
> >> Adding dummy arguments that are unused hurts code clarity.
> >
> > Couldn't this problem be solved by a factory method? I mean,
> > either this, or something like this:
> > http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html
>
> A constructor can alter const fields, but a factory method can't, so they're not exactly equivalent. I was hoping you could add a template parameter to a constructor to give a "name" to it, like:
>
> class Foo
> {
>   const int i;
>   this(string name: "shift")(int i) { this.i = 1 << i; }
>   this(string name: "direct")(int i) { this.i = i; }
> }
> new Foo!"shift"(3);
>
> But that doesn't work; there is no way to explicitly provide template parameters to a constructor.
>

import std.stdio;
import std.math;

void main()
{
    auto sa = S.constructorA(3,4);
    auto sb = S.constructorB(3,4);
    writeln(sa);
    writeln(sb);
}


struct S {
public:
   this(string name: "constructorA")(real x, real y)
   {
       this.x = x;
       this.y = y;
   }

   this(string name: "constructorB")(real x, real z)
   {
       this.x = x;
       this.z = z;
   }

   static constructorA(Args...)(Args args)
   {
       S s;
       s.__ctor!"constructorA"(args);
       return s;
   }

   static constructorB(Args...)(Args args)
   {
       S s;
       s.__ctor!"constructorB"(args);
       return s;
   }
   const real x, y, z;
}


January 09
On Wednesday, 9 January 2019 at 18:19:35 UTC, Daniel Kozak wrote:
> On Wed, Jan 9, 2019 at 5:50 PM Neia Neutuladh via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
>
>>[...]
>
> import std.stdio;
> import std.math;
>
> void main()
> {
>     auto sa = S.constructorA(3,4);
>     auto sb = S.constructorB(3,4);
>     writeln(sa);
>     writeln(sb);
> }
>
>
> struct S {
> public:
>    this(string name: "constructorA")(real x, real y)
>    {
>        this.x = x;
>        this.y = y;
>    }
>
>    this(string name: "constructorB")(real x, real z)
>    {
>        this.x = x;
>        this.z = z;
>    }
>
>    static constructorA(Args...)(Args args)
>    {
>        S s;
>        s.__ctor!"constructorA"(args);
>        return s;
>    }
>
>    static constructorB(Args...)(Args args)
>    {
>        S s;
>        s.__ctor!"constructorB"(args);
>        return s;
>    }
>    const real x, y, z;
> }
That solution has a compile time cost.
January 09
On Wednesday, 9 January 2019 at 18:24:28 UTC, 12345swordy wrote:
> That solution has a compile time cost.

I do not see any compile time cost, what do you mean?


January 09
On Wednesday, 9 January 2019 at 10:02:12 UTC, bauss wrote:
>
> I like this approach to it and you're entirely correct.
>
> It would be nice with support for this in the language without having to create factory methods.
>
> Basically something like the following could be lowered to your code.

I was going to say that this post will be mostly ignored and people will present some template magic to partially implement such feature (just add few imports and some boilerplate code), but I guess I got beaten to it :)
« First   ‹ Prev
1 2 3 4 5 6