Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2010-05

casting back a pointer to original type

12 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

spir ☣

Hello,

Say I store class instances of various types in a TFPList. All happen to be descendants of same superclass C and each one has a specific method "text". When retrieving and writing an element, if they were all direct instances of C, I would do something like:

    element := C(list[index]);   // casting back
    text := element.text;

I cannot do that, even if elements all are *indirect* instances of C, because this calls C.text instead of the element's own proper text method. Which is wrong, indeed. So, I must cast elements back to their own type; but I have no idea how to do this. Doesn't the compiler store this information? After all, when an element is put into the list, its type is known. Is there a reason why retrieving an element does not cast it back to its original type automatically? Should I store element types manually in a parallel list? There must be a way to solve this issue, I guess...

Side-question: it seems, when subclassing, that methods with same name and same parameters do not need any modifier keyword (such as "override" or "overload"). Eg I can have

    function text;

in both a superclass C0 and a subclass C1 work as expected. Correct?

Thank you,
Denis
________________________________

vit esse estrany ☣

spir.wikidot.com

Reply by email

Michael Van Canneyt
to spir ☣
spir ☣ wrote 8 lines
Hello, Say I store class instances of various types in a TFPList. All happen to be descendants of same superclass C and each one has a specific method "text". When retrieving and writing an element, if they were all direct instances of C, I would do something like: element := C(list[index]); // casting back text := element.text; I cannot do that, even if elements all are *indirect* instances of C, because this calls C.text instead of the element's own proper text method. Which is wrong, indeed. So, I must cast elements back to their own type; but I have no idea how to do this. Doesn't the compiler store this information? After all, when an element is put into the list, its type is known. Is there a reason why retrieving an element does not cast it back to its original type automatically? Should I store element types manually in a parallel list? There must be a way to solve this issue, I guess...

There are 2
1. Make all descendents descendent of a super class that has a text property.
2. Make the "text" property published, and use RTTI to get/set it.

uses typinfo;
text:=GetStrProp(element,'text');

Or

SetStrProp(Element,'text',AValue)

Side-question: it seems, when subclassing, that methods with same name and same parameters do not need any modifier keyword (such as "override" or "overload"). Eg I can have function text; in both a superclass C0 and a subclass C1 work as expected. Correct?

It depends on what you want to achieve. If you make it a static method, then the compiler will emit a warning "function Text hides method of parent class" (or something like it)

If you make it virtual, then you must explicitly add "override" to override the implementation of the method.

Michael.

Reply by email

Jonas Maebe
to Michael Van Canneyt
Michael Van Canneyt wrote 5 lines
It depends on what you want to achieve. If you make it a static method, then the compiler will emit a warning "function Text hides method of parent class" (or something like it) If you make it virtual, then you must explicitly add "override" to override the implementation of the method.

Virtual methods can also hide inherited methods (if you don't add override).

Jonas

Reply by email

Michael Van Canneyt
to Jonas Maebe
Jonas Maebe wrote 11 lines
On 29 May 2010, at 18:11, Michael Van Canneyt wrote: > It depends on what you want to achieve. If you make it a static method, then > the compiler will emit a warning "function Text hides method of parent > class" (or something like it) > > If you make it virtual, then you must explicitly add "override" to override the implementation of the method. Virtual methods can also hide inherited methods (if you don't add override).

Right. I should have added that.

Michael.

Reply by email

David Emerson
to spir ☣: Say I store class instances of various types in a TFPList. All happen to be descendants…
element := C(list[index]); // casting back text := element.text; I cannot do that, even if elements all are *indirect* instances of C, because this calls C.text instead of the element's own proper text method.

If you use virtual methods then C.text should call the descendant's method. This is what polymorphism, the most powerful concept in object oriented programming, is all about.

Show quoted text 5 lines
Side-question: it seems, when subclassing, that methods with same name and same parameters do not need any modifier keyword (such as "override" or "overload"). Eg I can have function text; in both a superclass C0 and a subclass C1 work as expected. Correct?

This is likely your problem. If you are not using virtual methods then you are not going to get polymorphism.

Here's a sample program to illustrate:

{$mode objfpc}
type
  C0 = class
    procedure statictext;
    procedure virtualtext; virtual;
  end;
  C1 = class (C0)
    procedure statictext;
    procedure virtualtext; override;
  end;
procedure C0.virtualtext;
  begin
    writeln ('C0 virtual');
  end;
procedure C0.statictext;
  begin
    writeln ('C0 static');
  end;
procedure C1.virtualtext;
  begin
    writeln ('C1 virtual');
  end;
procedure C1.statictext;
  begin
    writeln ('C1 static');
  end;
var
  a : C0;
  b : C1;
  p : pointer;
  t : tobject;
begin
  b := C1.create;
  p := b;
  a := C0(p);
  a.statictext;   // output: C0 static
  a.virtualtext;  // output: C1 virtual
  // let's add another degree of removal
  t := tobject(p);
  C0(t).statictext;
  C1(t).statictext;
  C0(t).virtualtext;
  C1(t).virtualtext;
end.

Cheers,
David

Reply by email

spir ☣
to David Emerson
Show quoted text 17 lines
> element := C(list[index]); // casting back > text := element.text; > I cannot do that, even if elements all are *indirect* instances of C, because > this calls C.text instead of the element's own proper text method. If you use virtual methods then C.text should call the descendant's method. This is what polymorphism, the most powerful concept in object oriented programming, is all about. > Side-question: it seems, when subclassing, that methods with same name and > same parameters do not need any modifier keyword (such as "override" > or "overload"). Eg I can have > function text; > in both a superclass C0 and a subclass C1 work as expected. Correct? This is likely your problem. If you are not using virtual methods then you are not going to get polymorphism.
I cannot do that. C0 (and all classes) instances need a text method. I also cannot have 2 methods (one static, one virtual) with different names. It's a basic feature, always called with the same name. Like getItem for a hierarchy of collections: every collection must have it, and always under the same name, so that any client can rely on it.
I'll have to find a workaround. As of now, the only solution I can imagine (but it's really really stupid) is to store somewhere, in a // list, the actual types of all items recorded in a TFPList.
But I find this incredible... I must be overlooking something basic; how do you use TFPList, if when reading back stored items their types are lost in ether? TFPList is precisely intended to record class instances, meanings all kinds of stuff descending from TObject, or am I wrong on this? Is there another kind of (pointed) sequential collection able to store class instances without losing its items' types?
Another solution may be to store the type as an additional attribute of items themselves, before they are put into a collection. But since Pascal is static, this cannot be done "live" at runtime; meaning *all* items in the system must have this attribute. But I could then probably do something like:
   // the looked up item is list[index]
   // first read (a reference to) its type on the item itself
   typ := list[index].type;
   // now I can read and cast it
   item := typ(list[index]);

Denis
________________________________

vit esse estrany ☣

spir.wikidot.com

Reply by email

David Emerson
to spir ☣
spir ☣ wrote
I cannot do that. C0 (and all classes) instances need a text method. I also cannot have 2 methods (one static, one virtual) with different names.

So make a virtual method called text. Don't use a static my example I was trying to demonstrate the difference between static methods and virtual methods. I was not trying to suggest that you change your method names, or use both static and virtual methods.

I can't be sure without seeing your code, but based on reading your messages three times, it appears to me that you are using static methods (i.e. without the virtual and override keywords) and expecting them to behave like virtual methods.

Cheers & good luck,
David

Reply by email

Andrew Hall
to spir ☣: On Sat, 29 May 2010 10:46:45 -0700 I cannot do that. C0 (and all classes) instances need…
spir ☣ wrote
I cannot do that. C0 (and all classes) instances need a text method. I also cannot have 2 methods (one static, one virtual) with different names. It's a basic feature, always called with the same name. Like getItem for a hierarchy of collections: every collection must have it, and always under the same name, so that any client can rely on it.

David is not suggesting you have two methods with different names - his example is demonstrating the different behaviour of static and virtual methods. Calling a static method is determined at compile time - the compiler must use the actual class of your variable (or cast) - so your descendent methods will never be called. This is what is happening to you at the moment - only your base class Text method is called. Calling a virtual method is determined at runtime based upon the actual class held by the variable (cast) - it seems this is exactly what you need. Set your base class Text method as virtual, and each descendent class Text method as override - your code should then function as you require...

   element := C(list[index]);   // casting back
   text := element.text;

if the pointer in list[index] is C1, C1.Text will be called.

Regards,
Andrew.

Reply by email

spir ☣
to Andrew Hall
Show quoted text 12 lines
On 29 May 10, at 15:26 , spir ☣ wrote: > I cannot do that. C0 (and all classes) instances need a text method. I also cannot have 2 methods (one static, one virtual) with different names. It's a basic feature, always called with the same name. Like getItem for a hierarchy of collections: every collection must have it, and always under the same name, so that any client can rely on it. David is not suggesting you have two methods with different names - his example is demonstrating the different behaviour of static and virtual methods. Calling a static method is determined at compile time - the compiler must use the actual class of your variable (or cast) - so your descendent methods will never be called. This is what is happening to you at the moment - only your base class Text method is called. Calling a virtual method is determined at runtime based upon the actual class held by the variable (cast) - it seems this is exactly what you need. Set your base class Text method as virtual, and each descendent class Text method as override - your code should then function as you require... element := C(list[index]); text := element.text; if the pointer in list[index] is C1, C1.Text will be called. Regards, Andrew.

Thank you very much, David & Andre. Sorry, David, I had not understood the intent of your example post. Your comment and Andre's clarified everything. OK, I lurred myself by taking words too literally. I thought "virtual" meant unimplemented, to be implemented in sub-classes. So, for me, this could not apply to my case, since supaer-classes also need the method to be implemented. Just did a fast trial: works fine.

Denis
________________________________

vit esse estrany ☣

spir.wikidot.com

Reply by email

spir ☣
to Andrew Hall: David is not suggesting you have two methods with different names - his example is…

This is a follow-up to the previous thread.

Say I put in a TFPList elements of types C C1 C2, where C is a super-class. When I retrieve an element using var element : C;
...

    element := C(list[index]);
    text := element.text;
    ...

what is the actual type of element? and of its value? Concretely, if the originally inserted element was of type C1, C1.text is called -- this is the point discussed & solved in the previous thread.

So, it seems the language knows (at runtime) about the real type of a *value*. I must declare element, the variable, with type C, since I do not know anything at coding time. But Pascal correctly calls C1.text, due to "virtual" & "override" modifiers. So, it means that the language has additional type information and uses it to make a distinction between variable and value types. Correct? Else, how can it correctly call C1.text? But I'm not 100% sure this means that, for Pascal and at runtime, element's value is of type C1. Is there a way to know value and/or variable's types, such as a func TypeName()?

(Side-note: I do not understand why this overriding mechanism needs to be explicitely stated with a modifier keyword: when a programmer writes a specialised method on a sub-class, obviously s/he intends it to be called, no?)

As you have certainly noted ;-), I have very limited knowledge about static languages. But I thought there were only variable types (at least in Pascal and C). While in dynamic languages there are only value types (and in fact there is no notion of variable like in Pascal, the name:value relation is different). But the case above contradicts this simplistic POV of mine.

