www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Inheritance for structs

reply "Uwe Salomon" <post uwesalomon.de> writes:
Hi,

what exactly are the objections against simple inheritance mechanisms  
(without virtual functions, RTTI or something like that) for structs? That  
would make it easy to base a struct upon an existing one without the need  
to rewrite/delegate existing functions. Has this been a discussion subject  
before?

Thanks
uwe
May 12 2005
next sibling parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Has this been a discussion subject before?"
I am pretty sure - yes.

If I need two structures to have common members then
I am using mixins for defininig common parts of such
structures. Well, of course it is a palliative , but mostly works.

Sample:

template BrickT()
{
    ushort  type;
    short   posx;
    short   posy;
    ushort  dimx;
    ushort  dimy;
    short   ascent;

    rect     place() { return rect( pos, dim ); }
    point    pos()   { return point( posx, posy); }
    size     dim()   { return size( dimx, dimy); }
}

  align(2) struct Brick // non breakable layout element
  {
    mixin BrickT;
  }

  align(2) struct Word // word or space
  {
    mixin   BrickT;
    ushort  length; // total length in wchars
    // wchars are here....
  }

Andrew.


"Uwe Salomon" <post uwesalomon.de> wrote in message 
news:op.sqnsqpu96yjbe6 sandmann.maerchenwald.net...
 Hi,

 what exactly are the objections against simple inheritance mechanisms 
 (without virtual functions, RTTI or something like that) for structs? That 
 would make it easy to base a struct upon an existing one without the need 
 to rewrite/delegate existing functions. Has this been a discussion subject 
 before?

 Thanks
 uwe 

May 12 2005
prev sibling parent reply Chris Sauls <ibisbasenji gmail.com> writes:
Andrew's use of mixins is one good method.  Another is to use inner 
structs and aliases.  For example:

#
# struct Size {
#   uint width, height;
# }
#
# struct Point {
#   uint x, y;
# }
#
# struct Rect {
#   Size size;
#   alias size.width width;
#   alias size.height height;
#
#   Point point;
#   alias point.x x;
#   alias point.y y;
# }
#

The one thing that is missing from either method, and is therefore the 
one thing I personally would like to see struct-inheritance for, is 
hierarchial type resolution.  In other words, I couldn't declare a 
function to take a parameter of type Size and then pass it a Rect.  I 
think the real stopping point probably boils down to virtual functions, 
but there might be another way?

-- Chris Sauls
May 12 2005
parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 In other words, I couldn't declare a function to take a parameter of  
 type Size and then pass it a Rect. I
 think the real stopping point probably boils down to virtual functions,  
 but there might be another way?

If i needed virtual functions and all that stuff, i would use a class. I think it is a good idea that structs don't have that ballast. All i am talking of is a simple way to base a struct upon another struct. If it were just for three member variables and a function, i would use aliases. But i have structs with a lot of functions, 30 or 50 or so. They are structs because they *need* value semantics. I don't want to write aliases for all these functions, for all these structs. And using mixins is just so clumsy with 2000 lines of code in them, and not very clear style either. uwe
May 12 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Uwe Salomon" <post uwesalomon.de> wrote in message 
news:op.sqoid3tj6yjbe6 sandmann.maerchenwald.net...
 In other words, I couldn't declare a function to take a parameter of 
 type Size and then pass it a Rect. I
 think the real stopping point probably boils down to virtual functions, 
 but there might be another way?

If i needed virtual functions and all that stuff, i would use a class. I think it is a good idea that structs don't have that ballast. All i am talking of is a simple way to base a struct upon another struct. If it were just for three member variables and a function, i would use aliases. But i have structs with a lot of functions, 30 or 50 or so. They are structs because they *need* value semantics. I don't want to write aliases for all these functions, for all these structs. And using mixins is just so clumsy with 2000 lines of code in them, and not very clear style either. uwe

I'm curious about the context - can you describe the problem in more detail? That's a pretty honkin' struct.
May 12 2005
parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 If i needed virtual functions and all that stuff, i would use a class. I
 think it is a good idea that structs don't have that ballast. All i am
 talking of is a simple way to base a struct upon another struct. If it
 were just for three member variables and a function, i would use  
 aliases.
 But i have structs with a lot of functions, 30 or 50 or so. They are
 structs because they *need* value semantics. I don't want to write  
 aliases
 for all these functions, for all these structs. And using mixins is just
 so clumsy with 2000 lines of code in them, and not very clear style
 either.

I'm curious about the context - can you describe the problem in more detail? That's a pretty honkin' struct.

