It is accomplished ...
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
<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();
|
||||
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 to do is 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;
|
||||
_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. Notice the added
|
||||
destructor; make sure you're cleaning up your new (dynamically allocated) data.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
And there you have it: Binary Compatibility in 3 Easy Steps!<br>
|
||||
Questions, comments, or corrections? Please let
|
||||
<a href="mailto:[email protected]">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 © 2001-2002
|
||||
<a href="http://www.openbeos.org">OpenBeOS</a> Project</small>
|
||||
</center>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user