Can I rely on this mechanism to correctly dispatch method calls in any case, by building a system such as:
* All values are class instances.
* They have a unique ancestor class C, on which all possibly overridable methods are declared virtual.
* On sub-classes, all methods existing on the ancestor(s) are explicitely declared override. Then, whatever the class of a variable or of its value for Pascal, the correct method will be called? If yes, as I think now, this is a very constructive point :-) Another one would be to acually know a value's type. How to get its name or, much better, a pointer to it? (Since the language obviously has such a pointer.)

Denis
________________________________

vit esse estrany ☣

spir.wikidot.com

Reply by email

Andrew Hall
to spir ☣
spir ☣ wrote
So, it seems the language knows (at runtime) about the real type of a *value*. I must declare element, the variable, with type C, since I do not know anything at coding time. But Pascal correctly calls C1.text, due to "virtual" & "override" modifiers. So, it means that the language has additional type information and uses it to make a distinction between variable and value types. Correct? Else, how can it correctly call C1.text?

Correct. Each object instance has a pointer to a structure which defines its class - inheritance, TypeInfo, VMT (virtual method table), etc. This allows the application access to everything about the instance reading the Class Types section of the Programmers Manual and anything related to Classes, VMT or TypeInfo in the Programmers, Run-Time Library and Language Reference Manuals.

But I'm not 100% sure this means that, for Pascal and at runtime, element's value is of type C1. Is there a way to know value and/or variable's types, such as a func TypeName()?

All classes are derived from TObject which has many useful methods - including Classname, ClassnameIs(), ClassType and InheritsFrom(). It would be worth reading the TObject section of the Run-Time Library Manual.

(Side-note: I do not understand why this overriding mechanism needs to be explicitely stated with a modifier keyword: when a programmer writes a specialised method on a sub-class, obviously s/he intends it to be called, no?)

Not necessarily. Choosing between Static/Virtual allows the class designer to determine what aspects of the ancestor class behaviour can be modified in a descendent - and therefore protect the ancestor function from being accidentally or purposefully broken. Usually only a few methods of a class need to be virtual.

In Delphi and FPC all methods are static by default (this is distinctly different from many other languages where methods are always virtual). Static methods execute faster as the VMT does not need to be accessed, and they can be called against a nil object. Static methods also allow parameters to vary between classes (this constraint can be very annoying in languages where all methods are virtual and overloading is not supported). Understanding the difference between static and virtual method behaviour is key to OO programming - especially if you are using polymorphism as you are in your example program - suggest reading more on this topic in the Programmers and Language Reference Manuals.

Show quoted text 6 lines
Can I rely on this mechanism to correctly dispatch method calls in any case, by building a system such as: * All values are class instances. * They have a unique ancestor class C, on which all possibly overridable methods are declared virtual. * On sub-classes, all methods existing on the ancestor(s) are explicitely declared override. Then, whatever the class of a variable or of its value for Pascal, the correct method will be called? If yes, as I think now, this is a very constructive point :-)

Yes.

Another one would be to acually know a value's type. How to get its name or, much better, a pointer to it? (Since the language obviously has such a pointer.)

aObj.ClassType will return the TClass of the object. If you refer to the Manuals as suggested above you will find how to access the TypeInfo for a class. Note - you can also variably instantiate an object at runtime by working with the TClass... extending your code example:

type
  TMyCClass = class of C;
function CreateTheCorrectObjectAtRunTime(aC: TMyCClass): C;
begin
  result := aC.Create;
end;

The result maybe C or any descendent of C (C1, C2, C21, etc) depending on what classtype is passed to the function. This feature may require that your constructor is virtual so that the compiler can determine the correct constructor to call at runtime (this is why TComponent's constructor is virtual as this behaviour is fundamental to TFiler/TReader and streaming components. If aC had been the base type TClass this code would work as well - however creating your own class types allows for more robust code and mean you can implement without casting variables - especially useful if your constructor is not virtual, and/or constructor parameters vary between your classes. If you are interested in this area, Chapter 6 Classes in the Language Reference Manual is worth a read...

Regards

Andrew.

Reply by email