Jump to page: 1 2
Thread overview
Struct "inheritance"
Feb 04, 2012
Vidar Wahlberg
Feb 04, 2012
Trass3r
Feb 04, 2012
Simen Kjærås
Feb 04, 2012
Vidar Wahlberg
Feb 04, 2012
bearophile
Feb 04, 2012
Simen Kjærås
Feb 04, 2012
Vidar Wahlberg
Feb 04, 2012
Simen Kjærås
Feb 05, 2012
Vidar Wahlberg
Feb 05, 2012
Simen Kjærås
Feb 05, 2012
Vidar Wahlberg
Feb 05, 2012
Daniel Murphy
Feb 05, 2012
Vidar Wahlberg
Feb 05, 2012
Daniel Murphy
Feb 04, 2012
Ali Çehreli
Feb 04, 2012
Ali Çehreli
February 04, 2012
Good day.

I know "inheritance" is a misleading word as there's no such thing when it comes to structs, but I couldn't think of a better description for this problem:

Let's say I got a struct for a location on a 2-dimensional plane:
struct Point {
  int x;
  int y;
}
Further I also need to represent a location in a 3-dimensional space:
struct Coordinate {
  int x;
  int y;
  int z;
}
If these were classes instead I could simply make Coordinate inherit from Point and only add "int z;". This would also make the thing I'm trying to achieve much easier; Consider I have a method that does something nifty with "x" and "y", I'd like this method to handle both "Point" and "Coordinate", if they were classes it would be fairly simple:
int somethingNifty(Point p) {
  return p.x + p.y;
}


So why not just use classes? I've understood it as there may be a performance gain by using structs over classes, and in my program Point and Coordinate are used heavily.

Obviously I'm fairly new to both D and structs, I have plenty Java and some C++ experience (I've avoided using structs in C++) and I have "The D Programming Language" book by Andrei which I'm happy to look up in, although I've failed to find an answer to this question in the book, in this newsgroup and on the net.
I would greatly appreciate if someone could give me a nudge the the right direction.
February 04, 2012
> So why not just use classes? I've understood it as there may be a performance gain by using structs over classes, and in my program Point and Coordinate are used heavily.

The other big difference is value vs. reference type.
You can use alias this to achieve something like "struct inheritance".
February 04, 2012
On Sat, 04 Feb 2012 12:38:30 +0100, Vidar Wahlberg <canidae@exent.net> wrote:

> Good day.
>
> I know "inheritance" is a misleading word as there's no such thing when it comes to structs, but I couldn't think of a better description for this problem:
>
> Let's say I got a struct for a location on a 2-dimensional plane:
> struct Point {
>    int x;
>    int y;
> }
> Further I also need to represent a location in a 3-dimensional space:
> struct Coordinate {
>    int x;
>    int y;
>    int z;
> }
> If these were classes instead I could simply make Coordinate inherit from Point and only add "int z;". This would also make the thing I'm trying to achieve much easier; Consider I have a method that does something nifty with "x" and "y", I'd like this method to handle both "Point" and "Coordinate", if they were classes it would be fairly simple:
> int somethingNifty(Point p) {
>    return p.x + p.y;
> }
>
>
> So why not just use classes? I've understood it as there may be a performance gain by using structs over classes, and in my program Point and Coordinate are used heavily.
>
> Obviously I'm fairly new to both D and structs, I have plenty Java and some C++ experience (I've avoided using structs in C++) and I have "The D Programming Language" book by Andrei which I'm happy to look up in, although I've failed to find an answer to this question in the book, in this newsgroup and on the net.
> I would greatly appreciate if someone could give me a nudge the the right direction.

It seems that what you want is alias this:

struct Point {
    int x;
    int y;
}

struct Coordinate {
    Point pt;
    int z;
    alias pt this;
}

void foo( Point p ) {}

void main( ) {
    Coordinate c;
    foo( c );
    c.x = 3;
    c.y = 4;
    c.z = 5;
}
February 04, 2012
On 2012-02-04 13:06, Simen Kjærås wrote:
> It seems that what you want is alias this:

Thank you both, that's exactly what I needed.

Leeching a bit more on the thread:
Going back to the method:
int somethingNifty(Point p) {
  return p.x + p.y;
}

Let's say I have the following code:
for (x; 0 .. 10) {
  for (y; 0 .. 10) {
    Point p = {x, y};
    somethingNifty(p);
  }
}

