Knowing Your Boundaries: C++/Objective-C with the Membrane Library

Apple’s preferred framework for writing new Mac OS X applications is Cocoa. Most cross-platform application development for the Mac not done by Apple is done in Carbon.

One reason for this state of affairs is that most large apps for the Mac are legacy apps, originally written for Mac OS 9 and earlier using the Toolbox. These large, flagship apps, such as Photoshop and Microsoft Office, are very important to the platform and with any luck won’t be going away anytime soon. For that reason alone, Apple must stick with a dual-API strategy.

But the second reason has to do with a different kind of history: the history of languages.

Apple’s OO language of choice is Objective-C, thanks to its NeXT heritage. Most cross-platform application development which isn’t done in scripting languages or Java is done in C++. Integrating large C++ and Objective-C codebases is just too hard. C++ and Objective-C code can be mixed, but their error-handling mechanisms and resource allocation traditions are incompatible, so there are no high-profile, large-scale examples.2 It’s easier to write (or buy, thank you, Metrowerks) your own C++ layer over Carbon, and stick with a single set of mechanisms.

Until now.

Well, more accurately, until June of 2003. That’s when Mac Murrett and I introduced the Membrane library to MacHack 18, along with a paper and sample code.

As the paper states in its introduction:

C++ and Objective-C can be used in the same codebase, but their error-handling mechanisms don’t mix well without some extra effort. This means careful resource allocation on both sides and translation code at every boundary between them. Sound like too much work? This paper describes the Membrane C++/Objective-C library, which makes these steps as easy as possible – often simple one-liners – while still both allowing for great flexibility and encouraging rigorous and systematic error-handling policies.

Membrane allows you to create rules for converting C++ exception types to Objective-C NSExceptions and vice versa. It allows you to encapsulate objects from one language in another so deallocation is automatic. This means you can develop the right way, fast.

For small codebases, this is not necessarily a big win. If you only call a few C++ APIs in your Cocoa application, you can just throw some try/catch blocks around them and be done with it. Alternately, if your C++ code doesn’t use exceptions much, you won’t need to convert them with Membrane.

So the conditions where Membrane will be really useful are: large codebase, modern C++ practices.

Working on such a project? Then go to the permanent Membrane Web page at http://umbar.com/membrane and have a look.


1Yes, there are exceptions, the biggest of them being iTunes. Funny coincidence that it’s the only new Apple app ported to Windows, eh?
2The exception to this statement being Safari and the C++ KDE codebase. But the KDE codebase is old and rarely uses such modern C++ best practices as exceptions.

0WNZ0RED

Don’t be alarmed.

Helpful Tiger has not been hacked.

No one’s going to take my home page and rewrite it so it’s filled with pictures of the Janet Jackson Maneuver from the Superbowl.

But I have been thinking about a certain kind of rewrite: code rewrites.

Joel Spolsky’s article on rewriting is clear: don’t throw away your old code and rewrite it from scratch, ever. No one appears to be arguing with him, and yet I’ve seen throw-away rewrite after rewrite, over and over again.

Why?

What’s in it for the developers?

One word: ownership.

When you rewrite code, it becomes yours. You’re invested in it, more focused on it, more willing to go the extra mile for bugfixes and enhancements.

This is the real benefit of rewrites. You may be able to convince a manager that the rewrite improved things, despite Spolsky’s arguments, because the rewrite improved you.

So how do you get those benefits without the downsides? Here are my thoughts, in no particular order, but phrased in the imperative, for both your and my consideration.

Enjoy!

Version 2 Syndrome
OK, so you’ve inherited a codebase. Most of the time, developers are already forced to work with such a codebase for a while before rewriting it. It’s only after the first new release is out the door, where they’re familiar enough with it to know its faults, but still not necessarily invested in it, that big rewrites get proposed. One idea for managers is to say: “Version 2 must still be based on the original codebase. You want to make changes? Propose them to me in terms of a series of changes to the existing codebase.” The challenge of implementing that may lead to more investment, more ownership.

Bye Bye Birdy
If you are going to allow a rewrite, don’t assign it to a developer who’ll be off the project in a few months. You’ve wasted your only concrete benefit! Same goes for “troubleshooters” brought in to fix a project on the ropes. Let ’em propose new designs, but let the developers who’ll be staying on the project do the work.

Square Peg
Some developers are just extraordinarily good at solving problems. They need to be challenged constantly, always chasing that Eureka! moment. And then the rest of the team is left with code that works, works really well, but nobody else understands. Such developers will never own anything. Trying to tie them to ownership is putting a square peg in a round hole. Put someone else in charge of that code, ASAP, so they can figure it out – and put questions about it to the original developer while she or he still remembers it. It’s an extra cost to involve a second developer, but it will be worth it.

