When working on a software project, you may sometimes have to mix programming languages.
It may be because you need to use some specific libraries, because of code portability needs, whatever.
When coding a Mac OS X (or iOS) application, you are usually going to use Objective-C as main language.
It's not mandatory, as you can use the old Carbon API, or stuff like QT (C++), but it will usually be easier, at least for all the UI related stuff.
Mixing C with Objective-C is not a problem, as Objective-C is a strict superset of C.
Whether you have pure C code, or if you are linking with a C library, there's no issue.
Mixing C or Objective-C with C++ is not as easy.
Although C++ was originally based on the C language, it's not strictly a superset, so trying to use C++ code from C or Objective-C may not be always easy.
Of course, with Objective-C, you have the possibility to use Objective-C++ (.mm files).
This way, you can use all the C++ features within Objective-C code.
But sometimes, this is not wanted. Moreover, it's of course impossible with pure C.
Before going on, let's take a little example in pure C.
We are going to code a typical «hello world» application.
For this, we decide to have a file for our program's main function, and another file for utility functions.
Let's start by the utility functions file.
First of all, we need to create a header file, named «lib.h»:
#ifndef __LIB_H__ #define __LIB_H__ void say_hello( void ); #endif
We declare here the prototype of a function named «say_hello».
We are now going to provide an implementation, in a new file named «lib.c»:
#include <stdio.h> #include "lib.h" void say_hello( void ) { printf( "hello, world\n" ); }
Nothing special here. Now let's create our program's main function, in a file named «main.c»:
#include "lib.h" int main( void ) { say_hello(); return 0; }
Here, we just use our «say_hello» function, before exiting the program.
Let's take a look at the compilation. We are going to generate an object file (.o) for each C file, and then link them together.
Let's start with «lib.c»:
gcc -Wall -o lib.o -c lib.c
From the «lib.c» source file, we generate an object file (machine code) named «lib.o». We can use the «nm» command to list the symbols available in that object file:
nm lib.o
It will output:
0000000000000028 s EH_frame0 0000000000000015 s L_.str U _puts 0000000000000000 T _say_hello 0000000000000040 S _say_hello.eh
We can see here our «say_hello» function.
As you can see, it has a leading underscore. This is totally normal.
Now let's create an object file from «main.c»:
gcc -Wall -o main.o -c main.c
Now, we have two distinct object files.
In order to create an executable, we need to link them. This can be done with:
gcc -Wall -o test lib.o main.o
This will create an executable named «test», which will just print «hello, world» when run.
Piece of cake.
But now imagine we want to use C++ for the library part, and C for the other parts.
First of all, we are going to rename «lib.c» to «lib.cpp».
The «lib.h» header doesn't need to be changed.
We are also going to replace the implementation of the «say_hello» function, so it uses «IOStream» instead of «printf()».
Our «lib.cpp» file will now look like that:
#include <iostream> #include "lib.h" void say_hello( void ) { std::cout << "hello, world" << std::endl; }
No problem here, just a standard C++ hello world.
Let's recompile everything (note that we are now going to use g++ instead of gcc for the C++ file):
g++ -Wall -o lib.o -c lib.cpp gcc -Wall -o main.o -c main.c
No problem here. Let's link the two files in order to create an executable, as previously:
gcc -Wall -o test lib.o main.o
Unfortunately, you'll have linker errors here, such as:
Undefined symbols for architecture x86_64: "std::cout", referenced from: say_hello() in lib.o "std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)", referenced from: say_hello() in lib.o "std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)", referenced from: say_hello() in lib.o "std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))", referenced from: say_hello() in lib.o "std::ios_base::Init::Init()", referenced from: __static_initialization_and_destruction_0(int, int)in lib.o "std::ios_base::Init::~Init()", referenced from: ___tcf_0 in lib.o "_say_hello", referenced from: _main in main.o ld: symbol(s) not found for architecture x86_64 collect2: ld returned 1 exit status
Ok, obviously we are not linked with the C++ standard library.
This is because we used gcc to create our final executable.
So let's use g++ for the final stage.
Note that we will still use gcc for the «main.c» file, as it's still pure C code.
No need for a C++ compiler here:
g++ -Wall -o lib.o -c lib.cpp gcc -Wall -o main.o -c main.c g++ -Wall -o test lib.o main.o
We still got a linker error:
Undefined symbols for architecture x86_64: "_say_hello", referenced from: _main in main.o ld: symbol(s) not found for architecture x86_64 collect2: ld returned 1 exit status
Here, we can see we are effectively linked with the C++ standard library, as the errors for the «std::» members, like «std::cout» are gone.
But it seems that the C++ version of our «say_hello» function is not found. Let's take a look at the «lib.o» file, to find out what's wrong. We are going to use the «nm» command again, to list the symbols available in that file:
nm lib.o
The result is:
0000000000000100 s EH_frame0 00000000000000eb s L_.str 0000000000000070 s __GLOBAL__I__Z9say_hellov 0000000000000148 s __GLOBAL__I__Z9say_hellov.eh 0000000000000090 s __Z41__static_initialization_and_destruction_0ii 0000000000000178 s __Z41__static_initialization_and_destruction_0ii.eh 0000000000000000 T __Z9say_hellov 0000000000000118 S __Z9say_hellov.eh U __ZNSolsEPFRSoS_E U __ZNSt8ios_base4InitC1Ev U __ZNSt8ios_base4InitD1Ev U __ZSt4cout U __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 00000000000001d8 b __ZStL8__ioinit U __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc U ___cxa_atexit U ___dso_handle 0000000000000040 t ___tcf_0 00000000000001a8 s ___tcf_0.eh
There's no symbol for our «say_hello» function.
Instead, we have:
__Z9say_hellov
What's this «_Z9» prefix?
Ever heard of «name mangling»?
First of all, remember that C++ has built-in support for function overloading.
It means that the following example is perfectly valid:
void foo( int x ); void foo( double x ); int foo( int x );
Here, we've got three functions named «foo».
In C or Objective-C, this is not possible, as a symbol can only be defined once.
In C++, this is called «function overloading». A function (or method) can have multiple implementations, as long as there is a difference in the return type and/or the arguments.
The compiler will then generate three different functions, and automatically choose which one to use when you issue a call, depending on the return type and the argument types.
In order to do this, as the compiler will create different functions, it will have to use different symbol names.
That's why C++ functions (or methods) have a modified symbol name, in the resulting object code.
So how can we deal with this, from our «main.c» file?
With Objective-C++, it would have worked, of course. But not with pure C, or pure Objective-C.
Fortunately, there is a way to tell the C++ compiler to comply with C-like declarations, so it won't use its calling conventions (with mangled names) when producing object code.
This way, our C++ function will be callable from pure-C (even if it's implementation uses C++ features).
In order to do this, we just need to change the «lib.h» header file:
#ifndef __LIB_H__ #define __LIB_H__ #ifdef __cplusplus extern "C" { #endif void say_hello( void ); #ifdef __cplusplus } #endif #endif
When the C++ compiler is used, the «__cplusplus» macro is defined.
Now, in such a case, our function's prototype will be wrapped with:
extern "C" { }
This will tell the C++ compiler that we intend to use everything inside the braces from C code, meaning it will generate symbol names according to C.
So let's recompile our «lib.cpp» file into object code:
g++ -Wall -o lib.o -c lib.cpp
Now, if we run «nm», we can see:
0000000000000100 s EH_frame0 00000000000000eb s L_.str 0000000000000070 s __GLOBAL__I_say_hello 0000000000000148 s __GLOBAL__I_say_hello.eh 0000000000000090 s __Z41__static_initialization_and_destruction_0ii 0000000000000178 s __Z41__static_initialization_and_destruction_0ii.eh U __ZNSolsEPFRSoS_E U __ZNSt8ios_base4InitC1Ev U __ZNSt8ios_base4InitD1Ev U __ZSt4cout U __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 00000000000001d8 b __ZStL8__ioinit U __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc U ___cxa_atexit U ___dso_handle 0000000000000040 t ___tcf_0 00000000000001a8 s ___tcf_0.eh 0000000000000000 T _say_hello 0000000000000118 S _say_hello.eh
Hurray! The symbol name for our «say_hello» function is now «_say_hello» as with pure C.
It means we are now able to link our object files, in order to produce the executable:
g++ -Wall -o test lib.o main.o
No error here. Our executable was successfully produced, and is working fine!
Of course, when using «extern "C" {}», you can no more use C++ stuff like function's overloading, or namespaces (and of course classes).
But this way, when providing functions implemented in C++, you can make them available to C code, and so to Objective-C as well.