haiku/docs/develop/ikteam/BinCompat.html
ejakowatz 7dfa005716 Fixed a mistake in the declaration for class BFoo in the "pointer to new
data structure example"; int32 fQux should have been included (otherwise,
the class size would have be reduced by 4 bytes!)


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2550 a95241bf-73f2-0310-859d-f6bbb57e9c96
2003-01-25 20:59:47 +00:00

188 lines
6.8 KiB
HTML

<html>
<header>
<title>Maintaining Binary Compatibility</title>
</header>
<body>
<h1>Binary Compatibility in 3 Easy Steps!<hr></h1>
<h2>An Introduction</h2>
In the early days of the OpenBeOS project, a debate raged concerning one of the projects
primary goals: maintaining binary compatibility with BeOS R5. The idea was that the
only way an effort to rewrite BeOS would be successful was if folks could continue
running the apps they already had. Certainly, a lot of software available for BeOS is
open source or actively maintained -- these apps could just be recompiled if necessary.
Others -- PostMaster, gobe's Productive suite and a few other crucial apps -- weren't
likely to get rebuilt, either because the original author had stopped maintainance
without being kind enough to release the source, or because it just wouldn't be
commercially feasible.<p>
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.<p>
"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.<p>
<h2>The Issues</h2>
There are three basic issues that have to be addressed to ensure binary compatibility:
<ul>
<li>
<b>Names must be identical</b><br>
This includes class and structure names as well as public, protected and global
function and variable names.
</li>
<li>
<b>Object sizes must be identical</b><br>
Classes must contain the same number of bytes of data; global variables must
be the same size. Maybe BGlobalVar should've been an <code>int32</code>
instead of an <code>int16</code>, but we're stuck with it now.
</li>
<li>
<b>Virtual function table layout must be identical</b><br>
The most cryptic and confusing aspect of maintaining binary compatibility.
The issue essentially boils down to this: for any given class, there must
be the same number of virtual functions, declared in the same order as the
original.
</li>
</ul>
<h2>The Nitty Gritty</h2>
"Good grief!" you say. "How on earth do I keep all this stuff straight?" Ah,
Grasshopper, it is easier that you might imagine. Just follow these steps, and you
should be binary compatible in no time!
<ol>
<li>
<b>Make a copy of the appropriate Be header file</b><br>
This is now your header file. You may need to change a thing or two, but what
you can (or will need to) change is quite limited, and discussed below.
</li>
<li>
<b>Implement public, protected and virtual functions</b><br>
In the course of doing this, you may discover that there are some private
non-virtual function declarations that you just don't use. Feel free to axe
them! Since they're private, nobody using the class will miss them, and
because they're not virtual, they don't effect the vtable layout. Conversely,
if you find a need to add some private, non-virtual functions, go right ahead
(for the very same reasons).
</li>
<li>
<b>Make sure you don't change the number of bytes used for data</b><br>
There are two situations that can make this seem difficult. First, there
may be data members that you don't use in your reimplementation. You can
just leave them (safe, but a little messy) or you can add the extra members'
bytes to the class's "unused" data array. An example will make this clear.<br>
Let's say we have a class BFoo:<br>
<code>
<pre>
class BFoo {
public:
BFoo();
~BFoo();
void SomeFunc();
private:
int32 fBar;
char fZig;
int32 fQux;
int32 fUnused[2];
};
</pre>
</code>
The Be engineers that originally wrote this BFoo purposely added some data
padding in the form of an array of 2 <code>int32</code>s (they did this with
most classes in the API). Now let's suppose in your implementation, you
really didn't need <code>fQux</code>. You can add <code>fQux</code>'s bytes
into <code>fUnused</code>:<br>
<code>
<pre>
class BFoo
{
...
private:
int32 fBar;
char fZig;
int32 fUnused[3];
};
</pre>
</code>
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.<br>
"But what if I don't need <code>fZig</code>, either?" you wonder. "It's only
one byte, not four like an <code>int32</code>!" Have no fear! Just rename it
"<code>fUnusedChar</code>" and be done with it.<p>
The second situation that can make preserving object size tricky is if there
aren't <i>enough</i> bytes of data available. Building on our cheesy BFoo
example, let's suppose that rather than getting rid of <code>fQux</code> and
<code>fZig</code>, you actually needed to <i>add</i> another 4
<code>int32</code>s worth of data: <code>fNewData1</code> through
<code>fNewData4</code>. The original implementation of BFoo has two extra
<code>int32</code>s which we can use, but that leaves us two <code>int32</code>s
short. What to do? The easiest thing is to create a data structure to hold
your new data and convert one of the <code>fUnused</code> items into a pointer
to that structure:
<code>
<pre>
// 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;
}
</pre>
</code>
Voila! More data without making the class bigger. Make sure you're cleaning up
your new (dynamically allocated) data in the destructor. <b>NOTE:</b> 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.
</li>
</ol>
And there you have it: Binary Compatibility in 3 Easy Steps!<br>
Questions, comments, or corrections? Please let
<a href="mailto:erik@cgsoftware.com">me</a> know!<br>
<hr>
<!-- The obligatory SourceForge plug -->
<center>
<small>The OpenBeOS project is hosted by:</small><br><br>
<a href="http://sourceforge.net">
<img src="http://sourceforge.net/sflogo.php?group_id=33869&type=1" width="88" height="31" border="0" alt="SourceForge Logo">
</a>
<p>
<small>Copyright &copy; 2001-2002
<a href="http://www.openbeos.org">OpenBeOS</a> Project</small>
</center>
</body>
</html>