Institutionalized
If a large, complicated codebase belongs to a company, but all the developers who worked on it have left or been abruptly transferred, it’s as if the company doesn’t really own it anymore. That may have been part of the problem with Netscape. The people who had owned the code weren’t in charge any more. Avoid this. Don’t move people around if the benefits – what, say, a certain developer can bring to a new project – don’t outweigh the costs – an eventual rewrite of another project’s codebase when major new functionality needs to be added.

This brings up the question: how do you transfer that real feeling of ownership? I don’t just mean formal responsibility, I mean the state of affairs where the new developers feel as invested in the code as the old ones did. The exact transfer process might be less important than finding the right kind of developer, willing to learn and commit to something external.

PDF Browser Plugin, SafariNoTimeout, Pacifist, and Iconographer (and Filthy Hypocrites)

Welcome to the newest Helpful Tiger category! Here’s where you’ll find out about the utilities I’ve found most useful in my programming endeavors.

PDF Browser Plugin
http://schubert-it.com/pluginpdf
In-browser viewer for PDF documents. Uses Apple’s PDF engine, so not a full-featured as Adobe’s viewer, but certainly very convenient, and it gets improvements when Apple’s engine improves. So, for instance, on 10.3 it can open Postscript documents.

I’ve found this plugin extremely useful for looking at documentation that I may not need permanently, but is only available in PDF format, such as the Unicode code charts.

Despite the fact that it’s free and Shubertit’s Web site also features shareware apps the poor man actually gets paid for, currently his Support page is mostly about PDF Browser Plugin and very informative.

He also has another free plugin for Microsoft Word.

And, err, some other stuff. Go look at that, too.

SafariNoTimeout
http://unsanity.com/haxies
A free Unsanity “haxie” that removes Safari’s 60-second connection timeout limit. Very helpful with Apple’s mailing list search pages, which I’ve talked about most recently in this post.

What!?, you’re saying to yourself. Didn’t I spend all that time telling you how to bypass Apple’s search pages? Don’t I “eat my own dog food”?

Okay, I admit it! I am a filthy hypocrite. I do use my own downloaded archives. Honestly. But– Apple’s site allows you to search for unrelated terms in a way unavailable to the client applications that I use to search the archives. If anybody is aware of an application that allows such OR-style searching, let me know. (And I’m not talking about grep, helpful as that can be.)

Also, if you’re on a new machine without the archives for a particular mailing list handy, it’s much easier to download this than wait 8 hours for all the archives to download, eh? It’s nice to have both techniques available when you need them.

Pacifist
http://charlessoft.com
A $20 shareware utility to open Mac OS X .pkg package files and allow you to extract individual files and folders out of them, as well as many other features.

This one’s a bit more special-purpose than the others, but I’ve found it helpful in the past when I’ve dealt more with OS X installer packages.

Iconographer
http://www.mscape.com/products/iconographer.html
A $15 shareware icon editor, especially useful for OS X-style icons.

I’ve found Icon Composer, the editor included with Apple’s developer tools, wanting in several respects, and Iconographer is in my opinion the best alternative for the money.

Cocoa Links, Part of a Good Breakfast

For a while now, I’ve been meaning to put together some good lists of links for Mac developers.

Here’s my first one, a list of Cocoa sites.

It’s based on a list from Paul Gobble of StudioBox, Inc. Thanks, Paul! I’ve edited the list and expanded it with my own contributions.

I’ll be adding to this in the future, as well as finding a permanent home for it. Feel free to suggest your own additions in the comments or, once they’re turned off, by emailing me. Enjoy!

Big Nerd Ranch, classes, books, and online resources, by Aaron Hillegass and others:
http://bignerdranch.com/

Blackhole Media BSD-Licensed Goodies:
http://blackholemedia.com/code/

Borkware LLC, some projects with source, MacEdition links, etc., by Mark Dalrymple and others:
http://borkware.com/

CamelBones, an Objective-C/Perl bridge framework, by Sherm Pendley:
http://camelbones.sourceforge.net/

Cocoa Dev Central, articles for “Cocoa newbies”:
http://cocoadevcentral.com/

CocoaDev, an OS X developer wiki (community-edited info site):
http://cocoadev.com/

The Cocoa Files, writings by Andrew Stone:
http://stone.com/The_Cocoa_Files/

Cocoa Literature List, by Jeff Biggus:
http://osx.hyperjeff.net/reference/CocoaArticles.php

Deep Cocoa, OpenGL tutorials by Katherine Tatters:
http://zerobyzero.ca/~ktatters/

Karelia Software‘s Cocoa Open Source:
http://cocoa.karelia.com/

MacDevCenter.com, articles, focus on Open Source, by O’Reilly:
http://macdevcenter.com/

Mamasam, a Resource Center for Cocoa Developers
Searchable archive of Apple’s cocoa-dev and OmniGroup’s MacOSX-dev mailing lists, and other resources.
http://cocoa.mamasam.com/

The Omni Group developer mailing lists:
http://omnigroup.com/developer/mailinglists/

Note the Omni Group does the Right Thing with its mailing lists and provides compressed archives for previous years’ messages. Apple? Hello? If you want such a feature yourself for Apple’s lists, see my rather high-bandwidth solution (original link and update).

Ranchero Software Weblog:
http://ranchero.com/

More Ranchero-provided Cocoa samples can be found at http://ranchero.com/cocoa/.

Unofficial cocoa-dev FAQ, by Alastair Houghton:
http://alastairs-place.net/cocoa/faq.txt

Double or Nothing: Pitfalls with NSTableView’s doubleAction

One of the core capabilities of Interface Builder, when used as the resource editor of a Cocoa application, is connecting UI widgets to code with just a bit of click and drag. The code that is connected to consists of outlets and actions.

Outlets are straightforward, just pointers to objects. The basic thing to remember, as with all Interface Builder connections, is that you shouldn’t try to refer to them in your init methods; use awakeFromNib instead.

Actions are methods, more specifically methods referred to by name (in the nib file) or selector (at runtime) instead of (as in C/C++) by pointer. To connect an action in Interface Builder, you control-drag from the UI widget that will send the action message to the object that will receive it, and then you choose the method in that receiver whose message will be sent.

The reason actions are more complicated than outlets is because actions are really part of a target/action pair. Interface Builder needs to remember more than just the message, it needs to remember the target to send that message to.

Until recently, to me at least, the Interface Builder UI seemed to be a little fuzzy on that distinction. In the Info window for an object, under the Connections pane, there was an Outlets column and an Actions column. Under the latest Interface Builder in Panther, what was called Actions is now referred to as “Target/Action”, which states the actual situation more explicitly.

Another thing to keep in mind is that the Cocoa framework has the action/target pair functionality built into Interface Builder solely for itself. You can’t make your own method/object pointer pair variables in a class and set them via the same Interface Builder UI that Cocoa uses for action/target; there’s just no way to tell Interface Builder to treat your variables that way.

With me so far? Good, because it’s about to get a little more complicated.


A while ago, I made a specialized outline view. When you double-clicked some of the cells, instead of getting the usual text editor, for certain columns and under certain circumstances, you would get more specialized behavior, such as a popup menu or a sheet with multi-line text editing capabilities.

The way to get this behavior is (a) turn off the regular editing behavior for the cell by having the delegate for the outline view return NO for the method outlineView:shouldEditTableColumn:item: and (b) send the message setDoubleAction: to the outline view with, as its parameter, the selector to the method you want to use to invoke the custom editor.

Those of you who know how this works already will protest, “Hey, you missed a step!” And indeed I did. But the above steps work under special circumstances, and they work because of how target/action pairs work.

The control-drag that I mentioned for actions at the top of this post fills in two variables in NSControl and its subclasses. It fills in the target variable with a pointer to the object. And it fills in the action variable with the selector to the method. Now, doubleAction is action‘s cousin in NSTableView. It works exactly the same way. But you can’t control-drag to connect it via Interface Builder, you need to set it via code. So, you would probably guess correctly that setDoubleAction: only sets the method, not the target, and you might look around in vain for setDoubleTarget:. Nope, doesn’t exist. Instead, it also uses target!

Why would this work even when you haven’t set target to anything? Well, if the target isn’t set, the Cocoa framework tries to make a guess what the target should be. You can see the order in which it checks here. This order doesn’t mention the delegate for a control, but NSTableView may be a special case since not all controls have delegates. In any case, it’s a good idea to send a setTarget: message explicitly.

There you have it. As with the solution to most pitfalls, esp. in Cocoa, the solution is very simple, but might not be easy to come up with on your own. Enjoy!

List Searching Update: Minor Bugfix and Xcode

I’ve uploaded a minor bugfix to my applelist.pl script, which I first announced on this Nov. 25, 2003 post. Line 154 has been changed from

system "./getlinks.pl $daypage $dayDir ".*.txt$"";

to

system "\"./getlinks.pl\" \"$daypage\" \"$dayDir\" \".*\.txt$\"";

Yes, ladies and gentlemen, I was bitten by the bug that often hits Unix scriptwriters getting used to the “Mac Way”: the annoying habit of real users to put spaces in their directory names. (Real users like me.)

In Unix scripting, spaces are often delimiters between arguments. If you have a script that takes two arguments, the first being a full path and the second being something else, and you pass in a full path not enclosed in quotes as that first argument, the script will parse everything after the first space it finds in that path as the second argument. Ugh.

When you’re working directly in Terminal, using things like tab completion or dragging in file/folders to get their paths, the OS handles this sort of thing for you by escaping out the spaces like this:

My\ HD/My\ Folder/

but you don’t want to do that sort of find and replace in your paths, and quotes are easier, anyway.
Second item of the day: people have been grumbling for a while now on the xcode-users list, which is the replacement list for projectbuilder-users in the same way that Xcode is the replacement for Project Builder (whose corresponding Apple Web site link now points to Xcode – is that a sign, or what?), that it does not yet have an entry in Apple’s list search page.

Now, there isn’t that much content in the list yet, just three months worth, but esp. considering that this is Xcode’s teething period, it’s nice to be able to search for specific problems you’re having or issues you’re facing. So I’m proud to say my applelist.pl script is the only way currently to enable such a search! Yes, yes, I know this state of affairs won’t last, but it’s nice to have bragging rights for a little while, eh?

“I would prefer not to”: Preferences Files Done Badly

I wrote a post I didn’t upload two days ago about how CodeWarrior 6 was working natively in Panther (Mac OS X 10.3) when it hadn’t worked in Jaguar (10.2) and possibly earlier. The appropriate comp.sys.programmer.codewarrior thread can be found here.

CW 6 is probably most useful to developers who still need to do 68K builds. Yes, there are still professional developers who are getting paid to do this in certain cases, whose identities (the cases, not the developers) I will leave as an exercise for the reader.

So I wrote a whole post about the tempestuous history of CodeWarrior 6 on OS X, waxed nostalgic about WWDC 2000, and marveled how something Apple had done in Panther managed to make it work again.

But I was wrong.

A little later I opened CodeWarrior 8, which is a much more respectable version of CodeWarrior to be using in this day and age, and when I tried 6 again for the express reason of verifying the post I’d written, CodeWarrior 6 crashed again just like it had in 10.2.

If I remove the /Metrowerks/ folder that CodeWarrior 8 puts in my OS X ~/Library/Preferences/ folder, then CodeWarrior 6 can be opened again as a native OS X application without crashing.

The problem isn’t something in the OS. The problem is CodeWarrior 6 choking on something in a preferences file it didn’t expect.

So this post is going to be a little different.

I don’t know for a fact exactly why CW 6 is crashing, but it might be related to code like this, which gets a preferences value from a preferences file:

  SInt32 valueInt; // Where we're saving value

  CFTypeRef valueRef = CFPreferencesCopyValue(ValueKey, AppID, (CFStringRef)kCFPreferencesAnyUser, (CFStringRef)kCFPreferencesAnyHost);
  if(value != nil)
  {
    if(CFGetTypeID(valueRef) == CFNumberGetTypeID() && CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &valueInt))
    {
      // Success
    }

    CFRelease(valueRef);
  }

Now, this code does the right thing. (Sorry about the formatting.) It doesn’t assume that the value referred to by ValueKey exists. And (what developers get wrong) it doesn’t assume that if a value is found, that it’s of the assumed type. This is important because the OS X preferences file format is text, XML-based, easy for end users to get into and muck around with. If the type check wasn’t there, and the value were instead, say, a string? The application would most likely crash.

And this is just Carbon. The Cocoa framework takes care of all of this for you, reading preferences XML data into objects. Guess what! Cocoa apps also crash when you muck with the preferences file, just like badly written Carbon apps. There’s nothing you can do about it.

Now, this is not a hugely important detail. Users who muck with preferences files willy-nilly deserve what they get. But I would prefer that my apps get this sort of thing right, and I’d prefer that the apps I use do, too.

Searching Apple’s Mailing Lists

You know about Apple’s mailing lists, right? No? Wellll…okay, I forgive you. In fact, I’m putting helpful links at the bottom of this post, so you can go check them out for yourself (or have an easy way to get back to them, if you are already in the know).

Signing up for these lists today is a good start. But what about searching the past archives to make sure a question you want to ask hasn’t already been asked? (You are always going to do that right? No? This time, I don’t forgive you.)

