Comparing if two objects are of same type without RTTI

V

vj

Hi All,

Just realized that one can directly compare the vptr of two objects to
check if they belong to the same class or not. Below is the example
code:

<code>
#define VPTR(__X__) (*((int*)&(__X__)))

class A{
public:
virtual ~A(){}
};
class B{
public:
virtual ~B(){}
};

int main(){
A a1,a2;
B b1;
ASSERT( VPTR(a1)==VPTR(a2) );
ASSERT( VPTR(a1)!=VPTR(b) );
}
</code>

This makes it possible to check if two objects are of same type or not
even if RTTI support is not available (like we have on some embedded
platforms). I do understand that this method is only applicable if the
said classes have a vtable. But I am not sure if there are any other
gotchas' that I am overlooking. Whats your take on this.

Thanks,
~Vaibhav
 
S

SG

Just realized that one can directly compare the vptr of two objects

The what? The C++ ISO standard does not say anything about a "vptr".
(And no, I don't want you to explain to me how virtual functions are
typically implemented.)
[ ... horrible non-portable hack ... ]

This makes it possible to check if two objects are of same type or not
even if RTTI support is not available (like we have on some embedded
platforms). I do understand that this method is only applicable if the
said classes have a vtable. But I am not sure if there are any other
gotchas' that I am overlooking. Whats your take on this.

I think I answered that already by summarizing your code with
"horrible non-portable hack". I don't know what I would do in your
position. I guess I would first question the design that required me
to use typeid. If the design is ok (which is hard to tell without any
more details) I'd consider filing a complaint with the compiler vendor
about the lack of RTTI.

SG
 
V

Victor Bazarov

Just realized that one can directly compare the vptr of two objects to
check if they belong to the same class or not.

That requires that there *is* such a thing as 'vptr' in both objects.
Below is the example
code:

<code>
#define VPTR(__X__) (*((int*)&(__X__)))

