www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - static data in a sub-class

reply "Steve D" <whatchcallamit weedline.com> writes:
Hello,
How can I get class B's array printed, using inherited A's 
function? (Dmd 2.060)

     class A {
         static float[3] hi = 1;

         void f() { writefln("hi %s",hi); }
     }

     class B : A {
         static float[3] hi = 2;
     }

     B b = new B();
     b.f();     // prints 'hi [1,1,1]'
                // can I get it to use B's overridden hi: [2,2,2] ?

Thanks for your help :)
Steve
Dec 22 2012
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, December 22, 2012 21:44:33 Steve D wrote:
 Hello,
 How can I get class B's array printed, using inherited A's
 function? (Dmd 2.060)
 
      class A {
          static float[3] hi = 1;
 
          void f() { writefln("hi %s",hi); }
      }
 
      class B : A {
          static float[3] hi = 2;
      }
 
      B b = new B();
      b.f();     // prints 'hi [1,1,1]'
                 // can I get it to use B's overridden hi: [2,2,2] ?
 
 Thanks for your help :)
 Steve
Functions can be overriden. Variables can't be. So, when hi is used, it'll be the one in the class that it's used in unless you specifically specify the one to use (e.g. A.hi or B.hi), and simply changing hi to a function won't help you, because static functions are never virtual (they have no this pointer, so there's no way to look up which version of the function to call). So, if you want hi to be overridable, it'll have to be a public or protected member function rather than a variable or static function. - Jonathan M Davis
Dec 22 2012
parent "Steve D" <whatchcallamit weedline.com> writes:
On Saturday, 22 December 2012 at 21:04:47 UTC, Jonathan M Davis 
wrote:
 On Saturday, December 22, 2012 21:44:33 Steve D wrote:
 Hello,
 How can I get class B's array printed, using inherited A's
 function? (Dmd 2.060)
 
      class A {
          static float[3] hi = 1;
 
          void f() { writefln("hi %s",hi); }
      }
 
      class B : A {
          static float[3] hi = 2;
      }
 
      B b = new B();
      b.f();     // prints 'hi [1,1,1]'
                 // can I get it to use B's overridden hi: 
 [2,2,2] ?
 
 Thanks for your help :)
 Steve
Functions can be overriden. Variables can't be. So, when hi is used, it'll be the one in the class that it's used in unless you specifically specify the one to use (e.g. A.hi or B.hi), and simply changing hi to a function won't help you, because static functions are never virtual (they have no this pointer, so there's no way to look up which version of the function to call). So, if you want hi to be overridable, it'll have to be a public or protected member function rather than a variable or static function. - Jonathan M Davis
Ok, thanks for the info John. Steve
Dec 22 2012
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 12/22/2012 12:44 PM, Steve D wrote:
 Hello,
 How can I get class B's array printed, using inherited A's function?
 (Dmd 2.060)

 class A {
 static float[3] hi = 1;

 void f() { writefln("hi %s",hi); }
 }

 class B : A {
 static float[3] hi = 2;
 }

 B b = new B();
 b.f(); // prints 'hi [1,1,1]'
 // can I get it to use B's overridden hi: [2,2,2] ?

 Thanks for your help :)
 Steve
One way is to present the data to the superclass during construction: import std.stdio; class A { const float[] hi; this(float[] hi) { this.hi = hi; } void f() { writefln("hi %s",hi); } } class B : A { static float[3] hi = 2; this() { super(hi); } } void main() { B b = new B(); b.f(); } Another ways is for A to require that the subclasses present the data by a member function: import std.stdio; class A { abstract float[] hi_data(); void f() { writefln("hi %s", hi_data()); } } class B : A { static float[3] hi = 2; override float[] hi_data() { return hi; } } void main() { B b = new B(); b.f(); } Orthogonally, if f() is strictly on the A interface, then you can make it a final function: final void f() { writefln("hi %s", hi_data()); } Ali
Dec 22 2012
parent reply "Steve D" <whatchcallamit weedline.com> writes:
On Saturday, 22 December 2012 at 21:14:58 UTC, Ali Çehreli wrote:
 On 12/22/2012 12:44 PM, Steve D wrote:
 Hello,
 How can I get class B's array printed, using inherited A's 
 function?
 (Dmd 2.060)

 class A {
 static float[3] hi = 1;

 void f() { writefln("hi %s",hi); }
 }

 class B : A {
 static float[3] hi = 2;
 }

 B b = new B();
 b.f(); // prints 'hi [1,1,1]'
 // can I get it to use B's overridden hi: [2,2,2] ?

 Thanks for your help :)
 Steve
