jueves, 13 de noviembre de 2014

Extern "C": Communicating with C interface through C++

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

int mySum(int x, int y);

#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!




No hay comentarios:

Publicar un comentario en la entrada