Problem: I have a class (PortableFoo) designed to be very portable. It contains a scoped class PortableBar. The surrounding codebase (call it Client A) requires both Foo and Bar to have a function that cannot be implemented portably, and Foo's implementation must call Bar's implementation. The following is a solution that compiles and works in GCC, but I know invokes undefined behavior when it casts the reference from base to derived:
//Code in portable codebase
class PortableFoo
{
public:
int a = 1;
class PortableBar
{
public:
int b = 1;
} bar;
};
//Code in Client A
#include <iostream>
class AdaptedFoo: public PortableFoo
{
public:
int fancy_foo_func()
{
return a + ((AdaptedBar&)bar).fancy_bar_func();
}
class AdaptedBar: public PortableBar
{
public:
int fancy_bar_func()
{
return b;
}
};
};
int main()
{
AdaptedFoo foo;
std::cout<<foo.fancy_foo_func(); //prints "2"
return 0;
}
As in the example, it's no issue for me to simply construct objects as AdaptedFoo in the first place in ClientA, the problem is that AdaptedFoo.bar is still of type PortableBar. I know of three solutions to the problem but each have significant drawbacks:
sizeof(AdaptedBar) != sizeof(PortableBar)
. (In the real codebase, I have tested that it always causes a segmentation fault if there's a size mismatch, because I actually store a vector of Bars. The minimal example is not quite complex enough for the segfault to show up.)Question: Since solutions 2 and 3 fail compilation and system requirements respectively, the undefined behavior is my only known valid solution right now. Are there other approaches I'm missing that do not invoke UB?
You ran into one of the main reason why public member variables are a bad idea.
If all access to the class goes through functions, overloading the accessors to return adapters that wrap a reference to the underlying member would be a transparent refactor, and would give you Mostly what you want.
Like so:
class PortableFoo
{
public:
class PortableBar
{
public:
int b = 1;
};
int get_a() { return a;}
bar& get_bar() { return bar;}
private:
int a = 1;
PortableBar bar;
};
class AdaptedFoo : public PortableFoo
{
public:
class AdaptedBar
{
PortableFoo::PortableBar& bar;
public:
AdaptedBar(PortableFoo::PortableBar& src_bar) : bar(src_bar) {}
int fancy_bar_func()
{
return bar.b;
}
};
int get_a() { return a;}
AdaptedBar get_bar() {return AdaptedBar{PortableFoo::get_bar()};}
int fancy_foo_func()
{
return a + get_bar().fancy_bar_func();
}
};