www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Struct "inheritance"

reply Vidar Wahlberg <canidae exent.net> writes:
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.
Feb 04 2012
next sibling parent Trass3r <un known.com> writes:
 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".
Feb 04 2012
prev sibling next sibling parent reply =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
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; }
Feb 04 2012
next sibling parent reply Vidar Wahlberg <canidae exent.net> writes:
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.
Feb 04 2012
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
Feb 04 2012
prev sibling parent reply Vidar Wahlberg <canidae exent.net> writes:
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)
Feb 04 2012
parent reply Vidar Wahlberg <canidae exent.net> writes:
Sending this again, got an error the first time and it appears like it 
was not sent.

On 2012-02-04 21:48, Simen Kjærås wrote:
 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;

I see, thanks, that does solve the problem. It is slightly confusing that you don't get this kind of error for "Foo" in "Bar.d", though (compiler seems to understand that I mean the class "Foo" rather than the module "Foo" in this code: "_foo = new Foo(Struct(1))"). Also, is this really ambiguous? Are there any cases where you can have a module name followed by a parentheses, i.e. "<module>("?
 I cannot seem to recreate this error message. Which version of the
 compiler are you using?

I'm using gdc-4.6 (Debian 4.6.2-4). Using the Struct from above I can easily recreate the error: struct Struct { int baz; bool opEquals(const Struct s) const { return baz == s.baz; } }
Feb 05 2012
parent reply Vidar Wahlberg <canidae exent.net> writes:
On 2012-02-05 14:16, Simen Kjærås wrote:
 On Sun, 05 Feb 2012 11:58:40 +0100, Vidar Wahlberg <canidae exent.net>
 wrote:
 Also, is this really ambiguous? Are there any cases where you can have
 a module name followed by a parentheses, i.e. "<module>("?

Not that I know.

Possibly something that could make the language slightly more newbie friendly here, then. For now I'll just keep the filenames in lowercase so i "import struct;" rather than "import Struct;" (I see the norm for D is to keep the filenames in lowercase, might as well follow it). Adding a note about GDC (4.6.2) here: It appears like it ignores "module <name>;" and always use the filename for module name (or I've misunderstood what "module" is used for). If I create a file "Foo.d" which contains "module foo;", then in any other file I wish to include module "foo" in I must write "include Foo;", not "include foo;".
 I'm using gdc-4.6 (Debian 4.6.2-4).

Ah. That's the equivalent of DMD 2.054. I don't have that installed, but it may be that this feature was not added until after that. Fix: install GDC 4.6.1: https://bitbucket.org/goshawk/gdc/downloads

I'm running GDC 4.6.2, not 4.6.0 (I just pasted output from "gdc-4.6 --version", I could've made it clearer), downgrading probably won't help me.
 Workaround: Use a templated opEquals:

 struct Struct {
 int baz;
 bool opEquals()(const Struct s) const {
 return baz == s.baz;
 }
 }

 Hope this works.

