Objective-C++ Overview


Objective-C++ is a set of standalone header files which can be included in your project to extend the syntax of Objective-C®. Started as a means to reference objects in NSArray containers with a more succinct syntax using the [] operator, the integration of the C++ compiler with Objective-C code is so complete it has grown to include dictionary containers and string concatenation along with regular expression operators and a few utility classes used as a shorthand for common operations.


The header files contain a number of wrapper classes defining inline functions for certain operators effectively becoming a pre-processor for code. These header files are typically included in your project's "<Project>-Prefix.pch" precompiled header file so they are available to all sources in the project. No linking to an external library is required.


Converting a project is a two step process. First, convert your project to use the Objective-C++ language for compilation using the “File/Convert Project” menu item and the rebuild correcting any errors that appear due to the slightly stricter C++ based compiler. Then, use “File/Patch Header” to include “Foundation++.h” in your project’s .pch file. See later for a discussion of this process in more detail.


“Live Coding” Xcode Plugin now Included


This release also includes the project to build the “Injection Plugin” used during development with Xcode. This allows you to inject changes to the implementation of an Objective-C class into a running application without having to restart it. A short video showing how to use the plugin is on vimeo:


https://vimeo.com/50137444


To use injection, use the “File/Injection Plugin” menu item in this application to open it’s project in Xcode and then build the “InjectionPlugin” target. Restart Xcode and the new functionality is available in Xcode’s “Product” menu. No further installation or changes to your project are required. Once you have run the program, use “Product/Inject Source” (control-=) and any code changes in your currently selected “.m” file should be applied to your program.


The primary repository for the source is on github:

 https://github.com/johnno1962/injectionforxcode


Please consult the release notes at the end of this document for changes in the 5.0 release. The main difference is the header include path in the application’s resources has changed from /Applications/Objective-C++.app/Contents/IncludeV4.6 to /Applications/Objective-C++.app/Contents/Resources/IncludeV5.0.The headers for all previous releases are included in the application package for compatibility but at the new path. A number of other changes are also outlined below.


Core Classes


Container classes are C++ templates so the NSArray replacement, OOArray is used as follows:


    OOArray<NSString *> array;

    array[0] = @"hello";

    array += @"world";


As a policy, references off the end of an array print a warning and optional stack-trace rather than throw an exception to keep programs running. Assignments past the last member of an array pad with kCFNull.


The NSDictionary counterpart OODictionary also makes available the subscript operator:


    OODictionary<NSString *> dict;

    dict[@"key1"] = @"value1";

    dict[@"key2"] = @"value2";


The underlying NSMutableArray and NSMutableDictionary instances are created lazily when the variable is first assigned to and deallocated when the variable goes out of scope. These classes can also be used as ivars or properties with attribute "assign" as the C++ constructors and destructors are called as Objective-C instances are allocated and deallocated.


The final core class wrapper is the OOString class which can be used as you might expect:


    OOString string = @"hello";

    string = string + @" ";

    string += @"world";


    assert( string == @"hello world" );


As these variables cast to their underlying objects they can be messaged in a type-safe manner in the normal way:


    OOArray<OOString> array2 = [dict allKeys];

    int count = [array2 count];

    int length = [array[0] length];


While some of these features will be available in upcoming iOS and clang compiler releases, Objective-C++ requires no new support from the system runtime. Code using it can be deployed to iOS versions back through 3.0 and OS X 10.6. 


Example project


The best way to get up and running is by looking at a simple iPhone/iPad example project "RateNews" which can be downloaded by clicking here. The  application allows you to rate news sites by their average word length and persists the information in a small sqlite db generated from "data classes". The code using Objective-C++ is mostly in the method webViewDidFinishLoad: in the file RNMainViewController.m.


Converting a Project to Objective-C++


Conversion is a two step process, most easily performed using the "File/Convert Project" or the "File/Patch Header" menu items provided in this application. The first step is to change your project to use Apple's Objective-C++ compiler for all Objective-C class sources using "File/Convert Project" on your project's ".xcodeproj" file. This is generally a reasonably painless procedure though conversion will likely throw up a few errors as the Objective-C++ compiler is more strict than the default compiler. For example, in C++ a (void *) can no longer be assigned to a more restrictively typed pointer (e.g. char *) without an explicit cast.


The second, slightly more involved problem, is that external references to functions that have been compiled by the Objective-C or vanilla C compiler must be surrounded by extern "C" { .. }. 


extern "C" {

    extern char *myFunction( int arg1 );

    #import "zlib.h"

}


This is because function symbol names are "mangled" to include argument types in C++ as they can be "overloaded". If you do not use extern "C" you will encounter a series of undefined references at linkage time involving obscure symbol names only vaguely related to your original function name. Modify the appropriate header or its #import statement as above to resolve these problems.


Once your project compiles using the Objective-C++ compiler the second step is to include the "objcpp.h" header from this application bundle. Open the project's "<Project>-Prefix.pch" file with the "File/Patch Header" menu item and it will insert the required #import. Build your project again and you can start using the OOString, OOArray and OODictionary classes in any of your class source files.


Occasionally, you may find Xcode's syntax highlighter gets confused showing errors against the source even though the project compiles and runs. If you encounter this problem, clean the project (cmd-shift-k) then build to force the pre-compilation header to be rebuilt.


Operator Overload


The great temptation when starting to use operator overloading is to get "carried away" and express all sorts of operations as operators. Objective-C++ is probably an example of this if you use all of the operators available. If you confine yourself however to the more intuitive basic operators such as [], += etc. the readability of your code need not suffer.


There are however three, key, less intuitive operators you need to be aware of as you will likely use them frequently. Operators !, * and ~ which are quite straightforward.


operator ! is a quick way to know if a wrapper contains an instance, for example:


    if ( !string )

        string = @"default";


    if ( !dict[@"key"] )

        dict[@"key"] = @"default";


operator * recovers the underlying pointer to the wrapper instance by "dereferencing" it. This is most often used with NSLog() which will give an error in C++ if you to pass the "POD" wrapper variable directly.


    NSLog( @"Value of string: %@", *string );


Dereferencing to get the contained class is also useful for container references which are implemented as an instance of the OOSubscript class (so they can be assigned to.)


    OOArray<UIView *> subviews = [view subviews];

    ((UIView *)[subviews objectAtIndex:0]).backgroundColor; // old way

    (*subviews[0]).backgroundColor; // new type-safe equivalent


This is important when the contained class is itself a wrapper and can need to be dereferenced twice.


    OODictionary<OOString> dict2 = dict;

    NSLog( @"Value in dict: %@", **dict2[@"key1"] );

    int length2 = [*dict2[@"key2"] length];


Finally, operator ~ has been pressed into service as a form of destructor analogous to the ~class() member functions in C++. It deallocs the wrapped instance and also works for array and dictionary references returning the previous value.


    ~string; // empties string variable

    ~array[-1]; // removes and returns last element

    OOString val = ~dict[@"key1"]; // deletes value for key


Mutability and Copying


Objective-C++ classes do not take a copy of their values on assignment or initialisation. Instead, they retain them similar to a normal "strong" instance pointer variable and assume the retained object is mutable unless the variable is declared const. To prevent mutation of passed values, use the "const" modifier instead as below (the & modifier passes by reference in C++.) The following would result in an error at compile time as the += operator method has not been flagged as being "const".


- (void)method:(const OOString &)string {

    string += @"!"; // gives a compiler error

}


As this is the most common way of passing arguments a #define has been created for this idiom:


- (void)method:(cOOString)string {

    string += @"!"; // still gives a compiler error

}


If you need to have a mutable copy, use the "mutableCopy" method. As it returns a retain count of 1 you will need to release the instance (if you are not using ARC) as the wrapper will also retain it.


    OOString string2 = [string mutableCopy];

    [string2 release];


There is of course, "an operator for that" which takes care of this:


    string2 <<= string;


Since version 5.0 this operator will perform a deep copy provided the operand is a “plist”:


More Esoteric Operators


At risk of obfuscating your code you might consider using some of the more obscure features of Objective-C++. For example, subscripting operators are recursive and can mix dictionary and array references for dynamic data structures as you might in a scripting language:


    // intermediate nodes are created automatically

    dict[@"key3"][@"key4"][@"key5"] = @"value";

    dict[@"key6"][5] = @"sparse arrays are fine";


Subscripts into arrays can also be ranges and can be assigned to:


    array[NSMakeRange(5,5)] = @[@"v1", @"v2"];

    array[OORange(5,10)] = @[@"v1", @"v2"]; // equivalent


The difference between an NSRange and an OORange() is that OORange is made up of the start and end positions rather than the start and length. Indexes can be negative which are counted back from the end of the array (or string.)


Strings can be indexed-into using integers and ranges and the results assigned to:


    if ( string[0] == '_' )

        string[OORange(0,1)] = @"";


The meaning of indexing into a string using a string has been used as a means to introduce regular expressions into the language extension. Again, they can be assigned to and groups are substituted as you might expect.


    OOStringArray words = string[@"\\w+"];

    string[@"hello (\\w+)"] = @"Hi $1";

The regular expression package if you are deploying to OS X 10.6 or iOS 4 was the POSIX C library that you will find documented by typing "man regex" into a Terminal window with character classes \w, \d and \s added. Otherwise it will now uses the more modern NSRegularExpression class. See the OOStringSearch class which implements subscripting by string on an OOString for details.


Two last operators on OOString deserve mention for their utility. First, subtracting from a string operates as a regular expression, removing all occurrences of the pattern from the string:


    OOString trimmed = string - @"^\\s+|\\s+$";

    // which is equivalent to the epic:

    [string stringByTrimmingCharactersInSet:

     [NSCharacterSet whitespaceAndNewlineCharacterSet]];


Finally, the operator "/" can be used on a string to split it and perhaps perversely on an array to re-combine it into a string:


    OOStringArray words = string / @" ";

    OOString commaSeparated = words / @", ";

    OOStringArray split = commaSeparated / OOPattern( @",?\\s+" );


Other Shorthand Classes


Thats probably more than enough about operators. Objective-C++ also contains a number of shorthand classes which make extensive use of casting for common objects: OOURL, OOFile, OOInfo and OODefaults which help cut down the verbiage in code:


    OOString contents = OOURL( @"http://www.google.com" );

    OOFile( OOHome()+@"/google.html" ) = contents;

    

    OOInfo appInfo;

    NSLog( @"App name %@", **appInfo[kCFBundleNameKey] );

    

    OODefaults defaults;

    defaults[@"string"] = @"value";

    defaults[@"number"] = 12345;

    defaults.sync();


This is the end of this quick overview of the Objective-C++ classes. For more information I have to refer you to the source code which can be viewed in the "File/Reveal Sources" menu item or the generated documentation available online here as I document it. If you encounter any bugs or have any suggestions on how to improve Objective-C++ please contact the authors at this address.


Other Sources


There are three other headers in the framework which you may find of interest. "objvec.h" which contains reference counted instances dynamic arrays of various types. "objsql.h" generates a sqlite schema based on the ivars of a set of "data classes" and natural joins by ivar name. Using objsql.h, requires that you add it associated implementation "objsql.mm" to your project as well. For more details, see the RateNews example project. Finally, "objxml.h" allows you to access parsed xml in a DOM or xpath-like manner using the subscript operator (as an OONode is just a subclass of OODictionary<OOString>.)


    OOData xml = OOString( @"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"

                          "<a>"

                          "  <b attr=\"88\">data1</b>"

                          "  <b attr=\"99\">data2</b>"

                          "</a>" );

    OONode node = xml;

    NSLog( @"value: %@, attribute value: %@",

          **node[@"a"][@"b"], **node[@"a/b/1/@attr"] );


value: data1, attribute value: 99

 

Incompatible changes with release 5.0 of Objective-C++


Very, minor bug fixes with this release apart from the inclusion of the “Injection Plugin”. Small changes to OOFile() class which can now return a relative “path()” string if you chdir() and the <<= operator now makes a deep copy using CFPropertyListCreateDeepCopy(). One final change was to make the numeric value of a string pattern search simply whether or not the pattern matches rather than the location of the patten in the string. This means the cumbersome:


    if ( string[@"pattern"] != NSNotFound )

        ; // do something if pattern not “not found”


or even 


    if ( !!string[@"pattern"] ) // “doesn't not match”


can now just be expressed in if statements as just:


    if ( string[@"pattern"] )

        ; // do something if pattern matches


You’ll see a warning if you have code comparing the results of the now BOOL value to NSNotFound.


Incompatible changes with release 4.6 of Objective-C++


4.6 is a maintenance release but there have been still more changes to the regular expression operator (subscripting a string with a string.) The major change is that regular expressions are now implemented with the NSRegularExpression class. Also, assigning the result of a regular expression to an array captures all groups now not just outer groups so use a non-capturing (?:) group if you do no want to capture that group and have it assigned to the array. Other than this operation should be the same except the regular expressions available using NSRegularExpression are more complete.


One final change is that assignment to a regular expression from a block now uses a cOOStringArray rather than an OOStringArray. Unrelated changes include operators and casts to interchange to/from C++ STL containers if <string> has been imported. 


Incompatible changes with release 4.5 of Objective-C++


The changes with the 4.5 release are minimal and backward compatible where possible. A couple of non-backward compatible changes where made however notably the following:


Nil references can be assigned to a hash or array as before and are store as kCFNull instances as a place holder. On access, this kCFNull value is no longer returned and "nil" substituted in it's place.


    dict[@"value3"] = (id)nil;

    assert( *dict[@"value3"] == nil );


Regular expressions now return a string for each of the "outer groups" for each regular expression match rather than a string for each match:


    OOString data = @"name1=value1\nname2=value2\n";

    OOStringVars( name, value ) = data[@"([^=]+)=([^\n]*)\n"];

    assert( name == @"name1" );

    assert( value == @"value1" );


As an array can be assigned to a dictionary this can be used to parse properties files as the pattern will be matched repeatedly:


    OOStringDictionary hash = data[@"([^=]+)=([^\n]*)\n"];

    assert( hash[@"name2"] == @"value2" );


Replacements can now be performed using a block (groups are numbered normally):


    OOString a = @"block test";

    a[@"(\\w)(\\w*)"] = ^( cOOStringArray groups ) {

        return +*groups[1]+groups[2];

    };

    assert( a == @"Block Test" );


Two new classes where added, the first "OOTask" is a simplified interface to running child processes.


    OOTask task;

    FILE *listing = task.exec( @[@"ls", @"-l"] );

    char buffer[1024];

    while( fgets(buffer, sizeof buffer, listing) )

        NSLog( @"%s", buffer );

    task.wait();


Finally, for iOS 5.0+ and OS X 10.7+ the "OOJson" class is available to parse Json format data:


        OOJson a = OOURL( @"http://zxapi.sinaapp.com/" ).data();

        assert( a[@"girlFriends"][0]["friend"][@"girlName"] == @"I don't know" );


OS X, iOS and Objective-C are registered trade marks of Apple Inc.