Introduction
This article overviews several complicated problems we can face with interoperation between C, Objective-C, C++ and Objective-C++ code. All the code samples were tested on both compilers from XCode 4.6.2 (Apple LLVM 4.2 (Clang) and LLVM GCC 4.2) and also on the latest XCode 5.0. Since the XCode 5.0 does not contain the GCC compiler anymore, so the examples were checked only on its Apple LLVM 5.0 (Clang). If the results are different for the latest Clang, I specify it explicitly.
Background
In many cases, for example, we need to use C++ code from an Objective-C one and call C functions from Objective-C, so the article is about it. You also should know what an Objective-C++ is. It is a method for mixing Objective-C and C++ code in the same file and for their interoperation. You can definitely use it in XCode - name your files with the .mm extension.
Problem 1. Including templates
MAX/MIN in Objective-C++/C++ code
Description
Imagine that
you have this code in a file CppTemplate.h:
template<class T>
inline const T &MAX(const T &a, const T &b)
{ return b > a ? (b) : (a); }
File ObjCppClass.h:
#import <Foundation/Foundation.h>
@interface ObjCppClass : NSObject
@end
File
ObjCppClass.mm:
#import "ObjCppClass.h"
#include "CppTemplate.h"
@implementation ObjCppClass
@end
When trying
to build you get the compilation errors like these (Clang):
«CppTemplate.h:17:17: Expected unqualified-id
CppTemplate.h:17:17: Expected ')'»
And, if you
include the CppTemplate.h in the .cpp file, then no error will be.
Solution
This problem
arises from the fact that there are macros called MAX/MIN in Objective-C, which
look like this (from a file NSObjCRuntime.h):
#define MAX(x,y) ((x) > (y) ? (x) : (y))
So the
template instantiation evaluates into the following:
template<class T>
inline const T& ((const T& a) > (const T& b) ? (const T&a) : (constT&b)) (const T& a, const T& b) { ... }
that is
completely meaningless and uncompilable.
To fix this error, either give the template another
name, or use std::max
.Problem 2. ARC forbids objects of
Objective-C types in structures
Description
Below is the
structure, which contains two objects of Objective-C class NSString:
typedef struct SStrings
{
NSString* firstName;
NSString* secondName;
} SStrings;
In the ARC
project (Automatic Reference Counting) you will get a compilation error «main.m:
15:15: ARC forbids Objective-C objects in structs or unions», even if you set
the flag -fno-objc-arc in the project settings for this specific file.
Solution
First of all
I should briefly explain what is ARC (Automatic Reference Counting). This is a
mechanism, freeing the developer from a manual memory management. That is, the
compiler inserts into the code calls to retain/release/autorelease
, not you.
The
mechanism of ARC occupies an intermediate position between a garbage collector
and a manual memory management. As the garbage collector, ARC frees the
developer from having to write the calls retain/release/autorelease
. However,
unlike the garbage collector, ARC does not recognize strong circular references
(retain
). Two
objects with strong reference to each other will never be disposed by ARC, even
if no one else refers to them. The developer still needs to avoid or destroy
the strong circular references to the object. ARC is supported only by Clang.
And now
about the subject matter. To avoid the error, you need to put the attribute __unsafe_unretained
before
declaring NSString
objects in
the structure:
typedef struct SStrings{
__unsafe_unretained NSString* firstName;
__unsafe_unretained NSString* secondName;
} SStrings;
Next, I
should explain what is __unsafe_unretained
.
By default,
all objects in ARC are of type __strong
. This means that when you assign an object to a
variable its reference count is incremented, and the object will be retained
for as long as there is a reference to it. This opens up opportunities for
circular references. For example, it occurs when an object contains another
object as a class variable, but the second object also refers with a strong
reference to the first, as a delegate, so both objects will never be released.
The
qualifiers __unsafe_unretained
and __weak
are exactly for these purposes. They are most often
used for the delegates. This means that the delegate instance will still point
to the first object, but for this object reference count will be not
incremented, thereby breaking the circular reference and allowing both objects
to release.
Both
modifiers prevent retain of objects, but a little differently. In the case of __weak
variable
will be assigned a nil
after the removal of the object, which is a very safe
behavior. As its name suggests, the variable with the qualifier __unsafe_unretained
will
continue to point to the memory where the object was even after its removal.
This can lead to a crash while accessing to the deallocated object.
Why, then,
you might want to use __unsafe_unretained
? Unfortunately, __weak
is only
supported with iOS 5.0 and Lion as a platform. If you want to run the
application on iOS 4.0 and Snow Leopard, you must use the qualifier __unsafe_unretained
.
Now suppose
that you could write such code using ARC:
typedef struct {
__strong NSObject *obj;
int ivar;
} SampleStruct;
Then you
could write code like this:
SampleStruct *thing = malloc(sizeof(SampleStruct));
The problem
is that malloc
does not
zero the returning memory. Therefore the thing->obj
is a random
value, not necessarily NULL
. Then you assign it a value like this:
thing->obj = [[NSObject alloc] init];
In fact, ARC
will transform this code into something like this:
NSObject *temporary = [[NSObject alloc] init];
[thing->obj release];
thing->obj = temporary;
The problem
here is that your program has just sent release
to some
random value. The application is likely to crash at this point.
You could
say that the ARC should recognize the call of malloc
and take
care to set obj
to nil
to prevent
this. The point is that malloc can be wrapped into some other
function:
void *customAllocate(size_t size) {
void *p = malloc(size);
if (!p) {
clearCaches();
p = malloc(size);
}
return p;
}
OK, now the
ARC is to know about your function customAllocate
too, and it
can be in some static library that you received in binary form.
Your
application can also use custom memory allocators that reuse the old
allocations without using of free
and malloc
. Therefore, even a change of malloc
so that it zeroes the allocated memory, will not work. ARC should be aware of
all the special allocators in your program.
It would be
very hard to make this work reliably. So instead, the creators of ARC just gave
up and have forbidden __strong
in structures.
That's why
you can put in the structures only the objects with the qualifier __unsafe_unretained
to say the
ARC «Do not try to control the ownership of the object to which this variable
references."
Valid only
for Clang, since GCC does not support ARC at all.
Problem 3. Code with blocks compiles
successfully in Objective-C, but does not compile in Objective-C++
Description
First, a few
words about the "blocks". The word "block" is rather
ambiguous, so I just say that I do not mean a group of operators, combined in
one unit by braces that existed in C. I am talking about proposed by Apple
addition to the language Objective-C, which makes it possible to use anonymous
functions (or lambdas).
So, a
typical simple block looks like this:
void (^block)() = ^{ printf("Hello world\n"); }
It simply
prints the string.
Sign of the
carriage (^) before the braces distinguishes our statement from the classic
block of operators. Having defined the block, you can call it as follows:
block ();
On the
console you see the printed line «Hello world».
Now let’s
get back to our subject. Imagine you have this code in Objective-C:
@interface TestClass1 : NSObject
- (void)test;
@end
@implementation TestClass1
- (void)test
{
void (^d_block)(void) =
^{
int n;
};
}
@end
Just about
the same in C++:
class TestClass2
{
public:
void TestIt();
};
void TestClass2::TestIt()
{
void (^d_block)(void) =
^{
int n;
};
}
And the same
in Objective-C++:
class TestClass3
{
public:
void TestIt();
};
void TestClass3::TestIt()
{
void (^d_block)(void) =
^{
int n;
};
}
Clang
compiles all 3 code snippets, but produces warnings «Unused variable 'n'» 3
times respectively. In C++ version Clang should generate an error message
because block is an Objective-C feature, but it does not. It looks like a bug
but it is rather a feature. Since the blocks are considered as a useful tool,
they have been made recognizable in C++-code.
GCC
complains on both Objective-C++, and C++ versions just about the same:
«TestClass2.cpp:15: 'int TestClass2::n' is not a static member of 'class TestClass2'»
«TestClass3.mm:15: 'int TestClass3::n' is not a static member of 'class TestClass3'».
In C++ code
GCC had to say that it is not aware of the blocks too. It seems also a feature.
Solution
These are
bugs in GCC. On this subject there is a bug: http://lists.apple.com/archives/xcode-users/2011/Mar/msg00232.html. You can work around making the variable static or switching
to Clang:
class TestClass3
{
static int n;
public:
void TestIt();
};
void TestClass3::TestIt()
{
void (^d_block)(void) =
^{
};
}
Problem 4. Inability to assign a
block to a C++11 lambda
Description
Clang
supports C++11. This causes some interesting points.
You can
assign a lambda to a block.
void (^block)() = []() -> void {
NSLog(@"Inside Lambda called as block 1!");
};
block();
You can
assign a block to std::function
.
std::function<void(void)> func = ^{
NSLog(@"Block inside std::function");
};
func();
You cannot
assign a block to a lambda though.
auto lambda = []() -> void {
NSLog(@"Lambda!");
};
lambda = ^{ NSLog(@"Block!");
};
lambda();
Then you get
a compilation error «main.mm: 40:12: No viable overloaded '='».
Solution
This problem
has no solution. You cannot assign to a lambda another lambda (even with the
same structure).
auto lambda1 = []() { return 1; };
auto lambda2 = []() { return 1; };
lambda1 = lambda2;
Compile-time
error «main.mm: 67:13: No viable overloaded '='» occurs.
Lambda
cannot be even assigned to itself.
auto lambda = []() -> void { printf("Lambda 1!\n"); };
lambda = lambda;
The code
also does not compile with the error «main.mm: 63:12: Overload resolution
selected implicitly-deleted copy assignment operator».
Each lambda
has its own implementation-defined type.
It is all
the same for Clang from XCode 5, but error messages are a little bit different.
Visual
Studio 2010 compiles the assignment of a lambda to itself, but IntelliSense
produces a strange error message:
«IntelliSense: function" lambda [] void () ->void :: operator = (const lambda [] void () -> void &) "(declared at line 9) cannot be referenced - it is a deleted function».
The reason
is Visual Studio 2010 does not support deleted methods.
A small
offtopic concerning the deleted methods. They manage the default behavior.
Now the
standard idiom "noncopyable" can be explicitly expressed as follows:
class X {
X& operator=(const X&) = delete;
X(const X&) = delete;
};
And vice
versa, you can explicitly say that you want to use the default behavior for
copying:
class Y {
Y& operator=(const Y&) = default;
Y(const Y&) = default;
};
Conclusion
It is clear that an interoperation of a code in different languages is not so simple, but unfortunately sometimes necessary. I would like to think that my article will be interesting, useful and will help many people avoid making similar mistakes.