distinguishing wheter void* ptr points to class A or class B?

J

jacek.dziedzic

Hello!

I have some legacy C code which expects a pointer to a function to
be evaluated at one point.
Because of the pointer-to-function vs. pointer-to-member
incompatibility, this needs to be a
global function. To aid the passing of some extra data to the
function, it takes an extra
parameter of type void* which is passed uninterpreted to it.

I am in a situation where this void* pointer can point either to
class A or to a class B,
which are not related. Is there a way to perform a reliable cast in
the function or otherwise
distinguish which the void* pointer actually points to? This is
compiled as C++.
I can static_cast<> to whatever, but, obviously, if I get the class
wrong, this segfaults
at the first dereference of a member. dynamic_cast<> does not work on
void*.

Is there a way out? I can modify classes A and B at will and the
function in question,
but its signature must remain intact.

TIA,
- J.
 
K

Kai-Uwe Bux

Hello!

I have some legacy C code which expects a pointer to a function to
be evaluated at one point.
Because of the pointer-to-function vs. pointer-to-member
incompatibility, this needs to be a
global function. To aid the passing of some extra data to the
function, it takes an extra
parameter of type void* which is passed uninterpreted to it.

I am in a situation where this void* pointer can point either to
class A or to a class B,
which are not related. Is there a way to perform a reliable cast in
the function or otherwise
distinguish which the void* pointer actually points to? This is
compiled as C++.
I can static_cast<> to whatever, but, obviously, if I get the class
wrong, this segfaults
at the first dereference of a member. dynamic_cast<> does not work on
void*.

Is there a way out? I can modify classes A and B at will and the
function in question, but its signature must remain intact.

As far as I know, once the pointer is converted to void*, type information
is lost. Thus, the classes must take care of the problem. Unfortunately,
you cannot call member function of A and B without knowing their type
first.

I wonder (and I am not sure whether it will work) if you could embed a type
tag field at the beginning of classes A and B. Like so:

struct A {
int type_tag;
...
};

struct B {
int type_tag;
...
};

The constructors of A and B would set the type_tag to the right values, and
you would cast the void* to int* first in order to tell whether it is A or
B.

One complication is that the standard does not guarantee that the type_tag
comes first unless (a) the classes A and B do not derive from somewhere and
(b) there are no interfering access specifiers. Probably, there are many
more.



Best

Kai-Uwe Bux
 
R

Ron Natalie

Daniel said:
class C
{
public:
virtual int getType() =0;
};

