www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there a keyword to access the base class

reply "Stephen Jones" <siwenjo gmail.com> writes:
I am trying to do this:

import std.stdio;
import std.conv;

class Bar{
}

class Foo : Bar{
	int val = 10;
}
class Foos : Bar{
	int val = 20;
	string str = "some more memory";
}

void main(){
	Bar[] bars;
	
	bars ~= new Foo();
	bars ~= new Foos();
	
	foreach(Bar b; bars){
		//writeln(b.val);//error: no property 'val' for type 'mod.Bar'
	}
	
	writeln(to!(Foo)(bars[0]).val);//works
}

The problem is that I have to cast each Bar instance to its base 
class (Foo, Foos) before the compiler recognizes the val 
variable. Is there some syntax or keyword to allow me to specify 
that the b in the foreach loop refers to the base class not the 
super, such as writeln(b.base.val);

I know I can cast, but how do I know what base class each b in 
the foreach loop is?
Jun 18 2013
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/18/2013 03:10 PM, Stephen Jones wrote:
 I am trying to do this:

 import std.stdio;
 import std.conv;

 class Bar{
 }

 class Foo : Bar{
      int val = 10;
 }
 class Foos : Bar{
      int val = 20;
      string str = "some more memory";
 }

 void main(){
      Bar[] bars;

      bars ~= new Foo();
      bars ~= new Foos();

      foreach(Bar b; bars){
          //writeln(b.val);//error: no property 'val' for type 'mod.Bar'
      }

      writeln(to!(Foo)(bars[0]).val);//works
 }

 The problem is that I have to cast each Bar instance to its base class
 (Foo, Foos) before the compiler recognizes the val variable. Is there
 some syntax or keyword to allow me to specify that the b in the foreach
 loop refers to the base class not the super, such as writeln(b.base.val);

 I know I can cast, but how do I know what base class each b in the
 foreach loop is?
val() must appear on Bar. I made it an interface: interface Bar{ int val(); } class Foo : Bar{ int val_ = 10; int val() { return val_; } } class Foos : Bar{ int val_ = 20; string str = "some more memory"; int val() { return val_; } } Ali
Jun 18 2013
next sibling parent reply "Stephen Jones" <siwenjo gmail.com> writes:
On Tuesday, 18 June 2013 at 22:15:51 UTC, Ali Çehreli wrote:
 On 06/18/2013 03:10 PM, Stephen Jones wrote:
 I am trying to do this:

 import std.stdio;
 import std.conv;

 class Bar{
 }

 class Foo : Bar{
     int val = 10;
 }
 class Foos : Bar{
     int val = 20;
     string str = "some more memory";
 }

 void main(){
     Bar[] bars;

     bars ~= new Foo();
     bars ~= new Foos();

     foreach(Bar b; bars){
         //writeln(b.val);//error: no property 'val' for type 
 'mod.Bar'
     }

     writeln(to!(Foo)(bars[0]).val);//works
 }

 The problem is that I have to cast each Bar instance to its 
 base class
 (Foo, Foos) before the compiler recognizes the val variable. 
 Is there
 some syntax or keyword to allow me to specify that the b in 
 the foreach
 loop refers to the base class not the super, such as 
 writeln(b.base.val);

 I know I can cast, but how do I know what base class each b in 
 the
 foreach loop is?
val() must appear on Bar. I made it an interface: interface Bar{ int val(); } class Foo : Bar{ int val_ = 10; int val() { return val_; } } class Foos : Bar{ int val_ = 20; string str = "some more memory"; int val() { return val_; } } Ali
Thanks Ali
Jun 18 2013
parent "Gary Willoughby" <dev kalekold.net> writes:
I iterated on Ali's solution with more OOP to demonstrate 
features you may find interesting.

import std.stdio;

interface IBar
{
	 property int val();
}

class Bar : IBar
{
	protected int _val;

	 property int val()
	{
		return this._val;
	}
}