One way is to present the data to the superclass during construction: import std.stdio; class A { const float[] hi; this(float[] hi) { this.hi = hi; } void f() { writefln("hi %s",hi); } } class B : A { static float[3] hi = 2; this() { super(hi); } } void main() { B b = new B(); b.f(); } Another ways is for A to require that the subclasses present the data by a member function: import std.stdio; class A { abstract float[] hi_data(); void f() { writefln("hi %s", hi_data()); } } class B : A { static float[3] hi = 2; override float[] hi_data() { return hi; } } void main() { B b = new B(); b.f(); } Orthogonally, if f() is strictly on the A interface, then you can make it a final function: final void f() { writefln("hi %s", hi_data()); } Ali
Thanks for the options Ali, I could in fact just make the array a parameter to f(), but I have lots of these calls to f() and similar. I could also just duplicate f() in all my sub-classes, but it's just code duplication. I will continue playing with your suggestions etc to see what's cleanest. Thanks Steve
Dec 22 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 12/22/2012 01:37 PM, Steve D wrote:
 I will continue playing with your suggestions etc to see
 what's cleanest.
Both of those options have unnecessary costs. If everything is known at compile time, you can use the curiously recurring template pattern: import std.stdio; class A(SubT) { final void f() { writefln("hi %s", SubT.hi); } } class B : A!B { static float[3] hi = 2; } void main() { B b = new B(); b.f(); } Ali
Dec 22 2012
parent reply "Steve D" <whatchcallamit weedline.com> writes:
Thanks Ali, I'm still playing with your earlier suggestion of 
passing the array to A in a super() call from B.
But now... I dunno things are getting worse... it 'sometimes' 
works see below..

While we're chatting, I've slightly added to the earlier example 
to better replicate my actual code.

Let's say it's a game and the objects are creatures. class A is a 
monster and array hi represents it's high'est speed(s) over 
various terrains.
Class B is a SwiftMonster who's hi's range a bit higher to allow 
it to move more quickly than a std monster.

Both 'hi' speeds apply to all monsters of that class so it's 
better to be a class-level (static) array (defined once vs 
hundreds of copies).

the run function  run() is common to any monster so I have 
defined it in Monster and 'should' be able to inherit it for any 
monster.
Both Monster and SwiftMonster may be instantiated many times.

I don't want to duplicate the common 'run' functionality run() so 
just define it in the lowest Monster class and inherit it for 
others (there could be many monster types).

Do these requirements sound reasonable? I think they do given the 
promise of Object Orientation - I'm not really asking for 
anything complicated or to jump through hoops.