And have A and B inherit from C; then, cast your void* to C* (this is
what I'm not sure about whether it is valid to do), query for the type,
and proceed accordingly.
Once you have a common polymorphic ancestor you can also use
dynamic_cast or typeid.
 
D

Daniel Kraft

I have some legacy C code which expects a pointer to a function to
be evaluated at one point.
Because of the pointer-to-function vs. pointer-to-member
incompatibility, this needs to be a
global function. To aid the passing of some extra data to the
function, it takes an extra
parameter of type void* which is passed uninterpreted to it.

I am in a situation where this void* pointer can point either to
class A or to a class B,
which are not related. Is there a way to perform a reliable cast in
the function or otherwise
distinguish which the void* pointer actually points to? This is
compiled as C++.
I can static_cast<> to whatever, but, obviously, if I get the class
wrong, this segfaults
at the first dereference of a member. dynamic_cast<> does not work on
void*.

Is there a way out? I can modify classes A and B at will and the
function in question,
but its signature must remain intact.

I'm not really sure whether this is valid, but I would try to define
some class C as:

class C
{
public:
virtual int getType() =0;
};

And have A and B inherit from C; then, cast your void* to C* (this is
what I'm not sure about whether it is valid to do), query for the type,
and proceed accordingly.

cheers,
Daniel
 
J

Justin.SpahrSummers

I have some legacy C code which expects a pointer to a function to
be evaluated at one point.
Because of the pointer-to-function vs. pointer-to-member
incompatibility, this needs to be a
global function. To aid the passing of some extra data to the
function, it takes an extra
parameter of type void* which is passed uninterpreted to it.

I am in a situation where this void* pointer can point either to
class A or to a class B,
which are not related. Is there a way to perform a reliable cast in
the function or otherwise
distinguish which the void* pointer actually points to? This is
compiled as C++.
I can static_cast<> to whatever, but, obviously, if I get the class
wrong, this segfaults
at the first dereference of a member. dynamic_cast<> does not work on
void*.

You could simple define a small structure with a type tag and a
pointer:

struct C {
int type;
void *ptr; // to either an object of class A or class B
}

When you create the structure, modify the type field accordingly so
that it can be read back once you have to determine what class the
objects belong to. After that, it's just a simple static_cast<> of the
'ptr' member.
 
A

Alf P. Steinbach

* (e-mail address removed):
You could simple define a small structure with a type tag and a
pointer:

struct C {
int type;
void *ptr; // to either an object of class A or class B
}

When you create the structure, modify the type field accordingly so
that it can be read back once you have to determine what class the
objects belong to. After that, it's just a simple static_cast<> of the
'ptr' member.

Uhm, well.

In C++ it's usually ungood to explicitly discriminate on type, when a
virtual function can do that type-discrimination.

Here's one way to Do Things (off the cuff, not compiled):

typedef void (*CCallbackFunc)( void* );
typedef void (*CFuncThatCallsBack)( CCallbackFunc, void* );
void generalCallback( void* );

struct AbstractCallbackHandler
{
virtual void onCallback() = 0;
void* pointer() { return this; }
invoke( CFuncThatCallsBack f ) { f( generalCallback, pointer(); }
};

CCallBackFunc generalCallBack( void* arg )
{
static_cast<AbstractCallbackHandler*>( arg )->onCallback();
}

template< class T > void callbackFor( T& o );

template< class T >
struct CallbackHandler
{
T* p;
CallbackHandler( T& o ): p( &o ) {}
virtual void onCallback() { callbackFor( *p ); }
}


// ... code specific to using classes A and B:

template<> callbackFor<A>( A& aha ) { ... }
template<> callbackFor<B>( B& boo ) { ... }

extern "C" void someCFunc( CCallBackFunc callBackFunc, void* arg );

struct A {};
struct B {};

void doThingsTo( A& a )
{
CallbackHandler<A>( a ).invoke( someCFunc );
}

Cheers, & hth.,

- Alf
 
J

Justin.SpahrSummers

* (e-mail address removed):




Uhm, well.

In C++ it's usually ungood to explicitly discriminate on type, when a
virtual function can do that type-discrimination.

Here's one way to Do Things (off the cuff, not compiled):

typedef void (*CCallbackFunc)( void* );
typedef void (*CFuncThatCallsBack)( CCallbackFunc, void* );
void generalCallback( void* );

struct AbstractCallbackHandler
{
virtual void onCallback() = 0;
void* pointer() { return this; }
invoke( CFuncThatCallsBack f ) { f( generalCallback, pointer(); }

Seems like you forgot a paren here.
};

CCallBackFunc generalCallBack( void* arg )

I think you meant "void", not "CCallBackFunc".
{
static_cast<AbstractCallbackHandler*>( arg )->onCallback();
}

template< class T > void callbackFor( T& o );

template< class T >
struct CallbackHandler
{
T* p;
CallbackHandler( T& o ): p( &o ) {}
virtual void onCallback() { callbackFor( *p ); }
}

// ... code specific to using classes A and B:

template<> callbackFor<A>( A& aha ) { ... }
template<> callbackFor<B>( B& boo ) { ... }

extern "C" void someCFunc( CCallBackFunc callBackFunc, void* arg );

struct A {};
struct B {};

void doThingsTo( A& a )
{
CallbackHandler<A>( a ).invoke( someCFunc );
}

All of the code you gave took me a hell of a long time to parse... I
personally would opt for the simple and transparent structure. That
said, I couldn't have come up with the code you gave in a million
years... it's very clever.
 
A

Alf P. Steinbach

* (e-mail address removed):
Seems like you forgot a paren here.
Rightum.



I think you meant "void", not "CCallBackFunc".
Right.

And here the struct declarations need to come textually before the
callbackFor specializations.

All of the code you gave took me a hell of a long time to parse... I
personally would opt for the simple and transparent structure.

As I wrote, discriminating explicitly on type is generally ungood.

For it means that as you introduce or use other types, you'll have to
(remember to) update every place that does such discrimination. And fix
bugs separately each place. Instead of in one central place.

And with explicit discrimination, every place you need the same
mechanism you'll have to code it anew, duplicating the earlier effort.

That
said, I couldn't have come up with the code you gave in a million
years... it's very clever.

Uh, thanks, but it isn't, really. :)

Cheers, & hth.,

- Alf
 
K

Kai-Uwe Bux

Ron said:
Once you have a common polymorphic ancestor you can also use
dynamic_cast or typeid.

You could use dynamic_cast on A* and B*. But you cannot use dynamic_cast to
get from void* to C*.


Best

Kai-Uwe Bux
 
H

Heinz Ozwirk

Daniel Kraft said:
I'm not really sure whether this is valid, but I would try to define some
class C as:

class C
{
public:
virtual int getType() =0;
};

And have A and B inherit from C; then, cast your void* to C* (this is what
I'm not sure about whether it is valid to do), query for the type, and
proceed accordingly.

In practice it probably works, but the original pointer, either to an
instance of A or B, must be cast to C* before the result of this cast is
converted to void*. Otherwise problems might occure when multiple
inheritance is used.

Heinz
 
J

James Kanze

As far as I know, once the pointer is converted to void*, type
information is lost. Thus, the classes must take care of the
problem. Unfortunately, you cannot call member function of A
and B without knowing their type first.

Correct. You can, however, pass a pointer to something like:

enum Type { typeA, typeB } ;
struct TypedPtr
{
Type type ;
void* ptr ;
} ;

and go on from there. But...
I wonder (and I am not sure whether it will work) if you could embed a type
tag field at the beginning of classes A and B. Like so:
struct A {
int type_tag;
...
};
struct B {
int type_tag;
...
};
The constructors of A and B would set the type_tag to the
right values, and you would cast the void* to int* first in
order to tell whether it is A or B.
One complication is that the standard does not guarantee that
the type_tag comes first unless (a) the classes A and B do not
derive from somewhere and (b) there are no interfering access
specifiers. Probably, there are many more.

Actually, it only guarantees that the address of the type_tag is
the same as the address of the class if the class is a POD. On
the other hand, if you can modify the classes, you can easily
make them both derive from a common base class, which consists
of nothing but a virtual destructor, and pass a pointer to the
common base. After which, dynamic_cast will work. If you can't
modify the classes in question, you can do something similar by
deriving a new class from the original class and the common
base, and passing a pointer to the common base.

Note that if the void* is forced, e.g. because the data is being
passed through a C API, you'll have to be very careful with the
casting. For example:

class ForRTTI
{
public:
virtual ~ForRTTI() {}
} ;

template< typename T >
class DynamicallyTyped : public T, public ForRTTI
{
// ...
} ;

void
handleCallback( void* p )
{
ForRTTI* obj = static_cast< ForRTTI* >( p ) ;
// further code using dynamic_cast, etc...
}

// ...
DynamicallyTyped< C > myC ;
registerCallback( handleCallback, &myC ) ;

will result in undefined behavior; the last line must be:

registerCallback( handleCallback,
static_cast< ForRTTI* >( &myC ) ) ;

(Basically, the void* must be converted back to the exact type
it was created from, or you have undefined behavior.)
 

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

No members online now.

Forum statistics

Threads
474,434
Messages
2,571,690
Members
48,796
Latest member
Greg L.

Latest Threads

Top