BTW, the use of double underscores in a name is reserved by the
implementation (even if it's a name of a formal argument of a macro).
There is *really* no need for those double underscores here.
class A{
public:
virtual ~A(){}
};
class B{
public:
virtual ~B(){}
};

What's going to happen if

class C {
A a; // contained
};

and you compare a 'C' to an 'A'?
int main(){
A a1,a2;
B b1;
ASSERT( VPTR(a1)==VPTR(a2) );
ASSERT( VPTR(a1)!=VPTR(b) );
}
</code>

This makes it possible to check if two objects are of same type or not
even if RTTI support is not available (like we have on some embedded
platforms). I do understand that this method is only applicable if the
said classes have a vtable. But I am not sure if there are any other
gotchas' that I am overlooking. Whats your take on this.

See above.

V
 
G

gwowen

That requires that there *is* such a thing as 'vptr' in both objects.

 > Below is the example



BTW, the use of double underscores in a name is reserved by the
implementation (even if it's a name of a formal argument of a macro).
There is *really* no need for those double underscores here.




What's going to happen if

    class C {
       A a; // contained
    };

and you compare a 'C' to an 'A'?

Or if sizeof(int)!= sizeof(_vptr)

(obviously, presupposing the usual implementation)
 
G

gwowen

Or if sizeof(int)!= sizeof(_vptr)

(obviously, presupposing the usual implementation)

class foo {
public:
int x;
virtual void reset() {x=0;};
virtual void increment() { x += 1;} ;
}

class tagged_foo : public foo
{
public:
std::string tag;
}

Given the "as is" rule, I can't see any reason why, on an
implementation lacking RTTI, that the vptr for tagged_foo should be
different for foo, since I don't override any of the virtual
functions.

So even as a non-standard, implementation-specific hack, the compiler
may decide to undermine you, in the name of efficiency.
 
V

vj

Thanks for the reply,

@SG
I guess I would first question the design that required me
to use typeid. If the design is ok (which is hard to tell without any
more details) I'd consider filing a complaint with the compiler vendor
about the lack of RTTI.

This is simply a test code. There is nothing production about it. I
simply wanted to test an idea thats all.

@ Victor Bazarov
BTW, the use of double underscores in a name is reserved by the
implementation (even if it's a name of a formal argument of a macro).
There is *really* no need for those double underscores here.

Please bear with the use of __X__ I wasnt aware that the use of
double underscore was reserved even for formal arguments of a macro.
Thanks for pointing this out.


@gwowen
Or if sizeof(int)!= sizeof(_vptr)

Yes you are right. But if this code is specifically for some platform
where sizeof(int) == sizeof(_vptr) (like IA32) then i guess this hack
is workable, though there is nothing portable about it.

What's going to happen if
class C {
A a; // contained
};
and you compare a 'C' to an 'A'?
class tagged_foo : public foo
{
public:
std::string tag;
}
This two really kills the generic use of this hack. Thanks for
pointing this out. I am more upset for the first one which definitely
will give false positives. Though I still think that this hack can be
used on classes residing on the same level of a class hierarchy and
its known that they don't have any associations with each other.

Thanks for your comments guys,
~Vaibhav
 
G

gwowen

class tagged_foo : public foo
{
public:
  std::string tag;

}

Given the "as is" rule, I can't see any reason why, on an
implementation lacking RTTI, that the vptr for tagged_foo should be
different for foo, since I don't override any of the virtual
functions.

OK, the destructor... but replace std::string with a POD type that
doesn't need destructificating, and I think the point still holds.
 
V

vj

OK, the destructor... but replace std::string with a POD type that
doesn't need destructificating, and I think the point still holds.

I tried this definition

class C:public A{
int j;
};

And it seems that MS C/C++ v14.0 compiler still allocates a different
vtable for class C , even though it could have reused the existing
vtable from class A. This was done with maximum optimizations on (/
Ox). I havent tried GCC though but seems like this hack works atleast
on MSVC .

~Vaibhav
 
T

Thomas J. Gritzan

Am 20.04.2011 15:52, schrieb vj:
I tried this definition

class C:public A{
int j;
};

And it seems that MS C/C++ v14.0 compiler still allocates a different
vtable for class C , even though it could have reused the existing
vtable from class A. This was done with maximum optimizations on (/
Ox). I havent tried GCC though but seems like this hack works atleast
on MSVC .

MSVC can't reuse the vtables for this, because the vtable also contains
the RTTI pointer. If you have a compiler without (or with disabled) RTTI
support, the vtables could be merged, because no virtual function in
class A has changed in class C.

You should test if this hack really works with your target compiler on
your target platform; but I advise not to use it in production code.

Instead, you should use virtual functions (think Visitor Pattern) or
integer IDs (or enums) per class.
 
V

vj

Am 20.04.2011 15:52, schrieb vj:













MSVC can't reuse the vtables for this, because the vtable also contains
the RTTI pointer. If you have a compiler without (or with disabled) RTTI
support, the vtables could be merged, because no virtual function in
class A has changed in class C.

You should test if this hack really works with your target compiler on
your target platform; but I advise not to use it in production code.

Instead, you should use virtual functions (think Visitor Pattern) or
integer IDs (or enums) per class.

I again tried this definition with maximum optimizations and disabling
RTTI (/GR-)
class C:public A{
int j;
};

And it seems that MS C/C++ v14.0 compiler still allocates a different
vtable for class C , even though it could have reused the existing
vtable from class A.
 
V

vj

I think multiple inheritance would toss it off too.

--http://crazycpp.wordpress.com

Surely it will also the contained object pattern as described by
gwowen. So overall to me it seems that though this hack surely is non
portable and has lot many issue but under certain strict scenarios
this can serve as a good alternative to using rtti which can be slow.

~Vaibhav
 
G

gwowen

I again tried this definition with maximum optimizations and disabling
RTTI (/GR-)
class C:public A{
    int j;

};

And it seems that MS C/C++ v14.0 compiler still allocates a different
vtable for class C , even though it could have reused the existing
vtable from class A.

But even then, the only guarantee is "it worked today, with these
compiler switches". Compile for space [likely in an embedded app], and
the linker may be smart enough to merge two identical read-only
structures. Upgrade the linker, or even recompile the linker with link-
time-optimizations activated, and a similar thing can happen.

Hell, for an embedded app, a compiler/linker may be able to deduce the
dispatch of all function calls at compile time, and the unreferred-to-
vtable won't even get linked - particularly for a class in an
anonymous namespace, say.

And the hack may not work on this compiler version VC++ v14.0 on
x86_64, because sizeof(int) == 4, on x86_64, whereas sizeof(void*) !=
8.

So yes, the hack might work today, on one one platform (true of all
hacks, or whats the point?) but relying on it is really, really not a
good idea. If you need cheap RTTI-type facilities, there are better
"hacks".
 
J

Juha Nieminen

vj said:
This makes it possible to check if two objects are of same type or not
even if RTTI support is not available (like we have on some embedded
platforms). I do understand that this method is only applicable if the
said classes have a vtable. But I am not sure if there are any other
gotchas' that I am overlooking. Whats your take on this.

Besides being horribly non-portable (which should go without saying),
it doesn't work in cases of multiple inheritance, even if you limit
yourself to just using "interfaces" like in many other OO languages
(which means that nobody should have any rational or philosophical
objections to MI in this case).

If C inherits from both A and B, comparing the (assumed) vptr at
the beginning of an object of type C with an object of type B will
obviously not produce the same result. In fact, most probably it
will not work when comparing objects of type C and A either. A dynamic
cast will, however, work properly in this situation.
 
N

Noah Roberts

Surely it will also the contained object pattern as described by
gwowen. So overall to me it seems that though this hack surely is non
portable and has lot many issue but under certain strict scenarios
this can serve as a good alternative to using rtti which can be slow.

If you're worried about the "slowness" of RTTI (I've never noticed it on
my profiler to tell the truth) then you can simply use an alternative
mechanism. Preferably something that will consistently work.

The tag thunking mechanism in
http://www.artima.com/cppsource/cooperative_visitor.html for
example...AFAICT it only uses standard, well-defined behavior and does
everything you might get from your method.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top