class Foo : Bar
{
	this()
	{
		this._val = 10;
	}
}
class Foos : Bar
{
	string str = "some more memory";

	this()
	{
		this._val = 20;
	}
}

void main()
{
	IBar[] bars; // <--- collection of objects conforming to your 
interface.
	
	bars ~= new Foo();
	bars ~= new Foos();
	
	foreach(IBar b; bars)
	{
		writeln(b.val);
	}
}
Jun 18 2013
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-06-19 00:15, Ali Çehreli wrote:

 val() must appear on Bar. I made it an interface:

 interface Bar{
 int val();
 }

 class Foo : Bar{
 int val_ = 10;

 int val() {
 return val_;
 }
 }
 class Foos : Bar{
 int val_ = 20;
 string str = "some more memory";

 int val() {
 return val_;
 }
 }
Why not just move "val" to the base class. -- /Jacob Carlborg
Jun 19 2013
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 18 Jun 2013 18:10:49 -0400, Stephen Jones <siwenjo gmail.com>  
wrote:

 I know I can cast, but how do I know what base class each b in the  
 foreach loop is?
Just an FYI, you are using the wrong terminology. In this case, Bar is the base class, and Foo and Foos are the *derived* classes. Other than that, I think Ali gave you the best solution. If you wanted to do something that *wasn't* common between two derived classes (i.e. some function/member that was only on one specific derived class), you can use a nice technique called auto-casting: if(auto der = cast(Foo)b) { // use Foo specific functionality on der } else if (auto der = cast(Foos)b) { // use Foos specific functionality on der } Hm... would be a nice idiom to implement generically in D. Like a type switch. -Steve
Jun 18 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-06-19 00:54, Steven Schveighoffer wrote:

 Hm... would be a nice idiom to implement generically in D. Like a type
 switch.
Pattern matching :) You could quite easily implement something like this in library code: match(b, (Foo f) => ,// use f (Foos fs) => // use fs ); -- /Jacob Carlborg
Jun 19 2013
prev sibling parent reply "Stephen Jones" <siwenjo gmail.com> writes:
 Hm... would be a nice idiom to implement generically in D.  
 Like a type switch.

 -Steve
That is what I would prefer, but I tried: writeln(to!(typeof(bars[1]))(bars[1]).val); to see if I could access the "DERIVED" (thanks) class type but even though bars[1] is initialized as a new Foos its type is still marked as Bar. So the question is, how do you find the derived class type when presented with only the super class?
Jun 19 2013
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/19/2013 04:10 PM, Stephen Jones wrote:
 Hm... would be a nice idiom to implement generically in D. Like a type
 switch.

 -Steve
That is what I would prefer, but I tried: writeln(to!(typeof(bars[1]))(bars[1]).val); to see if I could access the "DERIVED" (thanks) class type but even though bars[1] is initialized as a new Foos its type is still marked as Bar. So the question is, how do you find the derived class type when presented with only the super class?
typeid gives you a TypeInfo class: http://dlang.org/expression.html#TypeidExpression import std.stdio; class Base {} class Derived1 : Base {} class Derived2 : Base {} void foo(Base b) { writeln(typeid(b)); } void main() { foo(new Derived1()); foo(new Derived2()); } Prints: deneme.Derived1 deneme.Derived2 There is also TypeInfo_Class there which may be useful. Ali
Jun 19 2013
parent reply "Stephen Jones" <siwenjo gmail.com> writes:
It seems:

string me = (typeid(bars[1])).toString;
if(endsWith(me, "Foos")) writeln(to!(Foos)(bars[1]).val);

I see there is another post where somebody has asked if they can 
use cast(typeof(typeid(bars[1]))).val, and it was explained that 
the compiler won't know typeid until after compilation but it 
needs typeof at compilation, so that doesn't work. So it seems a 
string comparison is required (hopefully I am wrong on this). 
Thus it seems a choice of three or four reasonably off choices 
all because dmd is missing a keyword (maybe "derived") that would 
act like the keyword super but in the other direction; if the 
compiler can sus out the super from the derived at compile time 
then it necessarily knows what the derived is.

The options are accept property syntax and add an interface, 
which means introducing extraneous copies of functions and oddly 
duplicate variable names that invite all manner of bugs.

Define variables which are to be used from the derived class in 
the base class and spend hours scratching your head about where 
the variable you are looking at in the derived class has been 
declared, then remembering and spending ages flipping from 
derived to base class to see what is going on.

Perform string operations to discover what the actual derived 
type is and then cast the base to its initialized type.

Wouldn't it be easier to simply write bars[1].derived.val and let 
the compiler look to and accept val if it is declared in the 
derived class, else gracefully accept val if it is only declared 
in the base class?
Jun 19 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
The OO implementations that I am used to (C++ and D) do not do 
virtualization at the data level. Only member functions are virtual.

One of the reasons is that virtual functions remove the need to know 
what the exact derived type is. The compiler jumps off the virtual 
function pointer table and the actual virtual member function for the 
object gets called.

There are rare cases where the actual derived type may be useful. In 
such cases a series of casts are applied as Steven has shown.

On 06/19/2013 08:35 PM, Stephen Jones wrote:

 Wouldn't it be easier to simply write bars[1].derived.val and let the
 compiler look to and accept val if it is declared in the derived class,
 else gracefully accept val if it is only declared in the base class?
I don't think that implementation has any chance. A statically typed language like D will not do that kind of search at runtime. The code gets compiled by static types of variables. That's all. If it's a Base, it's a Base. Ali
Jun 19 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 19 Jun 2013 19:10:39 -0400, Stephen Jones <siwenjo gmail.com>  
wrote:

 Hm... would be a nice idiom to implement generically in D.  Like a type  
 switch.

 -Steve
That is what I would prefer, but I tried: writeln(to!(typeof(bars[1]))(bars[1]).val); to see if I could access the "DERIVED" (thanks) class type but even though bars[1] is initialized as a new Foos its type is still marked as Bar. So the question is, how do you find the derived class type when presented with only the super class?
You have to understand, D is statically typed. There are two types involved here, the dynamic, object-oriented type, which is only known at runtime, and the static type which is known by the compiler. typeof(bars[1]) gets the *static* type, or the type that the compiler knows. bars is a Bar[], so the compiler only knows that bars[1] is a Bar. The dynamic type of bars[1], as we know, is Foos. And bars[1] knows it too, but in order to call a function on a derived type, the *compiler* has to know it. So when you say something like: if(auto der = cast(Foos)bars[1]) You are telling the compiler: "OK, I know this is a Bar right now, but check during runtime to see if this is actually a Foos. If it is, branch in here with 'der' as a variable which is statically typed as Foos." Then the compiler can know inside that branch, that it can access bars[1] via the static type Foos, and you have access to all Foos' members. Note that if the instance is actually a derivative of Foos, the compiler STILL will go into that branch! The "correct" way to do this is to define what you are looking for as a virtual or abstract method on the base class/interface, and then the compiler does all this work for you (but it's only a vtable lookup, so it's much faster). But if you DON'T know that all derivatives will support 'val' (and you have to know this when the base class is written), then you have to do this casting dance. Dynamically typed languages make this easier, but it comes at a price. Statically typed languages have the compiler check so much more at compile time, so it's much more difficult to make a mistake. I deal with this all the time on PHP, where you can simply type a variable name wrong, and the compiler (and sometimes during runtime) won't complain, it just thinks you are declaring a new variable on an object instance. Note that Ali's suggestion of typeid does NOT give you a static type of the most derived type, it's a runtime type information object that has limited capability. It's like reflection in Java or .Net. But much less powerful, since D's TypeInfo does not provide a full mapping of members that is able to call methods and whatnot. So while typeid can tell you what the type is (good for comparing, etc), you can't call methods on it (well, there are a couple, like comparing two objects, but not arbitrary ones). -Steve
Jun 20 2013