Some said that we were crazy; that it couldn't be done. Thankfully, cooler heads prevailed and we're well on our way to a binary compatible clone of R5.
"But wait!" you cry. "How did the cooler heads which prevailed know that the holy grail of binary compatibility was achievable?" I'm so glad you asked! Keeping reading and be enlightened, Grasshopper.
int32
instead of an int16
, but we're stuck with it now.
class BFoo {
public:
BFoo();
~BFoo();
void SomeFunc();
private:
int32 fBar;
char fZig;
int32 fQux;
int32 fUnused[2];
};
The Be engineers that originally wrote this BFoo purposely added some data
padding in the form of an array of 2 int32
s (they did this with
most classes in the API). Now let's suppose in your implementation, you
really didn't need fQux
. You can add fQux
's bytes
into fUnused
:
class BFoo
{
...
private:
int32 fBar;
char fZig;
int32 fUnused[3];
};
Nothing could be easier! An additional twist that should be noted is that
data member order must also be preserved. In much the same way as existing
apps will "look" for virtual functions in a specific place in the vtable,
so will they "look" for data members in a specific place in an object.fZig
, either?" you wonder. "It's only
one byte, not four like an int32
!" Have no fear! Just rename it
"fUnusedChar
" and be done with it.
The second situation that can make preserving object size tricky is if there
aren't enough bytes of data available. Building on our cheesy BFoo
example, let's suppose that rather than getting rid of fQux
and
fZig
, you actually needed to add another 4
int32
s worth of data: fNewData1
through
fNewData4
. The original implementation of BFoo has two extra
int32
s which we can use, but that leaves us two int32
s
short. What to do? The easiest thing is to create a data structure to hold
your new data and convert one of the fUnused
items into a pointer
to that structure:
Voila! More data without making the class bigger. Make sure you're cleaning up
your new (dynamically allocated) data in the destructor. NOTE: this trick
will only work if the class originally had a destructor! Adding a destructor
after the fact won't work for existing apps (since they won't call it), leading
to memory leaks. Fortunately, there are very few instances in the BeAPI where a
class doesn't have a destructor already declared.
// Foo.h
struct _BFooData_;
class BFoo
{
public:
BFoo();
~BFoo();
void SomeFunc();
private:
int32 fBar;
char fZig;
int32 fQux;
_BFooData_* fNewData;
int32 fUnused[1];
};
// Foo.cpp
struct _BFooData_
{
int32 fNewData1;
int32 fNewData2;
int32 fNewData3;
int32 fNewData4;
};
BFoo::BFoo()
{
fNewData = new _BFooData_;
}
BFoo::~BFoo()
{
delete fNewData;
}
Copyright © 2001-2002 OpenBeOS Project