I just want to inherit the run function without it coming 
pre-packaged with the array of the class where it was declared 
(assume in some circumstances I don't even need a basic Monster)

So, ok I have to compromise a bit and started using your pass 
'hi' at constructor time and with some revised code ended up with 
this


     class Creature {
         abstract void move();
     }

     class Monster :Creature {
         static float[3] hi = 1;

         this(float[] x=null) {if (x) hi = x;}

         final void run() {  writefln("hi running at %s",hi);  }

         void move() { run(); }

     }

     class SwiftMonster : Monster {
         static float[3] hi = 2;

         this() {super(hi);}

         void move() { run(); }
     }


running thusly

void main()
{
     Creature a = new Monster();
     a.move();

     Creature b = new SwiftMonster();
     b.move();
}

gives...
'hi running at [1, 1, 1]'     <-- ok this seems to work fine
'hi running at [2, 2, 2]'     <-- ok this seems to work fine


But then when I try to process them in a loop of creatures (you 
would, in a game, right?) I get this

void main()
{
     Creature[2] creatures;
     creatures[0] = new Monster();
     creatures[1] = new SwiftMonster();
     foreach(c; creatures) c.move();
}

gives...
hi running at [2, 2, 2]       <-- WTF?
hi running at [2, 2, 2]

Why Ali? Why?
Why aren't things simple?
I even tried my original question code in C++ and it's the same 
there.
Are my requirements too darned demanding of OOP? Surely not...?
Also why isn't behavior consistent.

Why Ali? Why?   ;)

Cheers
Steve
Dec 23 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 12/23/2012 02:08 PM, Steve D wrote:

 Let's say it's a game and the objects are creatures.
Thanks for giving more context. It helps to see meaningful names to understand code better.
 class Creature {
 abstract void move();
 }
That's very reasonable.
 class Monster :Creature {
 static float[3] hi = 1;

 this(float[] x=null) {if (x) hi = x;}
That is a bad idea. x is passed to a single Monster object but the 'hi' static member of the whole Monster class gets affected. Just because there is a SwiftMonster, all Monsters become swift. This shows that it would be better to mark 'hi' as immutable to avoid such mistakes.
 Why Ali? Why?
 Why aren't things simple?
:) I still think making Monster a template is a better way to go: import std.stdio; class Creature { abstract void move(); } class MonsterImpl(SubT) : Creature { final void run() { writefln("hi running at %s", SubT.hi); } override void move() { run(); } } class RegularMonster : MonsterImpl!RegularMonster { static immutable float[3] hi = 1; } class SwiftMonster : MonsterImpl!SwiftMonster { static immutable float[3] hi = 2; } void main() { Creature[2] creatures; creatures[0] = new RegularMonster(); creatures[1] = new SwiftMonster(); foreach(c; creatures) c.move(); } The output: hi running at [1, 1, 1] hi running at [2, 2, 2] Ali
Dec 23 2012
parent "Steve D" <whatchcallamit weedline.com> writes:
 class Monster :Creature {
 static float[3] hi = 1;

 this(float[] x=null) {if (x) hi = x;}
That is a bad idea. x is passed to a single Monster object but 
the 'hi' static member of the whole Monster class gets affected. 
Just because there is a SwiftMonster, all Monsters become swift.
Yep I know and that's what I was trying to avoid with my original (simplistic) version. I originally just wanted the inherited function to be compiled in the context of it's current environment (class B/SwiftMonster) rather than being linked to array of class where declared. Ah well, I think you have the solution which I haven't tried just yet, so thanks in advance (and thanks for your patient replies). While you where typing I tried it in python just for laughs.. class Creature: # not required, but just to be complete def move(): pass class Monster(Creature): hi = [1,1,1] def run(self): print "hi running at",self.hi def move(self): self.run() class SwiftMonster(Monster): hi = [2,2,2] def move(self): self.run() if __name__ == '__main__': a = Monster() a.move() b = SwiftMonster() b.move() print creatures = [Monster(),SwiftMonster(),SwiftMonster()] for c in creatures: c.move() print id(c.hi) hi running at [1, 1, 1] hi running at [2, 2, 2] hi running at [1, 1, 1] 28612448 hi running at [2, 2, 2] 28639280 hi running at [2, 2, 2] 28639280 (these id's are just to check that they're class-level arrays (not object) which they seem to be. See? Python eh? pseudo-code that just works! See my requirements really aren't too demanding or impossible after all! :) Thanks for now Ali, will get to trying your latest offering which I'm sure will work. Cheers Steve
Dec 23 2012