Hi all!
Today, I bring you a short explanation, which is not directly tied to games programming, but just programming in general. However, I think it's something useful as you may find what I'll explain here in libraries that you may use in your games.
The explanation is about the keyword
extern for specifying C linkage. Note that the keyword "extern" has another meaning when it
precedes variables or function names, but we're dealing here with a different use, in particular, with the following one:
extern "C"
{
//Headers (.h) or functions declarations
}
With the above statement, we're telling the C++ compiler the following: "Listen guy, I have several functions (or headers, which in turn comprise several functions) that were compiled with a C compiler, but I want to be able to call these functions from my C++ code".
And why would you need this? What's the difference that makes C and C++ unable to talk to each other natively? The problem is
name mangling due to
function overloading. C++ allows overloading functions, so the C++ compiler needs to use both the function name and arguments information in order to provide a unique identifier for the function. Since C does not allow overloading, it does not need to use name mangling. If you used name mangling, the C++ linker would look for function names that are mangled, but it wouldn't find them because the C compiler didn't mangle the names. So basically, when you use extern "C", you're deactivating the default C++ name mangling for the functions declared inside the block.
A short example comes now. Suppose you have the following C header/implementation:
//mymath.h
#ifndef MYMATH_H
#define MYMATH_H
int mySum(int x, int y);
#endif
//mymath.c
#include "mymath.h"
int mySum(int x, int y)
{
return (x + y);
}
Now, we compile this with a C compiler (suppose gcc):
gcc -c mymath.c
The above line generates an object file called mymath.o.
Now, let's make a simple C++ program that uses the above function like this:
//maincpp.cpp
#include <iostream>
#include "myMath.h"
int main()
{
std::cout << "Sum in C yields: " << mySum(4, 2) << std::endl;
return 0;
}
And now, let's compile with a C++ compiler:
g++ -c maincpp.cpp
This generates an object file called maincpp.o.
Now, let's try to build the executable:
g++ -o main maincpp.o mymath.o
The output of this line is:
Undefined symbols for architecture x86_64:
"mySum(int, int)", referenced from:
_main in maincpp.o
ld: symbol(s) not found for architecture x86_64
So basically, the linker is telling us that it is unable to find the function with name mySum(int, int). The way to solve it is by informing the C++ compiler that mySum is actually a C function, so that it generates the appropriate name for it that the linker can find.
//maincpp.cpp
#include <iostream>
extern "C"
{
#include "mymath.h"
}
int main()
{
std::cout << "Sum in C yields: " << mySum(4, 2) << std::endl;
return 0;
}
Now, after executing:
g++ -o main maincpp.o mymath.o
we get the expected result:
Sum in C yields: 6
Finally, it's very common (and convenient) to find the extern "C" statement wrapping header files as shown next:
#ifdef __cplusplus
extern "C"
{
#endif
//Functions declarations
#ifdef __cplusplus
}
#endif
The symbol __cplusplus is defined only if the compiler is a C++ compiler. Therefore, suppose that we do the following in mymath.h:
//mymath.h
#ifndef MYMATH_H
#define MYMATH_H
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef __cplusplus
}
#endif
#endif
We can still compile mymath.c because __cpluplus is 0 and "extern" will not make it to the object file (C does not understand extern "C"). And what's more important, this allows any C++ client code to be oblivious about whether it should specify or not C linkage. So now, maincpp.cpp would be like this:
//maincpp.cpp
#include <iostream>
#include "mymath.h"
int main()
{
std::cout << "Sum in C yields: " << mySum(4, 2) << std::endl;
return 0;
}
And that's all! Hope you understand everything and find it useful!
See you!