Click here to Skip to main content
15,860,943 members
Articles / Mobile Apps / iOS

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

Rate me:
Please Sign up or sign in to vote.
4.63/5 (7 votes)
12 May 2014CPOL6 min read 28.2K   11   1
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. Error when using some C++-objects in the block

Description

Below is the code, where you try to use a variable of type std::ifstream declared outside the block inside the block.

Objective-C
__block std::ifstream file("/tmp/foo") ;
// With and without __block
void (^block)() = ^{  
    file.rdbuf();
    file.close();
    file.open("/tmp/foo");
};
block(); 

If you declare the variable with the qualifier __block, you’ll get the following error message (Apple LLVM 4.2(Clang)):

«main.mm:28:27: Call to implicitly-deleted copy constructor of 'std::ifstream' (aka
'basic_ifstream<char>'

On the latest clang (XCode5.0) you'll not get any error message. I also have submitted a bug about it http://llvm.org/bugs/show_bug.cgi?id=17597. The copy constructor for std::ifstream is not deleted in the latest version.

If without this modifier, then the other:

«main.mm:31:9: Call to implicitly-deleted copy constructor of 'const std::ifstream' (aka
'constbasic_ifstream<char>') 
main.mm:32:9: Member function 'close' not viable: 'this' argument has type 'const
std::ifstream' (aka 'constbasic_ifstream<char>'), but function is not
marked const»

GCC will produce the similar error messages. However if you use the __block in GCC an internal compiler error occurs at first

«Internal compiler error: Segmentation fault: 11»  

Solution

When you deal with local variables of common Objective-C types, by default they are passed by value into the block and cannot be changed inside it. If you try to modify a variable inside a block you will get an error. If you put in front of the variable __block, then it will be passed by reference and you can change its value within a block. __block affects the C++ objects in a different way.

If you use the __block with C++-objects, the compiler tries to call the copy constructor for this object, which is deleted (=delete) in the class std::ifstream. So from this goes the first error.

If you do not use __block, the const copy constructor should be called, which is also deleted for this class (hence the second error). The third error is caused by the fact that you are trying to call a non-const method close on a constant object std::ifstream.

The solution here is to use an object through a pointer. True for both Clang and GCC.

Problem 2. Passing an Objective-C object to a C function under ARC

Description

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

Objective-C
NSMutableArray *rowsArray = [[NSMutableArray alloc] init];
sqlite3_exec(database, "select distinct category from persons", callback, rowsArray, NULL);

When ARC is on you will get a compilation error

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

ARC will not allow so easy 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
sqlite3_exec(database, "select distinct category from persons", callback, (__bridge  void*)rowsArray, NULL); 

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

Objective-C
sqlite3_exec(database, "select distinct category from persons", callback,
(__bridge_retained  void*)rowsArray, NULL);

This will call <span lang="EN-US">objc_retain</span><span lang="EN-US">()</span> before passing the argument.

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

Objective-C
static int myCallback(void* ptr, int i, char** p1, char** p2) {    
    NSMutableArray* rowsArray = (__bridge_transfer NSMutableArray *)ptr;    
    ptr = NULL;    
    // Do something with rowsArray here    
    return 0;
} 

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 scope. The line ptr = NULL is optional, but it is a good style. You have placed a reference that the ptr contained, under the control of ARC, so reset of the pointer is clear.

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, because only Clang supports ARC.

Problem 3. Creating Objective-C properties returning C++-objects in Objective-C classes

Description

Suppose, we have an Objective-C class:

@interface MyObjc : NSObject
{
}

@end  

And imagine that we have some simple C++ class named MyCppClass. We would like to add some property to the Objective-C class that returns the object of type MyCppClass.

Solution

Now you can do such thing but only with C++ object variables, that have default constructors. Both Clang and GCC support such case.

Starting with Mac OS X 10.4 and GCC 4 you can place C++ objects as instance member variables in Objective-C classes assuming that the C++ object must be created with default constructor. GCC compiler has a special compiler option that controls the process of calling the default constructor for this variables «Call C++ Default Ctors / Dtors in Objective-C» (GCC_OBJC_CALL_CXX_CDTORS, -fobjc-call-cxx-cdtors). Though Clang has no special option for C++ constructors/destructors it generates correct calls for C++ constructors.

For example, we can write the following code

C++
class MyCppClass
{
    MyCppClass(); 
};
 
@interface MyObjc : NSObject
{
    MyCppClass cppVariable;
}

@property (nonatomic, assign) MyCppClass cppVariable;
@end 

and at implementation file, @synthesize cppVariable; as well.

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)


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 5 Pin
Volynsky Alex15-May-14 11:47
professionalVolynsky Alex15-May-14 11:47 

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.