Yes, it does. I have to read a bit about templating as I don't understand exactly what that "()" means, but it did solve my problem, thanks!
Feb 05 2012
parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Vidar Wahlberg" <canidae exent.net> wrote in message 
news:jgm2qk$c2g$1 digitalmars.com...
 Adding a note about GDC (4.6.2) here:
 It appears like it ignores "module <name>;" and always use the filename 
 for module name (or I've misunderstood what "module" is used for). If I 
 create a file "Foo.d" which contains "module foo;", then in any other file 
 I wish to include module "foo" in I must write "include Foo;", not 
 "include foo;".

The names only need to match if the compiler/build tool has to find the module itself. If you call the compiler with all modules listed: gdc bar.d Foo.d etc.d then it should be able to work it out. (This is how it works with dmd, anyway. GDC is probably the same)
Feb 05 2012
parent reply Vidar Wahlberg <canidae exent.net> writes:
On 2012-02-05 15:19, Daniel Murphy wrote:
 The names only need to match if the compiler/build tool has to find the
 module itself.  If you call the compiler with all modules listed:
 gdc bar.d Foo.d etc.d
 then it should be able to work it out.  (This is how it works with dmd,
 anyway.  GDC is probably the same)

Actually, that is what I do. GDC does not seem to figure it out: naushika:~/tmp> cat Foo.d module foo; ... naushika:~/tmp> gdc-4.6 bar.d Foo.d baz.d bar.d:2: Error: module foo is in file 'foo.d' which cannot be read import path[0] = /usr/include/d2/4.6/x86_64-linux-gnu import path[1] = /usr/include/d2/4.6 naushika:~/tmp> mv Foo.d foo.d naushika:~/tmp> gdc-4.6 bar.d foo.d baz.d naushika:~/tmp>
Feb 05 2012
parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
I guess you've found a bug then. :)

"Vidar Wahlberg" <canidae exent.net> wrote in message 
news:jgm7sh$k4u$1 digitalmars.com...
 On 2012-02-05 15:19, Daniel Murphy wrote:
 The names only need to match if the compiler/build tool has to find the
 module itself.  If you call the compiler with all modules listed:
 gdc bar.d Foo.d etc.d
 then it should be able to work it out.  (This is how it works with dmd,
 anyway.  GDC is probably the same)

Actually, that is what I do. GDC does not seem to figure it out: naushika:~/tmp> cat Foo.d module foo; ... naushika:~/tmp> gdc-4.6 bar.d Foo.d baz.d bar.d:2: Error: module foo is in file 'foo.d' which cannot be read import path[0] = /usr/include/d2/4.6/x86_64-linux-gnu import path[1] = /usr/include/d2/4.6 naushika:~/tmp> mv Foo.d foo.d naushika:~/tmp> gdc-4.6 bar.d foo.d baz.d naushika:~/tmp>

Feb 05 2012
prev sibling next sibling parent =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Sat, 04 Feb 2012 13:55:55 +0100, Vidar Wahlberg <canidae exent.net>  =

wrote:

 On 2012-02-04 13:06, Simen Kj=C3=A6r=C3=A5s 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 =3D {x, y}; somethingNifty(p); } } [How] can you rewrite those two statements inside the loops to a singl=

 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 =3D point;
    }
     property auto z() const {
      return _z;
    }
     property auto z(int z) {
      return _z =3D z;
    }
    bool opEquals(ref const Coordinate c) const {
      return z =3D=3D c.z && point =3D=3D 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 compil=

 (gdc-4.6) says:
 Error: function Coordinate.Coordinate.opEquals type signature should b=

 const bool(ref const(Coordinate)) not int(ref const const(Coordinate) =

 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 Poin= t by reference, and the return value from your property is a temporary, fr= om 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 do= wn on copying, and might be worthwhile on larger structures.
Feb 04 2012
prev sibling next sibling parent =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
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
Feb 04 2012
prev sibling parent =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Sun, 05 Feb 2012 11:58:40 +0100, Vidar Wahlberg <canidae exent.net>  
wrote:

 Also, is this really ambiguous? Are there any cases where you can have a  
 module name followed by a parentheses, i.e. "<module>("?

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

 I'm using gdc-4.6 (Debian 4.6.2-4).
 Using the Struct from above I can easily recreate the error:
 struct Struct {
    int baz;
    bool opEquals(const Struct s) const {
      return baz == s.baz;
    }
 }

Ah. That's the equivalent of DMD 2.054. I don't have that installed, but it may be that this feature was not added until after that. Fix: install GDC 4.6.1: https://bitbucket.org/goshawk/gdc/downloads Workaround: Use a templated opEquals: struct Struct { int baz; bool opEquals()(const Struct s) const { return baz == s.baz; } } Hope this works.
Feb 05 2012
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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
Feb 04 2012
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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
Feb 04 2012