Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will polymorphism hold for C++ object references passed around in C?

I have a C++ lib that makes use of a object hierarchy like this:

class A { ... }
class B : public A { ... }
class C : public A { ... }

I expose functionality through a C API via typedefs and functions, like this:

#ifdef __cplusplus
    typedef A* APtr;
#else
    typedef struct A* APtr;
#endif

extern "C" void some_function(APtr obj);

However, say a use of the C API does something like this:

BPtr b = bptr_create();
some_function((APtr) b);

This is polymorphically valid, since B extends A, and my API depends on such functionality being possible, but I want to make sure that this will still interoperate properly with the C++ code, even if B overrides some of A's virtual methods.

More importantly, why or why not? How can C++ identify at runtime that the obj parameter of some_function is actually a pointer to B, and therefore call its overridden virtual methods instead?

like image 788
Alexis King Avatar asked Jun 19 '13 07:06

Alexis King


1 Answers

The C code is not valid (nor would the equivalent C++ code in a context where the class definition is not visible) because what C does in this case is the equivalent of a reinterpret_cast. Note that in a simple situation like yours it will likely "work" because most compilers will put the single base object at the beginning of the derived object, so a pointer adjustment is not necessary. However, in the general case (especially when using multiple inheritance), the pointer will have to be adjusted to point to the correct subobject, and since C does not know how to do that, the cast is wrong.

So what is meant with "pointer adjustment"? Consider the following situation:

class A { virtual ~A(); int i; ... };
class B { virtual ~B(); int j; ... };
class C: public A, public B { ... };

Now the layout of C may be as follows:

+----------------------------+----------------------------+
| A subobject (containing i) | B subobject (containing j) |
+----------------------------+----------------------------+

where the virtual pointers of both the A and B subobjects point to C.

Now imagine you've got a C* which you want to convert to a B*. Of course the code which receives the B* may not know about the existence of C; indeed, it may have been compiled before C was even written. Therefore the B* must point to the B subobject of the C object. In other words, on conversion from C* to B*, the size of the A subobject has to be added to the address stored into the pointer. If you do not do this, the B* will actually point to the A subobject, which clearly is wrong.

Now without access to the class definition of C, there's of course no way to know that there even is an A subobject, not to mention how large it is. Therefore it is impossible to do a correct conversion from C* to B* if the class definition of C is not available.

like image 198
celtschk Avatar answered Nov 15 '22 03:11

celtschk