Click here to Skip to main content
15,881,281 members
Articles / Mobile Apps / iPhone

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

Rate me:
Please Sign up or sign in to vote.
4.35/5 (14 votes)
26 Mar 2014CPOL9 min read 29.6K   19   2
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, we need to use C++ code from Objective-C one and call C functions from Objective-C, for example, 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. Error when Assigning Blocks that Return C++-objects

Description

Let's say we have some simple Objective-C class hierarchy.

Objective-C
@interface Vegetable : NSObject 
@end 
@interface Carrot : Vegetable
@end 

We can then write something like this:

Objective-C
Vegetable *(^getVegetable)();
Carrot *(^getCarrot)();
getVegetable = getCarrot;  

This means that in respect of Objective-C classes, the blocks are covariant in the return value: a block that returns something more specific, can be considered as a "subclass" of the block that returns something more general. Here the block getCarrot, which returns a Carrot, can be safely assigned to the block getVegetable. Indeed, it is always safe to take Carrot *, when you expect Vegetable *. And, it is logical that the reverse does not work: getCarrot = getVegetable; does not compile, because when we really want a carrot, we do not really like to get a vegetable.

You can also write this:

Objective-C
void (^eatVegetable)(Vegetable *);
void (^eatCarrot)(Carrot *);
eatCarrot = eatVegetable; 

This shows that blocks are contravariant in arguments: a block which can handle more general argument type may be used where we need a block that handles a more specific argument. If the block knows how to eat a vegetable, it will know how to eat a carrot too. Again, the converse is not true and will not compile: eatVegetable = eatCarrot;

It's all so in Objective-C. Now try to do something similar in C++ and Objective-C++.

Objective-C
class VegetableCpp {};
class CarrotCpp : public VegetableCpp {};  

And then the following lines of code will not compile:

Objective-C
VegetableCpp *(^getVegetableCpp)();
CarrotCpp *(^getCarrotCpp)();
getVegetableCpp = getCarrotCpp; // error!
 
void (^eatVegetableCpp)(VegetableCpp *);
void (^eatCarrotCpp)(CarrotCpp *);
eatCarrotCpp = eatVegetableCpp; // error! 

Clang produces the following errors:

«Main.mm: 28:17: Assigning to 'VegetableCpp * (^) ()' from incompatible type 'CarrotCpp * (^) ()'

main.mm: 32:17: Assigning to 'void (^) (CarrotCpp *)' from incompatible type 'void (^) (VegetableCpp *)' »

GCC produces the similar errors:

«Main.mm: 28: Cannot convert 'CarrotCpp * (^) ()' to 'VegetableCpp * (^) ()' in assignment

main.mm: 32: Cannot convert 'void (^) (VegetableCpp *)' to 'void (^) (CarrotCpp *)' in assignment

main.mm: 28:17: Assigning to 'VegetableCpp * (^) ()' from incompatible type 'CarrotCpp * (^) ()'

main.mm: 32:17: Assigning to 'void (^) (CarrotCpp *)' from incompatible type 'void (^) (VegetableCpp *)' » 

Solution

This problem has no solution, and it is not expected to appear in the future. A bug on this topic on the compiler Clang was submitted here though. As I understood, this will not be implemented. This distinction is intentional, the reason is the difference between object models in Objective-C and C++. More specifically: having a pointer to the Objective-C object, one can convert it to a pointer to the object of the base class or a derived class without actually changing the value of the pointer: the address of the object remains the same.

Since C++ allows multiple and virtual inheritance, all this is not for C++ objects: if I have a pointer to a C++ class, and I convert it to a pointer to the object of the base class or a derived class, I may need to adjust the value of the pointer.

Consider this example:

C++
class A { int _a; }
class B { int _b; } 
class C : public A, public B { }
 
B *getC() { 
  C *cObj = new C; 
  return cObj;
}  

For example, a new object of class C in the method getC() has been allocated at the address 0x10. The value of the pointer cObj is 0x10. In the return statement, that pointer to <code>C should be adjusted so that it points to the part B within C. Since B follows A in the inheritance list of class C, then (usually) in memory B will follow A, which means adding a 4-byte offset (== sizeof (A)) to the pointer, so the returned pointer is 0x14. Similarly, the conversion of B* to C* would lead to the subtraction of 4 bytes from the pointer. When we are dealing with virtual base classes, the idea is the same, but the offsets are unknown.

Now, consider what effect all this has on such an assignment:

C++
C* (^getC)(); 
B* (^getB)(); 
getB = getC;  

Block getC returns a pointer to C. To turn it into a block that returns a pointer to B, we would have to adjust the pointer returned in each block call by adding 4 bytes. This is not the adjustment for the block, it is the adjustment of the pointer value, that the block returns. It would be possible to implement this by creating a new block that wraps the previous block and makes corrections, for example:

C++
getB = B* (^thunk)() { return getC(); }  

This is achievable for the compiler, which already provides a similar "trick" when you override the virtual function which returns the covariant type and needs adjustment. However, in the case of blocks it causes additional problems: the equality comparison operation == is allowed for blocks, therefore, to determine, "getB == getC", we should be able to look through this wrapper, which would have been generated by assigning "getB = getC" to compare the underlying pointers returned by the blocks. Again, this is achievable, but would require much more heavy runtime for the blocks that is able to create unique wrappers that can perform these adjustments of the return value (the same as for any contravariant parameters). While all of this is technically possible, the cost (in the size of runtime, complexity and execution time) outweighs the gains.

Going back to the Objective-C, I note that the model of single inheritance will never require any adjustments to the pointer to the object: there is only one address in the Objective-C object, and the type of static pointer is not important, so the covariance/contravariance will never require any wraps, and a block assignment is just a pointer assignment (+ _Block_copy / _Block_release when using ARC).

Problem 2. Errors when Passing an Objective-C Object to a C Function in ARC Mode

Description

Assume that we want to pass some Objective-C object to the C function as a parameter of type void*, for example, into pthread_create.

Objective-C
pthread_t thread;
NSObject *obj = [[NSObject alloc]init];
pthread_create(&thread, NULL, startThread, obj); 

When ARC is on, we will get a compilation error:

«main.m:36:48: Implicit conversion of Objective-C pointer type 'NSObject *' to C pointer type 'void *' requires a bridged cast»

ARC will not allow so easily to make such a conversion.

Solution

You need to use the bridged cast to move between the two memory models. The simplest form of such cast is (the XCode itself offers it, if you hover over the error in the code):

Objective-C
pthread_create(&thread, NULL, startThread, (__bridge  void*)obj); 

This is an equivalent to the first code, but now, as we are in ARC mode, there is an additional caution. The point is that the ARC will consider that can free obj, if it is the last reference to it. Unfortunately, pthread_create does not know anything about the objects, so will not retain the argument. By the time the thread is started, the object can be removed already. The right solution is the following:

Objective-C
pthread_create(&thread, NULL, startThread, (__bridge_retained  void*)obj); 

This will call objc_retain() before passing the argument.

On the other hand, the thread function should look something like this:

Objective-C
void* startThread(void *arg)
{
    id anObject = (__bridge_transfer id)arg;
    arg = NULL;
    //
}  

This will put the object back under the control of ARC. This form of bridge cast expects that the object was already retained, and thus will free it (call release) at the end of the block. The line arg = NULL is optional, but it is a good style. You have placed a reference that the arg contained, under the control of ARC, so reset of the pointer is obvious.

NOTE

Here is what the documentation for Automatic Reference Counting of Clang says:

«A bridged cast is a C-style cast annotated with one of three keywords:

  • (__bridge T) op casts the operand to the destination type T. If T is a retainable object pointer type, then op must have a non-retainable pointer type. If T is a non-retainable pointer type, then op must have a retainable object pointer type. Otherwise the cast is ill-formed. There is no transfer of ownership, and ARC inserts no retain operations.
  • (__bridge_retained T) op casts the operand, which must have retainable object pointer type, to the destination type, which must be a non-retainable pointer type. ARC retains the value, subject to the usual optimizations on local values, and the recipient is responsible for balancing that +1.
  • (__bridge_transfer T) op casts the operand, which must have non-retainable pointer type, to the destination type, which must be a retainable object pointer type. ARC will release the value at the end of the enclosing full-expression, subject to the usual optimizations on local values.

These casts are required in order to transfer objects in and out of ARC control; see the rationale in the section on conversion of retainable object pointers.

Using a __bridge_retained or __bridge_transfer cast purely to convince ARC to emit an unbalanced retain or release, respectively, is poor form.»

Valid only for Clang.

Problem 3. How to Call the Inline C-function from Objective-C Code

Description

Let's say we have the files Foo.h and Foo.c.

File Foo.h
C++
inline void foo(); 

File Foo.c

C++
inline void foo()
{
    printf("Hello, World\n");
} 

A main.m file is as follows:

Objective-C
#import <Foundation/Foundation.h>
#include "Foo.h"
 
int main(int argc, const char * argv[])
{
 
    foo();
 
    // insert code here...
    NSLog(@"Hello, World!");
 
    return 0;
} 

Then we will get a linker error which states that the symbol foo is not found.

This problem has different solutions. C99 offers this approach: it requires from the programmer to have the module that contains a callable copy of the extern inline function. By default, in C99 inline functions without any storage specifier are considered extern. If all the declarations of inline function in a module do not have a storage specifier, then the function is considered as extern inline, and the body will not be generated for it. On the other hand, if one of the declarations for inline function in the module explicitly contains a keyword extern, then exactly this module will generate a callable copy for the function. This leads to the following organization of the source files.

Solution

Insert the definition of extern inline function in the header file without using the keyword extern. This header can be included in as many modules as you want to. In only one single module, you should include this header and declare the function prototype using extern, to get the callable copies of the function (in this module it is not necessarily to repeat the keyword inline).

The example will change as follows:

File Foo.h

C++
inline void foo()
{
    printf("Hello, World\n");
}   

File Foo.c

C++
extern inline void foo();  

The file main.m remains the same.

And the problem is fixed. There is another solution - you can switch the C dialect option in the settings of the environment to GNU 89 without modifying the source files. True for both compilers. With the latest clang (XCode 5.0) this solution does not work. You need to place static before the function. I have submitted a bug for this here, but I could not get any information on this issue except that we need to place static.

Conclusion

It is clear that the interoperation of the 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.

History

  • 05.12.2013: Initial version

License

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


Written By
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 4 Pin
KarstenK19-Dec-16 2:00
mveKarstenK19-Dec-16 2:00 
QuestionVery good article Pin
Volynsky Alex5-Dec-13 11:18
professionalVolynsky Alex5-Dec-13 11:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.