Jump to page: 1 2
Thread overview
Is there a keyword to access the base class
Jun 18, 2013
Stephen Jones
Jun 18, 2013
Ali Çehreli
Jun 18, 2013
Stephen Jones
Jun 18, 2013
Gary Willoughby
Jun 19, 2013
Jacob Carlborg
Jun 19, 2013
Jacob Carlborg
Jun 19, 2013
Stephen Jones
Jun 19, 2013
Ali Çehreli
Jun 20, 2013
Stephen Jones
Jun 20, 2013
Ali Çehreli
June 18, 2013
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?

June 18, 2013
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

June 18, 2013
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
June 18, 2013
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);
	}
}
June 18, 2013
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
June 19, 2013
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
June 19, 2013
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
June 19, 2013
> 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?
June 19, 2013
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

  http://dlang.org/phobos/object.html#.TypeInfo

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

June 20, 2013
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?
« First   ‹ Prev
1 2