Using isa & co with custom class hierarchy

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Using isa & co with custom class hierarchy

Jan Bierbaum-2
Hi!

I hope I'm not too OT with that question, but Clang makes extensive use
of this mechanism so maybe somebody can help me.

LLVM provides isa<>, cast<> and so on. Recently I tried to make use of
this for my own class hierarchy, but failed.

>From the documentation and examples in Clang's code I figured that the
only thing to do was add a static method 'classof' to any involved class
with a parameter that will match any parent class. Then inside this
method do any checks necessary and return 'true' if the current instance
is indeed of the type given.

But when I do this I get 'true' for any 'isa<>' I try!?

Attached is a small example that outputs the following:

| $ ./a.out
| isa<A>(a) = 1
| isa<B>(a) = 1
| isa<B>(b) = 1
| isa<A>(b) = 1

What I would expect instead is the second line to yield false/0, since a
is an instance of class A which "is" no B.

Can somebody please give me a hint on what I'm doing wrong?


Regards, Jan.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev

isa.cpp (602 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Using isa & co with custom class hierarchy

Victor Zverovich-2
Hi Jan,

I think 'classof' should be virtual for isa<> and friends to work. In addition you can provide a static 'classof' for the same class that will speed up the cases like isa<A>(a) and isa<A>(b).

Regards,
Victor

On Wed, Aug 25, 2010 at 4:02 PM, Jan Bierbaum <[hidden email]> wrote:
Hi!

I hope I'm not too OT with that question, but Clang makes extensive use
of this mechanism so maybe somebody can help me.

LLVM provides isa<>, cast<> and so on. Recently I tried to make use of
this for my own class hierarchy, but failed.

>From the documentation and examples in Clang's code I figured that the
only thing to do was add a static method 'classof' to any involved class
with a parameter that will match any parent class. Then inside this
method do any checks necessary and return 'true' if the current instance
is indeed of the type given.

But when I do this I get 'true' for any 'isa<>' I try!?

Attached is a small example that outputs the following:

| $ ./a.out
| isa<A>(a) = 1
| isa<B>(a) = 1
| isa<B>(b) = 1
| isa<A>(b) = 1

What I would expect instead is the second line to yield false/0, since a
is an instance of class A which "is" no B.

Can somebody please give me a hint on what I'm doing wrong?


Regards, Jan.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev



_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Using isa & co with custom class hierarchy

Victor Zverovich-2
Sorry,  'classof' shouldn't be virtual itself of course.
The problem is that B::classof(const A *) always return true in your implementation, so llvm::isa<B>(a)returns true as well.
To make it work as you expect you should return true only if an argument to B::classof(const A *)is a pointer to class B or its subclass.
This can be done e.g. using a type field or a virtual function returning type identifier or dynamic_cast though if you use the latter you don't need llvm::isa. You can have a look at how classof is implemented in clang's AST.

Victor
On Wed, Aug 25, 2010 at 5:10 PM, Victor Zverovich <[hidden email]> wrote:
Hi Jan,

I think 'classof' should be virtual for isa<> and friends to work. In addition you can provide a static 'classof' for the same class that will speed up the cases like isa<A>(a) and isa<A>(b).

Regards,
Victor

On Wed, Aug 25, 2010 at 4:02 PM, Jan Bierbaum <[hidden email]> wrote:
Hi!

I hope I'm not too OT with that question, but Clang makes extensive use
of this mechanism so maybe somebody can help me.

LLVM provides isa<>, cast<> and so on. Recently I tried to make use of
this for my own class hierarchy, but failed.

>From the documentation and examples in Clang's code I figured that the
only thing to do was add a static method 'classof' to any involved class
with a parameter that will match any parent class. Then inside this
method do any checks necessary and return 'true' if the current instance
is indeed of the type given.

But when I do this I get 'true' for any 'isa<>' I try!?

Attached is a small example that outputs the following:

| $ ./a.out
| isa<A>(a) = 1
| isa<B>(a) = 1
| isa<B>(b) = 1
| isa<A>(b) = 1

What I would expect instead is the second line to yield false/0, since a
is an instance of class A which "is" no B.

Can somebody please give me a hint on what I'm doing wrong?


Regards, Jan.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev




_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Using isa & co with custom class hierarchy

Jan Bierbaum-2
Hi Victor!


Thanks for your answer.

Victor Zverovich meinte am 25.08.2010 18:33:
> The problem is that B::classof(const A *) always return true in your
> implementation, so llvm::isa<B>(a)returns true as well.

Yeah, but that one is intended. Every instance of B "is a" instance of A
due to inheritance. I just modeled this in the 'classof' method.

But even leaving this aside: There is no 'A::classof(const B *)' yet
'isa<B>(a)' returns 'true'. I added this one specifically to make sure I
did not mix up something with which class must define which as the
parameter to 'classof'.


> You can have a look at how classof is implemented in clang's AST.

I actually did. First the short introduction LLVM gives [1] states

| To add support for these templates, you simply need to add classof
| static methods to the class you are interested casting to.

That's how I came up with the scheme at all. Then for example
'CXXRecordDecl' [2] define the following (besides others):

| static bool classof(const CXXRecordDecl *D) { return true; }
| static bool classof(const ClassTemplateSpecializationDecl *D) {
|   return true;
| }

I take this to mean that every 'CXXRecordDecl' is a 'CXXRecordDecl' -
well obviously - and every 'ClassTemplateSpecializationDecl' is a
'CXXRecordDecl'.

Hmm, looks I did mix up the directions, but my example should
nevertheless also work only one way - every 'A' being a 'B' but not vice
versa. So 'isa<A>(b)' should report false/0.


[1] http://llvm.org/docs/ProgrammersManual.html#isa
[2] http://clang.llvm.org/doxygen/DeclCXX_8h_source.html#l01051



Regards, Jan.
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Using isa & co with custom class hierarchy

Sebastian Redl

On Aug 25, 2010, at 11:22 AM, Jan Bierbaum wrote:

> Hi Victor!
>
>
> Thanks for your answer.
>
> Victor Zverovich meinte am 25.08.2010 18:33:
>> The problem is that B::classof(const A *) always return true in your
>> implementation, so llvm::isa<B>(a)returns true as well.
>
> Yeah, but that one is intended. Every instance of B "is a" instance of A
> due to inheritance. I just modeled this in the 'classof' method.

Here's how isa is implemented (leaving aside all the unwrapping magic):

template <typename Test, typename Arg>
bool isa(Arg *Ptr) {
  return Test::classof(Ptr);
}

For a class Class, Class::classof is supposed to check if Class is the dynamic type (or a superclass thereof) of the argument.

So A::classof(A*) tests whether A is a valid cast target for the given object, which is trivially true.
B::classof(A*) tests whether B is a valid cast target for the given object. This is NOT trivially true, since it's only true for objects whose dynamic type is actually B, but the A* could point to an A. This is where the real work needs to be done. However, you return true here, which is why isa<B>(a) returns true. Remember, isa<B>(a) == B::classof(a) == true in your implementation.
B::classof(B*), finally, is simply an optimization. When the type of the object is statically known to be at least a B, there's no need to perform a runtime check.

> Then for example
> 'CXXRecordDecl' [2] define the following (besides others):

Those others are what's important. In particular, look at the overload that takes a Decl*.
In fact I think that the ClassTemplateSpecializationDecl overload here is completely superfluous. I have no idea why it's there.

> So 'isa<A>(b)' should report false/0.


A::classof(b) -> A::classof(A*) (since there's no B* overload) -> true.

You don't have any classof function that can return false, ever. Therefore, all isa tests will always return true. (A missing classof is a compile error, not a result of false.)

Sebastian
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev