* * *

Author Topic: Why can't I use "is" on a class reference type?  (Read 420 times)

ahydra

  • Newbie
  • Posts: 4
Why can't I use "is" on a class reference type?
« on: March 11, 2017, 04:40:40 pm »
Code: Pascal  [Select]
  1.  
  2. type
  3.   TA = class
  4.     ...
  5.   end;
  6.  
  7.   TB = class(TA)
  8.     ...
  9.   end;
  10.  
  11.   TAClass = class of TA;
  12.  
  13. function IsB(aType: TAClass): boolean;
  14.   begin
  15.     Result := (aType is TB); // won't compile: Class or COM interface type expected, but got "TAClass"
  16. end;
  17.  
  18.  

(Lazarus 1.6.4, FPC 3.0.2)

Why is this not possible? Since all the "is" operator does is compare the VMT pointer given by the RHS to the VMT pointer of the class instance on the LHS (and recurse through its parents until there's a match), it should also be possible to do the same thing with VMT pointers in the form of class reference types.

Thanks,

ahydra

Thaddy

  • Hero Member
  • *****
  • Posts: 3400
Re: Why can't I use "is" on a class reference type?
« Reply #1 on: March 11, 2017, 04:49:36 pm »
A class reference is not a class.It is as simple as that.

Try:
Code: Pascal  [Select]
  1. function IsB(aType: TAClass): boolean;
  2.   begin
  3.     Result := (aType = TB.ClassType); // just compare the class references
  4. end;

A bit nicer is this (untested):
Code: Pascal  [Select]
  1. operator = (const a:TClass;const b:Tobject) c:boolean;inline;
  2. begin
  3.   c := (a=b.classtype);
  4. end;
  5.  
  6. operator i = (const a:Tobject;const b:TClass) c:Boolean;inline;
  7. begin
  8.   c := (a.classtype = b);
  9. end;
  10.  

 
« Last Edit: March 11, 2017, 05:18:44 pm by Thaddy »

SkyKhan

  • Full Member
  • ***
  • Posts: 141
Re: Why can't I use "is" on a class reference type?
« Reply #2 on: March 11, 2017, 05:57:04 pm »
I think you're looking for "InheritsFrom",
Code: Pascal  [Select]
  1.   Result:=aType.InheritsFrom(TB);
  2.  
I will always ignore you! Yes, you too ( except if you are topic owner )

howardpc

  • Hero Member
  • *****
  • Posts: 2111
Re: Why can't I use "is" on a class reference type?
« Reply #3 on: March 11, 2017, 05:59:15 pm »
Yes. In the following program, the function IsAClass(aClassRef: TBClass) is pretty useless, since no TBClass (TB being declared as other than TA) can ever be a TAClass, i.e. the outcome of the function is always known in advance, so it hardly adds any useful information.
 
Potentially more useful is whether TA and TB are related, viz. the DescendsFromA function. Note that (unlike your function IsB) the parameter is a general TClass, which makes the applicabiity of the the wrapped  InheritsFrom() function much wider than just to TAClass classes.

Code: Pascal  [Select]
  1. program project1;
  2.  
  3. {$mode objfpc}
  4.  
  5. type
  6.  
  7.  TA = class
  8.  end;
  9.  
  10.  TB = class(TA)
  11.  end;
  12.  TBClass = class of TB;
  13.  
  14. function IsAClass(aClassRef: TBClass): boolean;
  15. begin
  16.   Result:=(aClassRef = TA.ClassType);
  17. end;
  18.  
  19. function DescendsFromA(aClassRef: TClass): Boolean;
  20. begin
  21.   Exit(aClassRef.InheritsFrom(TA));
  22. end;
  23.  
  24. begin
  25.   WriteLn('IsAClass(TB) is ',IsAClass(TB));
  26.   WriteLn('DescendsFromA(TB) is ',DescendsFromA(TB));
  27.   ReadLn;
  28. end.

ahydra

  • Newbie
  • Posts: 4
Re: Why can't I use "is" on a class reference type?
« Reply #4 on: March 11, 2017, 06:03:39 pm »
Fair enough - TB is a type rather an a VMT pointer, so you need to call ClassType on it to get the VMT pointer. However Thaddy's solution doesn't take care of inheritance (a class reference variable set to a subclass of TB should also return true for "is TB").

Here's what I came up with:

Code: Pascal  [Select]
  1. program Project1;
  2.  
  3. uses sysutils;
  4.  
  5. type
  6.   TA = class
  7.  
  8.   end;
  9.  
  10.   TB = class(TA)
  11.  
  12.   end;
  13.  
  14.   TC = class(TB)
  15.  
  16.   end;
  17.  
  18.   TD = class(TA)
  19.  
  20.   end;
  21.  
  22.   TAClass = class of TA;
  23.  
  24. function ClassReferenceIs(aObjectClass, aTargetClass: TAClass): boolean;
  25.   type
  26.     PVMT = ^TVMT;
  27.     TVMT = packed record
  28.       Useless: ptruint;
  29.       Useless2: ptruint;
  30.       AncestorVMT: pointer;
  31.     end;
  32.  
  33.   var
  34.     p: pointer;
  35.  
  36.   begin
  37.     p := pointer(aObjectClass);
  38.  
  39.     repeat
  40.       if p = pointer(aTargetClass) then
  41.       begin
  42.         Result := true;
  43.         exit;
  44.       end;
  45.  
  46.       p := PVMT(p)^.AncestorVMT;
  47.     until p = nil;
  48.  
  49.     Result := false;
  50. end;
  51.  
  52. var
  53.   AC: TAClass;
  54.  
  55. begin
  56.   AC := TA;
  57.   writeln(booltostr(ClassReferenceIs(AC, TB), 'yep', 'nope')); // nope
  58.   AC := TB;
  59.   writeln(booltostr(ClassReferenceIs(AC, TB), 'yep', 'nope')); // yep
  60.   AC := TC;
  61.   writeln(booltostr(ClassReferenceIs(AC, TB), 'yep', 'nope')); // yep
  62.   AC := TD;
  63.   writeln(booltostr(ClassReferenceIs(AC, TB), 'yep', 'nope')); // nope
  64.  
  65.   readln();
  66.  
  67. end.
  68.  

I think to be honest though, FPC should support "is" on class references. It's a perfectly sensible thing to want to do, and can use exactly the same algorithm as for class instances.

In fact, it should probably support things like "TB is TA" as well? That may be rather useless, but it still makes sense - when compiling, the type "TB" used as a value is just the VMT pointer of TB.

I see now two people have suggested InheritsFrom, that works wonderfully - thanks.

ahydra

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus