Click here to Skip to main content
Click here to Skip to main content

Interoperation Issues in Mixed C/C++/Objective-C Development, Part 2

, 25 Mar 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
An article about interoperation issues in mixed C/C++/Objective-C development

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) { 
        // malloc failed.  Try to free up some memory. 
        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 = ^{ // error!
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; //Error  

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 { 
    // ... 
    // Noncopyable 
    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 { 
    // ... 
    // Default copy semantics 
    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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Anna Koneva

Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411019.1 | Last Updated 25 Mar 2014
Article Copyright 2014 by Anna Koneva
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid