digitalmars.D.learn - Is there a keyword to access the base class
- Stephen Jones (31/31) Jun 18 2013 I am trying to do this:
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (19/46) Jun 18 2013 val() must appear on Bar. I made it an interface:
- Stephen Jones (2/64) Jun 18 2013 Thanks Ali
- Gary Willoughby (43/43) Jun 18 2013 I iterated on Ali's solution with more OOP to demonstrate
- Jacob Carlborg (4/21) Jun 19 2013 Why not just move "val" to the base class.
- Steven Schveighoffer (19/21) Jun 18 2013 Just an FYI, you are using the wrong terminology. In this case, Bar is ...
- Jacob Carlborg (9/11) Jun 19 2013 Pattern matching :)
- Stephen Jones (6/9) Jun 19 2013 That is what I would prefer, but I tried:
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (25/35) Jun 19 2013 typeid gives you a TypeInfo class:
- Stephen Jones (27/27) Jun 19 2013 It seems:
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (14/17) Jun 19 2013 The OO implementations that I am used to (C++ and D) do not do
- Steven Schveighoffer (41/51) Jun 20 2013 You have to understand, D is statically typed. There are two types
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
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
On Tuesday, 18 June 2013 at 22:15:51 UTC, Ali Çehreli wrote:On 06/18/2013 03:10 PM, Stephen Jones wrote:Thanks AliI 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
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
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
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
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
Hm... would be a nice idiom to implement generically in D. Like a type switch. -SteveThat 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
On 06/19/2013 04:10 PM, Stephen Jones wrote: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. AliHm... would be a nice idiom to implement generically in D. Like a type switch. -SteveThat 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
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
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
On Wed, 19 Jun 2013 19:10:39 -0400, Stephen Jones <siwenjo gmail.com> wrote: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). -SteveHm... would be a nice idiom to implement generically in D. Like a type switch. -SteveThat 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 20 2013