The trouble is, Apple’s Web-based mailing list search is often excruciatingly slow, and doesn’t work very well anyway. If you want detailed, customizable searches, it seems to me a good technique is to cut out the middleman: download all the archive files yourself, and then search them with your own text editor. BBEdit and CodeWarrior, for example, can do directory searches and grep searches. Or you could use Apple’s content indexing and search from the Finder itself. Now, with circa 56,000 files in the current cocoa-dev archive, for example, this won’t be lightning fast until we have quantum hard drives and G9 processors, but it’s not that much slower than Apple’s site, and my impression is the search will be much more accurate.

So how do you download all these files? I seem to recall, at some point in the past, making the herculean effort to download all the files by hand. This was when the archives were stored as a single file per day. I went to each archive Web page and saved each link to a file. One after another after another. Less than pleasant. Now, the archives appear to be stored as a single file per post, 30+ files for busy days!

A better solution involves an application you already have on your system, and a little help.

If you asked most Unix-heads what to use to download links from a Web page, they’ll mention wget. Mac OS X doesn’t have wget, but it has something similar: curl. To find out about curl, you can go to its official Web site, at http://curl.haxx.se/.

One thing you can find there is helpful sample scripts. The one we want is getlinks.pl, which extracts all the links from an HTML page.

So we’re done? Not quite. We still need to point getlinks.pl at the right Web pages. For that, I (with gratefully accepted help from Dan Shiovitz and Gunther Schmidl) wrote my own Perl script, applelist.pl. It and getlinks.pl (be sure to change the name of that script file, since it downloads as “getlinks.txt”) should be put in the directory where you want the downloaded mailing lists to go, and applelist.pl should be run from there with the lists you want to download as command-line arguments.

For large lists like cocoa-dev and carbon-development, the download process will take 4+ hours on a broadband connection, or at least that was my experience, and will take up circa 250 Mb. of disk space.

Note the scripts have been written to be run repeatedly: they will check for the existence of downloaded files before downloading them again. Have a look to see for yourself how it works.

Some parting thoughts:
– Yes, this solution is not for everyone, esp. people with dial-up Internet connections, little need for repeated mailing list searches, or little patience.
– The script could probably use some improvement and augmentation. Feel free to improve it yourself, since it’s in the public domain.
– One improvement would be to strip out all the email header text when saving the files, and merge the files together into single-day, single-week, or single-month files. This would probably improve search speed quite a bit, at the expense of longer first-time downloading.

Enjoy!

All Apple Mailing Lists:
http://lists.apple.com/mailman/listinfo

carbon-development

Web Recent Threads:
http://lists.apple.com/mhonarc/carbon-development/threads.html
Search:
http://search.lists.apple.com/carbon-development
Text Archives:
http://lists.apple.com/archives/carbon-development/

cocoa-dev

Web Recent Threads:
http://lists.apple.com/mhonarc/cocoa-dev/threads.html
Search:
http://search.lists.apple.com/cocoa-dev
Text Archives:
http://lists.apple.com/archives/cocoa-dev/

Exchange Files Gotcha

Summary: FSpExchangeFiles() will happily exchange two files even if one is already open for writing, which can lead to some bad behavior.

In your cool whiz-bang application, you’re implementing “Save As” with the following steps:

  • Save document data to a temporary file.
  • If file exists at “Save As” location, swap the temporary file with the real file.
  • Delete the temporary file.

This works swimmingly — unless the real file is open for writing in another application. Let’s call that app “BusyBody”.

You’d think, in that case, you’d get an OS error when you attempt to swap files, wouldn’t you? The filesystem should prevent such access when a file’s open…shouldn’t it?

Turns out it doesn’t prevent such access. It will happily swap open-for-writing files all the live-long day. You won’t get an error until you get to the last step. Then, the OS will tell you the temp file is “busy” (fBsyErr), because, as far as it’s concerned, the temp file is open in BusyBody.

So, if your app is handling errors correctly, at that point it will tell the user “Can’t do that, file’s busy.” But the damage has already been done.

When the user tries to “Save As” again to the same file — because she’s an idiot, or she’s a QA tester — the save succeeds. Why? Since BusyBody thinks it’s got that temp file open, the real file is free and clear for writing. This might not be a problem, but if BusyBody attempts to save again, instead of saving to where the user thinks it should save to, it will save to an invisible temp file. Oops!

The easiest solution to this that I can see is that, before you attempt such a switch, try to opening for writing the real file. If there’s an error with that open step, stop and signal the user, before any harm is done. That works as it should!

I wonder how many developers do the “right thing” by using FSpExchangeFiles(), but fail to check for open files first? Hopefully not many.

Note: I have not tried this with FSExchangeObjects(), though I’ll be getting to that. When I know, I’ll update this!