Reusing your code investment

Something I’ve always loved doing is creating frameworks for other developers to use. I’ve also had the pleasure of working on some wonderful developer tools during my career. First at Visio then later at Pelco.

Code Investment

Some companies, like Adobe and Pelco, have built frameworks over time to help deal with abstracting OS specific things into reusable components. These reusable components are built to allow the developer to focus on features and not worry about how to do it per OS. Windows, Mac OS X, and Linux have similar philosophies but specific libraries and models to support their OS. There is a point they do cross however. C and C++ are supported by all three of these platforms and are still in wide use across them.

If API’s vary across systems you have to provide an abstraction for developers if you want to support multiple OS’s. Depending on the level of portability you’re interested in you can end up with a large framework that does everything the same on all platforms, but this can result in lowest common denominator framework, but I digress.

That investment is time consuming but often times well worth because the developers using it can move quickly and get great results on multiple platforms. I saw this first hand at Pelco where I had the pleasure of working with a group of crazy smart people that delivered a cross platform Media Framework for video, audio, and metadata. It’s now used by many groups in the organization, in may different ways. A true success.

How do you do it?

For lower level things it tends to be a bit easier. You can stick to the CRT (C Runtime) for a lot of things. You also tend to typedef things so you end up with matching intrinsic, system defined, and framework defined types. Again, so the developer doesn’t have to spend time thinking about it.

Let’s say you’re writing a C++ class library, or framework. The objects provided by the framework would provide the abstraction.

Big Changes

This leads me to the point of this post. What happens when you have years of investment and the OS vendor makes a HUGE change to their API and development model? The Adobe Photoshop team faced this very hurdle, and managed to get through it. I linked to a post by Adobe’s John Nack that touches on their struggle to bring Photoshop into the Objective-C/Cocoa world(I’d love to see that code.) Objective-C and Cocoa are Apple’s preferred language and API(framework) for creating Mac OS applications. If you’ve ever looked at Objective-C it’s a strange mix of C and Smalltalk, but it maintains support for its C lineage, which allows you to mix C, C++, and Objective-C all in the same file. This is what allowed Adobe, and others, to bring applications into the new model. Hopefully by leveraging their frameworks abstraction from the OS.

I’ve created an example, granted it’s very simple, but it illustrates the point of a framework abstraction. In my example I’ve only created the Mac side of a C++ framework I called GrilledCheese because these abstractions form a kind of sandwich. The application is the top piece of bread, the middle is the abstraction, or framework, and the bottom piece of bread is the native OS layer. Here’s an example of drawing a rectangle in an abstract framework using Objective-C and Cocoa inside a C++ class.

void GrilledCheeseMac::DrawRectangle
(
int x,
int y,
int width,
int height
)
{
// Create a rect from our coordinates.
NSRect rect = NSMakeRect(
(CGFloat)x,
(CGFloat)y,
(CGFloat)width,
(CGFloat)height);
NSFrameRect(rect);

} // DrawRectangle

Here’s that same method implemented on Windows using Windows GDI functions.

void GrilledCheeseWin::DrawRectangle
(
int x,
int y,
int width,
int height
)
{
HDC hdc = ::GetDC(_hWnd);
if (hdc)
{
::Rectangle(hdc, x, y, x+width, y+height);
::ReleaseDC(_hWnd, hdc);
}
} // DrawRectangle

From the application developers viewpoint they would use this method exactly the same on all platforms. No need to fret over how to do it for Windows, Mac, and Linux. They’re used exactly the same way for all. Like this.

GrilledCheese* gc = new GrilledCheese();
if (gc)
{
gc->DrawRect(0, 0, 20, 20);
delete gc;
}

There is something to note in our example. In the example the class is named GrilledCheeseMac. This is where a typedef would come in handy.

E.G.

typedef GrilledCheeseMac GrilledCheese;

On Windows it may look like…

typedef GrilledCheeseWin GrilledCheese;

There you have it.

Please note, this is just one possible way to do things, and the example is overly simple to make the point.

There are definitely more ways than one to skin this cat.