There is a forward C struct declared in an unmodifiable header. I would like to “virtually” add convenience member functions to it. Obviously my first choice would be to extend the struct and add the methods to the derived class. No-can-do, as the struct itself is declared as “forward” in the header, so I get the error “error: invalid use of incomplete type …”. I get a similar error if I try to define a new struct with a single element of the old struct. This sucks.
However, I was thinking that I could do some hackery with reinterpret_cast to get it to work anyway. The way it would go is this:
//defined in header
struct A forward;
void do_something_with_A(A* a, int arg);
//defined in my wrapper
struct B {
B* wrap(A* a) {return reinterpret_cast<B*>(a); }
void do_something(int arg) {do_something_with_A(reinterpret_cast<A*>(this),arg); }
}
If I added implicit conversions from type B to type A, I was thinking that this could work out almost as if B was a zero-data inheritor of A. However, this obviously brings up the question: is this undefined in C++? Normally I would think that accessing an element of an illegally casted struct would be undefined; that makes sense. However, I would think that reinterpret_casting from one type to another, passing that pointer around, and then casting back again, without doing anything illegal in between would be fine. I would also think that the way a compiler implements non-virtual struct members would be creating a function
B::do_something(B* b, int arg)
and calling that with the appropriate argument for B. This then reduces to the previous case, which by my dubious logic is okay. So I would think calling .do_something on a struct which is actually a reinterpret_cast A would be okay.
However, this says nothing for what the C++ standard actually says on the matter. Any help with that? Also, if someone has information on how well this will work practically, (i.e. “Every compiler ever made accepts this”, or “this only works with a few compilers”) that would also be helpful, but slightly less so.
I don’t believe this works if you’re using
static_castbecause you cannotstatic_castbetween two completely unrelated class types. To be specific, if you have a pointer of typeA*and try to convert it to a pointer of typeB*, thestatic_castonly succeeds if this declaration is valid:or if
Bis non-virtually derived fromA(which it isn’t). See the ISO spec §5.2.9 for details on the specifics of this. If we consider the above declaration, the only possible conversions that could be applied here in all of §4 are those in §4.10, and of those the only one that might be applicable is conversion from base to derived classes (§4.10/3), but this doesn’t apply here becauseAandBaren’t related types.The only cast you might be able to use here is a
reinterpret_cast, and it doesn’t look like this will work either. In particular, the behavior of casting across class hierarchies is (§5.2.10/7)So immediately there’s no guarantee that anything is going to work if the two objects have different alignment restrictions, and you cannot ensure that this is true. But suppose that you could. In that case, though, I believe that this will actually work correctly! Here’s the reasoning. When you call the member function of the
Bobject, then rule &5.2.2/1) kicks in and says that, since the function is nonvirtual:Okay, so we’re at least calling the right function. Now, what about the
thispointer? Well, according to &5.2.2/4:The type conversion done in the last part is the identity conversion from a
B*to aB*, since that’s the selected type. So you’ve called the right function with thethispointer set appropriately. Nice! Finally, when you do areinterpret_castback to the original type, by the earlier rule, you’ll get back theA*object and everything will go as expected.Of course, this only works if the objects have the same alignment requirements, and this cannot be guaranteed. Consequently, you shouldn’t do it!
Hope this helps!