Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is the address of a typeid-object constant

9 views
Skip to first unread message

Bonita Montero

unread,
Aug 14, 2019, 4:00:02 AM8/14/19
to
When I have a code like this:

#include <typeinfo>

struct S1
{
virtual void f();
};

struct S2 : public S1
{
virtual void f();
};

bool f( S1 *a, S2 *b )
{
return &typeid(a) == &typeid(b);
}

Is it really guaranteed to work, i.e. are the adresses
of typeid-objects really constants of all "invocations"
of the operator typeid()?
I know that there is type type_info::hash-code and it
could simply return a size_t which has the same value
as the address of the global type_info-object. But with
the implementation I checked I get a slower method-call.

Alf P. Steinbach

unread,
Aug 14, 2019, 4:16:03 AM8/14/19
to
On 14.08.2019 09:59, Bonita Montero wrote:
> When I have a code like this:
>
> #include <typeinfo>
>
> struct S1
> {
>     virtual void f();
> };
>
> struct S2 : public S1
> {
>     virtual void f();
> };
>
> bool f( S1 *a, S2 *b )
> {
>     return &typeid(a) == &typeid(b);
> }
>
> Is it really guaranteed to work, i.e. are the adresses
> of typeid-objects really constants of all "invocations"
> of the operator typeid()?

As far as I know, no.

A portable way to compare two `std::type_info` objects for equality is
to just compare them directly with `==`, or `!=`.

The `before` method provides an ordering relation in an inconvenient
way. If you need ordering then consider converting the objects to
`std::type_index`, which provides a full set of relational operators.


> I know that there is type type_info::hash-code and it
> could simply return a size_t which has the same value
> as the address of the global type_info-object. But with
> the implementation I checked I get a slower method-call.

Not sure what you're asking, but I'd guess that for any implementation
`hash_code` is reasonably efficient while providing a reasonable code.


Cheers!,

- Alf

Bonita Montero

unread,
Aug 14, 2019, 4:29:34 AM8/14/19
to
> bool f( S1 *a, S2 *b )
> {
>     return &typeid(a) == &typeid(b);
return &typeid(*a) == &typeid(*b);
> }

Bonita Montero

unread,
Aug 14, 2019, 4:42:54 AM8/14/19
to
> Not sure what you're asking, but I'd guess that for any implementation
> `hash_code` is reasonably efficient while providing a reasonable code.

Even doing typeid(x) is slow with VC++, and operator == also has a
lot of overhead; I just traced through the disassembly. It think
it's better to stick with a virtual method that gives a scalar value
to compare against other objects.

Bo Persson

unread,
Aug 14, 2019, 5:54:37 AM8/14/19
to
The "slowness" has to do with allowing the code to correctly compare two
typeid(x) possibly coming from different DLLs. That could easily produce
two different addresses, even if they are both constant.

The language standard doesn't say anything about dynamic libraries, so
this is an extension.


Bo Persson

Öö Tiib

unread,
Aug 14, 2019, 7:56:29 AM8/14/19
to
Basically taking address of the const std​::​type_­info object from typeid is
wrong, since these are allowed to be different objects for same type:

const std::type_info& ti1 = typeid(A);
const std::type_info& ti2 = typeid(A);

assert(&ti1 == &ti2); // not guaranteed
assert(ti1.hash_code() == ti2.hash_code()); // guaranteed
assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed

Bonita Montero

unread,
Aug 14, 2019, 9:19:39 AM8/14/19
to
> The "slowness" has to do with allowing the code to correctly compare two
> typeid(x) possibly coming from different DLLs. That could easily produce
> two different addresses, even if they are both constant.

You're right. I just wrote a little EXE and DLL that tests this.

This is the shared header:

struct S
{
virtual void f();
};

void S::f()
{
}

This is the DLL (ignore the __declspec(...)):

#include <windows.h>
#include "..\shared\shared.h"

__declspec(dllexport)
void GimmeObject( S *&pS )
{
static S s;
pS = &s;
}

This is the executable:

#include <iostream>
#include "..\shared\shared.h"

using namespace std;

__declspec(dllimport)
void GimmeObject( S *&pS );

int main()
{
S s;
S *pS;
GimmeObject( pS );
cout << "typeid(s) == typeid(*pS): "
<< (typeid(s) == typeid(*pS) ? "true" : "false")
<< endl;
}

The whole Visual Studio 2019 project can be downloaded here:
https://transfer.sh/F4QKl/typeid_dll.zip

> The language standard doesn't say anything about dynamic libraries,
> so this is an extension.

Ok, and I think this also applies to dynamic_cast; i.e. you could
downcast an object built in a DLL to a class included in an EXE.
But I think it would have been cleverer to say that this doesn't
work across DLL / excecutable boundaries and let the VMT-pointers
and RTTI-information be a part that could be imported by indidual
compiler-directives. This would allow more performant implementa-
tions; f.e. type_id::hash_code() could be simply the address of
the VMT-table.

Bonita Montero

unread,
Aug 14, 2019, 9:23:41 AM8/14/19
to
> assert(&ti1 == &ti2); // not guaranteed

This is even slow with my MSVC++-implementation,
so it doesn't matter if this works or not. ;-)

Melzzzzz

unread,
Aug 14, 2019, 10:23:11 AM8/14/19
to
On 2019-08-14, Bonita Montero <Bonita....@gmail.com> wrote:
> When I have a code like this:
>
> #include <typeinfo>
>
> struct S1
> {
> virtual void f();
> };
>
> struct S2 : public S1
> {
> virtual void f();
> };
>
> bool f( S1 *a, S2 *b )
> {
> return &typeid(a) == &typeid(b);
> }
>
> Is it really guaranteed to work, i.e. are the adresses
> of typeid-objects really constants of all "invocations"
> of the operator typeid()?

Address perhaps changes, but you can take it and store it for later use.



--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

Bonita Montero

unread,
Aug 14, 2019, 10:45:36 AM8/14/19
to
> Ok, and I think this also applies to dynamic_cast; i.e. you could
> downcast an object built in a DLL to a class included in an EXE.

Ok, it works:

shared.h (having the method doubled on the exe and dll ;-)):

struct S
{
virtual void f();
};

void S::f()
{
}

struct T : public S
{
virtual void f();
};

void T::f()
{
}

DLL:

#include <windows.h>
#include "..\shared\shared.h"

static S s;
static T t;

__declspec(dllexport)
void GimmeObject( S *&pS, S *&pT )
{
pS = &::s;
pT = &::t;

}

EXE:

#include <iostream>
#include "..\shared\shared.h"

using namespace std;

__declspec(dllimport)
void GimmeObject( S *&pS, S *&pT );

int main()
{
S s;
S *pS, *pT;
GimmeObject( pS, pT );
cout << "typeid(s) == typeid(*pS): "
<< (typeid(s) == typeid(*pS) ? "true" : "false")
<< endl;
cout << "dynamic_cast<T *>(pT) != nullptr: "
<< (dynamic_cast<T *>(pT) != nullptr ? "true" : "false")
<< endl;
}
0 new messages