tags:

views:

960

answers:

2

Is it possible in Delphi to use RTTI (or something else) to check if a class is declared as abstract? Something like:

TMyAbstractClass = class abstract(TObject)
  // ...
end;

...

if IsAbstract(TMyAbstractClass.ClassInfo) then
  ShowMessage('Yeah')
else
  ShowMessage('Computer says no...');
A: 

A quick look through the TypInfo unit doesn't turn up anything helpful. I think the notion of an "abstract class" is purely for the compiler's benefit. It gives it a rule to enforce--no instantiation of this class, only its descendants--but doesn't really do anything at runtime, so there's no need to record any RTTI for it.

Why are you trying to find this out anyway, just out of curiosity?

Mason Wheeler
I have a TCollection derived class that can contain different classtypes (TCollectionItem) with a common ancestor. To be specific it's a grid column collection that contains different types of columns. And now I want to create a designtime collection editor that allows the user to add registered column types (using TClassFinder).. but I don't want the abtract classes in this editor. My abstract classes is named TCustom* so for now I'm filtering using the class name.
mrMoo
Your problem, therefore, is not about how to detect abstract classes. Your problem is how to get a list of valid "column types." The easy solution is to make sure you only register the classes that are valid column types. Don't register the abstract classes in the first place. The only reason to register a class is so you can search for it by name and then instantiate it. Do not register classes that are never meant to be instantiated. You should find that the VCL doesn't register its own "custom" base classes, either.
Rob Kennedy
Yes very true, the problem is that I do only register the classes that are "valid", as you suggests, but Delphi automatically also registers the parent classes, e.g. if you register TButton using RegisterClass(TButton), Delphi will also register TCustomButton, TButtonControl and so on... Thanks anyway for your post about abstract methods, very interesting, but I guess the correct answer to my question is "No"
mrMoo
+6  A: 

I don't have a version recent enough to answer your question directly, but keep in mind that it doesn't really matter whether the class is abstract. All that does is make the compiler stop you from calling a constructor directly on the class. If you put the class reference into a class-reference variable, the compiler will allow you to call the constructor on the variable, and at run time you'll have an instance of the supposedly uninstantiable class.

var
  c: TClass;
  o: TObject;
begin
  c := TMyAbstractClass;
  o := c.Create;
  Assert(o is TMyAbstractClass);
end;

What's really important is whether the class has any abstract methods. You can check for that fairly easily. Look in the class's VMT. Any virtual-method slot that contains a pointer to System._AbstractError is an abstract method. The tricky part is knowing how many virtual-method slots to check, since that isn't recorded. Allen Bauer demonstrated how to do that in an answer to another question, but in the comments Mason Wheeler points out that it may return larger values than it should. He mentions the GetVirtualMethodCount function from the JCL, which should give a more accurate count of user-defined virtual methods. Using that function and GetVirtualMethod, also from the JCL, we get this function:

function HasAbstractMethods(c: TClass): Boolean;
var
  i: Integer;
begin
  Result := True;
  for i := 0 to Pred(GetVirtualMethodCount(c)) do
    if GetVirtualMethod(c, i) = @_AbstractError then
      exit;
  Result := False;
end;

If an abstract class has no abstract methods, then how abstract can it really be? It must have been marked abstract to prevent developers from creating instances of it, but if you really want to, you can create instances of it anyway, so marking an abstract class is really more of a warning than any actual restriction on usage.

Rob Kennedy
Unfortunately, when I ran some tests, Allen's algorithm turned out to not work in all cases. I'll look into it a bit and post a correction in that question if I can get it figured out.
Mason Wheeler
Apparently some other stuff can get put there in front of the name. Instead of Allen's algorithm, try JclSysUtils.GetVirtualMethodCount, which is conveniently located in the same JCL unit as GetVirtualMethod.
Mason Wheeler
Indeed. The JCL version checks all the other pointers in the VMT, and if any of them occur between the first virtual method and the class name, it "shortens" the window of what it considers to be the range of virtual methods.
Rob Kennedy