# Binary Compatibility in 3 Easy Steps!

## An Introduction

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.

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.

## The Issues

There are three basic issues that have to be addressed to ensure binary compatibility:

- Names must be identical
  This includes class and structure names as well as public, protected and global function and variable names.
- Object sizes must be identical
  Classes must contain the same number of bytes of data; global variables must be the same size. Maybe BGlobalVar should've been an `int32` instead of an `int16`, but we're stuck with it now.
- Virtual function table layout must be identical
  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.

## The Nitty Gritty

"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!

1. Make a copy of the appropriate Be header file
   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.
2. Implement public, protected and virtual functions
   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).
3. Make sure you don't change the number of bytes used for data
   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.
   Let's say we have a class BFoo:
   `

       	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.
   "But what if I don't need `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: ``

       	// 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;
       	}

   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.

And there you have it: Binary Compatibility in 3 Easy Steps!
Questions, comments, or corrections? Please let me know!

The OpenBeOS project is hosted by:

Copyright © 2001-2002 OpenBeOS Project