Currently I am working with a legacy c++ code base. In this codebase pointer to objects are converted to void-pointers and then stored in a c-library. Consider the following code:
class interface {
public:
virtual void foo() {
std::cout << "Interface" << std::endl;}
virtual ~interface(){};
};
class debug_interface: public interface {
public:
virtual void foo() {
std::cout << "Debug Interface" << std::endl;}
};
The objects interface
and debug_interface
are allocated on the heap and the address is stored to a void pointer. At some point the pointers are retrieved and then casted back to the base-class interface
. Then the virtual function call is invoked. See
int main(int argc, char *argv[]){
void *handle = reinterpret_cast<void*>(new interface());
void *debug_handle = reinterpret_cast<void*>(new debug_interface());
//void *handle = new interface();
//void *debug_handle = new debug_interface();
interface *foo1 = reinterpret_cast<interface*>(handle);
interface *foo2 = reinterpret_cast<interface*>(debug_handle);
//interface *foo1 = static_cast<interface*>(handle);
//interface *foo2 = static_cast<interface*>(debug_handle);
foo1->foo();
foo2->foo();
return 0;
}
First of all I don't understand, why reinterpret_cast is used. As far as I know, pointer-to-objects can be implicitly converted to void*. Furthermore, to make this cast explicit, a static_cast would be enough, right? But the more important question: Is it really save to cast the pointer debug_handle to interface* ( not to debug_interface*) and invoke the virtual call? According to the c++-standard, (5.2.10) this is undefined behavior:
A pointer to an object can be explicitly converted to a pointer to a different object type.69 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
The conversion from handle
to foo1
should be ok, but I can again use a static_cast?
Edit My example source code was wrong. debug_interface is a derived class of interface.
Yes virtual function used for polymorphism can return any type, instead of being a void.
Void functions, also called nonvalue-returning functions, are used just like value-returning functions except void return types do not return a value when the function is executed. The void function accomplishes its task and then returns control to the caller. The void function call is a stand-alone statement.
Casting to void is used to suppress compiler warnings. The Standard says in §5.2. 9/4 says, Any expression can be explicitly converted to type “cv void.” The expression value is discarded. Follow this answer to receive notifications.
The virtual keyword can be used when declaring overriding functions in a derived class, but it is unnecessary; overrides of virtual functions are always virtual. Virtual functions in a base class must be defined unless they are declared using the pure-specifier.
Disclaimer: This first part was written when the two interfaces were not related by inheritance.
The undefined behaviour actually happens here:
foo2->foo();
Here, you are using the interface
API on a pointer to an object that is not implementing this API.
The fact that both interface
and debug_interface
happen to implement the foo()
member as their first method does not change anything: those classes are not related by inheritance, so they are not compatible.
The extract you are citing treats about cases where the conversion itself is allowed. In your case, my understanding is that you can actually convert a pointer to debug_interface
to a pointer to interface
: yet, the only safe thing you can now do with you pointer to interface is to convert it back to a debug_interface
pointer: using it to access interface
members is unsafe.
EDIT: If debug_interface
publicly derives from interface
, this is a different problem.
In this case, it would be totally safe to cast from debug_interface*
to interface*
: the derived-to-base conversion can even be applied implicitly by the compiler.
Yet to be safe, this cast must be done directly, through either:
static_cast
dynamic_cast
(which would introduce runtime check, being overkill for an upcast).Doing it through two reinterpret_cast
is an undefined behaviour: it is likely to work for single inheritance (on some compilers), but it is absolutely not guaranteed by the standard.
Doing it through two static_cast
would also be an undefined behaviour. The standard guarantees that (emphasis mine):
A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.
In your example, you are not converting back to the original pointer, but to another pointer type: the standard is not giving you any guarantee about the value you will get.
Knowing that:
debug_interface
to interface
void *
then back to pointer to the same object type.You could assemble that to get a standard guaranteed solution:
// Safe, see point #1.
// The new expression returns a debug_interface* and static_cast applies a derived-to-base conversion.
interface *debug_handle_interim = static_cast<interface*>(new debug_interface());
// Convert a interface* to void* then back to interface*, see #2
void *type_erased = static_cast<void*>(debug_handle_interim);
interface *debug_handle_back = static_cast<interface*>(type_erased);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With