:) Well, the idea behind it is just the same as with MinTL: Containers should be structs to reduce overhead and, more important, to gain value semantics. That makes containers in containers possible: Map!(char[], Vector!(int)) funnyMap; If Vector was a template class, all the vectors would need initialization (-- not sure if this is correct grammar, i have problems with the conjunctive... --), because they are null by default. That fact made all my containers (Vector, List, LinkedList, Map) structs, just like yours in MinTL. They are quite big, as you can imagine (lots of convenience functions and algorithms, sorting, etc.). Now if i want to write a Queue for example, that's just a special LinkedList (though that's not the best way to implement a queue, yes): struct Queue(T) : LinkedList!(T) { public void enqueue(T value) { append(value); } // Or better, with aliases: alias takeLast dequeue; } That gives me a nice queue, with minimal effort and all the good things from LinkedList. This could be realized with the technique you used, yes (enqueue and dequeue in LinkedList, and Queue is just an alias), but look at the following example: struct StringList : public List!(char[]) // Actually, char[] would be a struct String. { public char[] join() { // Not bad, but could be implemented a bit better: size_t fullLength = 0; foreach (char[] str; *this) fullLength += str.length; char[] result; result.length = fullLength; fullLength = 0; foreach (char[] str; *this) { result[fullLength .. (fullLength + str.length)] = str[]; fullLength += str.length; } } // Other functions, like join(char[] sep) ... } I cannot program that functionality into List, neither do i want to reimplement List!(char[]). All i have left is extracting the List implementation into a mixin, and inserting this mixin into List and StringList (or mess around with partial specialization, even worse). Though that is possible, it is not very straightforward and clear style. The above is such a simple idea, but to realize it i need to come up with complex templates, mixins and whatever is possible in D. That's why i asked for struct inheritance. But, as it is unlikely that Walter changes it, are there some clever solutions for the StringList that come to your mind? As i see it, it is not possible to imitate the behaviour of QStringList here... :( Ciao uwe
May 13 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Uwe Salomon" <post uwesalomon.de> wrote in message 
news:op.sqppz5wb6yjbe6 sandmann.maerchenwald.net...
 If i needed virtual functions and all that stuff, i would use a class. I
 think it is a good idea that structs don't have that ballast. All i am
 talking of is a simple way to base a struct upon another struct. If it
 were just for three member variables and a function, i would use 
 aliases.
 But i have structs with a lot of functions, 30 or 50 or so. They are
 structs because they *need* value semantics. I don't want to write 
 aliases
 for all these functions, for all these structs. And using mixins is just
 so clumsy with 2000 lines of code in them, and not very clear style
 either.

I'm curious about the context - can you describe the problem in more detail? That's a pretty honkin' struct.

:) Well, the idea behind it is just the same as with MinTL: Containers should be structs to reduce overhead and, more important, to gain value semantics. That makes containers in containers possible: Map!(char[], Vector!(int)) funnyMap; If Vector was a template class, all the vectors would need initialization (-- not sure if this is correct grammar, i have problems with the conjunctive... --), because they are null by default. That fact made all my containers (Vector, List, LinkedList, Map) structs, just like yours in MinTL. They are quite big, as you can imagine (lots of convenience functions and algorithms, sorting, etc.).

ok, that makes sense to me now. I actually thought you had wanted to inherit data members and not function members. You are right that it would be useful to allow "virtual functions" for structs. I guess
 Now if i want to write a Queue  for example, that's just a special 
 LinkedList (though that's not the best  way to implement a queue, yes):

 struct Queue(T) : LinkedList!(T)
 {
   public void enqueue(T value)
   {
     append(value);
   }

   // Or better, with aliases:
   alias takeLast dequeue;

hmm - MinTL has "removeLast"... I think I actually like "takeLast" better.
 }

 That gives me a nice queue, with minimal effort and all the good things 
 from LinkedList. This could be realized with the technique you used, yes 
 (enqueue and dequeue in LinkedList, and Queue is just an alias), but look 
 at the following example:

 struct StringList : public List!(char[])
 // Actually, char[] would be a struct String.
 {
   public char[] join()
   {
     // Not bad, but could be implemented a bit better:
     size_t fullLength = 0;
     foreach (char[] str; *this)
       fullLength += str.length;

     char[] result;
     result.length = fullLength;
     fullLength = 0;
     foreach (char[] str; *this)
     {
       result[fullLength .. (fullLength + str.length)] = str[];
       fullLength += str.length;
     }
   }

   // Other functions, like join(char[] sep) ...
 }

Thinking off the top of my head to write join() today you'd have to put it on the top level instead, correct? Generalizing it to join any container of lists might not be a bad things, though. If the container supports foreach one could write template join(T, Container) { T[] join(Container c){ size_t n=0; foreach(T[] val; c) { n += val.length; } T[] res = new T[n]; n = 0; foreach(T[] val; c) { res[n .. n+val.length] = val[]; n+= val.length; } return res; } } and then call it like alias List!(char[]) StringList; alias join!(char,StringList) strjoin; // for simpler user code StringList strs; ... char[] together = strjoin(strs); ArrayList!(int[]) ints; ... int[] together2 = join!(int,typeof(ints))(ints); I might include some handy things like that into MinTL.
 I cannot program that functionality into List, neither do i want to 
 reimplement List!(char[]). All i have left is extracting the List 
 implementation into a mixin, and inserting this mixin into List and 
 StringList (or mess around with partial specialization, even worse). 
 Though that is possible, it is not very straightforward and clear style. 
 The above is such a simple idea, but to realize it i need to come up with 
 complex templates, mixins and whatever is possible in D. That's why i 
 asked for struct inheritance.

I don't completely agree that mixins are not straightforward or have a clear style (if I understand your position). It's pretty much the same to me if a type gets features from inheritance or from a mixin. The difference is only syntactic, really: struct Foo : Bar { } struct Foo { mixin Bar } unless you want to allow Foos to be implicitly cast to Bars (mixins wouldn't be able to do that).
 But, as it is unlikely that Walter changes it, are there some clever 
 solutions for the StringList that come to your mind? As i see it, it is 
 not possible to imitate the behaviour of QStringList here... :(

 Ciao
 uwe 

May 13 2005
parent "Uwe Salomon" <post uwesalomon.de> writes:
   // Or better, with aliases:
   alias takeLast dequeue;

hmm - MinTL has "removeLast"... I think I actually like "takeLast" better.

:) I have both of 'em, removeLast only removes, takeLast removes and returns it.
 char[] together = strjoin(strs);

I have to admit that this is really neat. :)
 It's pretty much the same to me if a type gets features from inheritance  
 or from a mixin.

Perhaps i will go this way, then. Or make it global. Ah, well, D is not C++, and Indigo is not Qt. Thanks & ciao uwe
May 13 2005