[How] can you rewrite those two statements inside the loops to a single line? For example (this doesn't work):
somethingNifty(Point(x, y));


And finally a question about operator overloading, here's the code for "Coordinate" ("Point" is similar in structure):
import Point;
struct Coordinate {
  Point _point;
  int _z;
  @property auto point() const {
    return _point;
  }
  @property auto point(Point point) {
    return _point = point;
  }
  @property auto z() const {
    return _z;
  }
  @property auto z(int z) {
    return _z = z;
  }
  bool opEquals(ref const Coordinate c) const {
    return z == c.z && point == c.point;
  }
}

Compilation fails with the following error:
Coordinate.d:18: Error: function Point.Point.opEquals (ref const const(Point) p) const is not callable using argument types (const(Point)) const
Coordinate.d:18: Error: c.point() is not an lvalue

Noteworthy the code compiles fine if i replace "point" and "c.point" with "_point" and "c._point", but then I'm referencing _point directly instead of through the property "point" (which may do something else than just return _point in the future).
I've looked at http://www.d-programming-language.org/operatoroverloading.html#equals and that page is slightly confusing. It claims that:
If structs declare an opEquals member function, it should follow the following form:
struct S {
  int opEquals(ref const S s) { ... }
}

However, I can't even get the code to compile if I do that, the compiler (gdc-4.6) says:
Error: function Coordinate.Coordinate.opEquals type signature should be const bool(ref const(Coordinate)) not int(ref const const(Coordinate) c)


I hope it's somewhat clear what I'm trying to achieve.
February 04, 2012
Vidar Wahlberg:

> Leeching a bit more on the thread:
> Going back to the method:
> int somethingNifty(Point p) {
>    return p.x + p.y;
> }
> 
> Let's say I have the following code:
> for (x; 0 .. 10) {
>    for (y; 0 .. 10) {
>      Point p = {x, y};
>      somethingNifty(p);
>    }
> }
> 
> [How] can you rewrite those two statements inside the loops to a single
> line? For example (this doesn't work):
> somethingNifty(Point(x, y));

This works:

struct Point {
    int x, y;
}

int somethingNifty(Point p) {
    return p.x + p.y;
}

void main( ) {
    foreach (x; 0 .. 10)
        foreach (y; 0 .. 10)
            somethingNifty(Point(x, y));
}

Bye,
bearophile
February 04, 2012
On Sat, 04 Feb 2012 13:55:55 +0100, Vidar Wahlberg <canidae@exent.net> wrote:

> On 2012-02-04 13:06, Simen Kjærås wrote:
>> It seems that what you want is alias this:
>
> Thank you both, that's exactly what I needed.
>
> Leeching a bit more on the thread:
> Going back to the method:
> int somethingNifty(Point p) {
>    return p.x + p.y;
> }
>
> Let's say I have the following code:
> for (x; 0 .. 10) {
>    for (y; 0 .. 10) {
>      Point p = {x, y};
>      somethingNifty(p);
>    }
> }
>
> [How] can you rewrite those two statements inside the loops to a single line? For example (this doesn't work):
> somethingNifty(Point(x, y));

Like bearophile said, Point(x, y) should work - assuming you have
defined no other constructors for Point.

You can also explicitly define a constructor that does what you want.


> And finally a question about operator overloading, here's the code for "Coordinate" ("Point" is similar in structure):
> import Point;
> struct Coordinate {
>    Point _point;
>    int _z;
>    @property auto point() const {
>      return _point;
>    }
>    @property auto point(Point point) {
>      return _point = point;
>    }
>    @property auto z() const {
>      return _z;
>    }
>    @property auto z(int z) {
>      return _z = z;
>    }
>    bool opEquals(ref const Coordinate c) const {
>      return z == c.z && point == c.point;
>    }
> }
>
> Compilation fails with the following error:
> Coordinate.d:18: Error: function Point.Point.opEquals (ref const const(Point) p) const is not callable using argument types (const(Point)) const
> Coordinate.d:18: Error: c.point() is not an lvalue
>
> Noteworthy the code compiles fine if i replace "point" and "c.point" with "_point" and "c._point", but then I'm referencing _point directly instead of through the property "point" (which may do something else than just return _point in the future).
> I've looked at http://www.d-programming-language.org/operatoroverloading.html#equals and that page is slightly confusing. It claims that:
> If structs declare an opEquals member function, it should follow the following form:
> struct S {
>    int opEquals(ref const S s) { ... }
> }
>
> However, I can't even get the code to compile if I do that, the compiler (gdc-4.6) says:
> Error: function Coordinate.Coordinate.opEquals type signature should be const bool(ref const(Coordinate)) not int(ref const const(Coordinate) c)
>
>
> I hope it's somewhat clear what I'm trying to achieve.

It is. The problem is that bool opEquals(ref const Point) expects a Point
by reference, and the return value from your property is a temporary, from
which we can get no reference. The solution is to remove the ref:
bool opEquals(const Point). The only reason to use ref here is to cut down
on copying, and might be worthwhile on larger structures.
February 04, 2012
On 2012-02-04 14:45, Simen Kjærås wrote:
> Like bearophile said, Point(x, y) should work - assuming you have
> defined no other constructors for Point.

You are correct, my apologies for not testing more thoroughly.
Let me try again, explaining the real issue:
I have 3 files:
-- Bar.d --
import Foo;
import Struct;
class Bar {
  Foo _foo;
  this() {
    _foo = new Foo(Struct(1));
  }
}
void main() {
  new Bar();
}

-- Foo.d --
import Struct;
class Foo {
  Struct _s;
  this(Struct s) {
    _s = s;
  }
}

-- Struct.d --
struct Struct {
  int baz;
}

This code does not compile:
Bar.d:6: Error: function expected before (), not module Struct of type void
Bar.d:6: Error: constructor Foo.Foo.this (Struct s) is not callable using argument types (_error_)

Why is that?


> It is. The problem is that bool opEquals(ref const Point) expects a Point
> by reference, and the return value from your property is a temporary, from
> which we can get no reference. The solution is to remove the ref:
> bool opEquals(const Point). The only reason to use ref here is to cut down
> on copying, and might be worthwhile on larger structures.

I've tried removing the "ref", but I get other errors in return then:
Point.d:19: Error: function Point.Point.opEquals type signature should be const bool(ref const(Point)) not const bool(const const(Point) p)
Coordinate.d:20: Error: function Coordinate.Coordinate.opEquals type signature should be const bool(ref const(Coordinate)) not const bool(const const(Coordinate) c)
February 04, 2012
On 02/04/2012 03:38 AM, Vidar Wahlberg wrote:

> Let's say I got a struct for a location on a 2-dimensional plane:
> struct Point {
> int x;
> int y;
> }
> Further I also need to represent a location in a 3-dimensional space:
> struct Coordinate {
> int x;
> int y;
> int z;
> }
> If these were classes instead I could simply make Coordinate inherit
> from Point and only add "int z;".

There is also template mixins to inject complete features into the code (similar to C macros but without their gotchas).

The following templatizes the coordinate types, but you could use put write everywhere:

import std.stdio;

template Point2D(T)
{
    T x;
    T y;

    void foo2D()
    {
        writefln("Using (%s,%s)", x, y);
    }
}

struct Coordinate(T)
{
    mixin Point2D!T;   // <-- Inject x, y, and foo2D() here
    T z;

    this(T x, T y, T z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

void main()
{
    auto c = Coordinate!double(1.1, 2.2, 3.3);
    c.foo2D();
}

You could insert the following line in any other scope and you would have x, y, foo2D() inserted right there as well:

    if (someCondition) {
        mixin Point2D!T;   // <-- Inject x, y, and foo2D() here

        // ... use the local x, y, and foo2D() here
    }

Of course 'static if' may be even more suitable in other cases.

Ali

February 04, 2012
On 02/04/2012 08:25 AM, Ali Çehreli wrote:
> The following templatizes the coordinate types, but you could use put
> write everywhere:

That should be "... you could write *ints* everywhere".

Ali
February 04, 2012
On Sat, 04 Feb 2012 15:04:50 +0100, Vidar Wahlberg <canidae@exent.net> wrote:

> This code does not compile:
> Bar.d:6: Error: function expected before (), not module Struct of type void
> Bar.d:6: Error: constructor Foo.Foo.this (Struct s) is not callable using argument types (_error_)
>
> Why is that?

I see. There's a hint in the error message: "function expected [...],
not module". Struct is the name of a module, so the compiler thinks
you want to access something inside it. If you want 'Struct' to refer
to the type, you must use selective import[1]:

import Struct : Struct;

This says 'I want only the type Struct from the module Struct, not
everything else in there.'

The other solution is to simply use different names for the type and
the module.


>> It is. The problem is that bool opEquals(ref const Point) expects a Point
>> by reference, and the return value from your property is a temporary, from
>> which we can get no reference. The solution is to remove the ref:
>> bool opEquals(const Point). The only reason to use ref here is to cut down
>> on copying, and might be worthwhile on larger structures.
>
> I've tried removing the "ref", but I get other errors in return then:
> Point.d:19: Error: function Point.Point.opEquals type signature should be const bool(ref const(Point)) not const bool(const const(Point) p)
> Coordinate.d:20: Error: function Coordinate.Coordinate.opEquals type signature should be const bool(ref const(Coordinate)) not const bool(const const(Coordinate) c)

I cannot seem to recreate this error message. Which version of the
compiler are you using?

[1]: http://www.d-programming-language.org/module.html#ImportDeclaration
« First   ‹ Prev
1 2