C++ Functions and Objects in C

In order to call C++ functions from C objects we’ll need a few things:

  • A way for C to reference classes.
  • A way to keep names from getting change so that C can recognize them.
  • A header file that defines the methods that C and C++ compilers can recognize.

In order to accomplish the first one we’ll need a common reference. There’s not really a good clean way to tell C exactly how big a given object should be. The best reference therefore is a pointer to that class that can be held and passed by the C code.

C Code with Main Function

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>
#include "foo.h"

int main(const int argc, const char **argv)
{
   
   void *temp_foo = getFoo(5);

   printString(temp_foo);

   return( EXIT_SUCCESS );
}

In the code above you’ll see that on line 4 the method getFoo(5) returns a void pointer called temp_foo. This is the pointer to our class Foo that we’ll define next.

The Foo class is defined in its own header file. This is done for a few reasons, first it keeps the precompiler directives simpler, and two it makes ensuring the accessor functions for C are defined correctly for every C++ function. Theres quite a bit going on here so take a look at the code below and we’ll get to explaining.

C++ and C Dual Purpose Header File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
 * gotta use stdint so we can use the same type in C++/C
 * could make the example a bit more complicated with some
 * #defines to fix but it's simpler this way.
 */
#include <stdint.h>


#ifdef __cplusplus

#include <string>

class Foo
{
public:
   Foo( int32_t a );
   ~Foo();
   std::string toString();
private:
   int32_t k;
};

#endif

#ifdef __cplusplus
extern "C" 
{
#endif

void*  getFoo( int32_t a );
void   destroyFoo( void *foo );
void   printString( void *foo );

#ifdef __cplusplus
}
#endif

The first thing you’ll see that’s out of the ordinary is the __cplusplus ifdef statement. This is defined if this header is being processed by a c++ compiler. If you follow the code to the end of the first if statemetn at line 15 you’ll see it only encompases the class definition. That is because classes aren’t recognized in C++.
When this header is processed by a C compiler the declaration of class Foo and all its functions disappears entirely. The next thing you’ll notice is line 17. It has the same directive as line 4, however this time we’re using it to insert the line extern “C” around the functions declared on lines 20,21,22 ending in the same declaration with closing braces at line 24. This ensures that when this header file and its implementation are compiled into an object file that they are not mangled, that is they are recognizable by the C compiler produced object file. Now, if we take out everything thats only compiled using a C++ compiler (everything surrounded by the __cplusplus), we see that the only thing that’ll get compiled by the C compiler when we include this header are the functions at 20, 21 and 22 (our accessor functions) which is exactly what we want.

I’ve mentioned accessor functions quite a bit. The accessor functions are a way to convert that reference we passed to C (the void pointer) back into something usable by C++ and the do something with it. We’ll see more of this next.

C++ Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdint.h>

#include "foo.h"

Foo::Foo( int32_t a )
{
   k = a << 1;
}

Foo::~Foo()
{
   /* don't really need to do anything here */
   /* k = 0 only for example purposes       */
   k = 0;
}

std::string 
Foo::toString()
{
   std::ostringstream os;
   os << "Foo is currently: " << this->k << std::endl;
   return( os.str() );
}

void* 
getFoo( int32_t a )
{
   Foo *out( new Foo(a) );
   return( reinterpret_cast< void* >( out ) );
}

void 
destroyFoo( void* foo )
{
   delete( reinterpret_cast< Foo* >( foo ) );
}


void 
printString( void *foo )
{
   std::string s = reinterpret_cast< Foo* >( foo )->toString();
   std::cout << s;
}

In the above code at line 6 we see that our header, foo.h, is included. At lines 12 through 18 we’ve defined all our class methods. At line 24 is where our C functions begin. There are a few restrictions that must be placed on these functions:

  • They must only use types available to C, i.e.C has no concept of a class
  • Its typically not a good idea to try to use a struct with the same name as the class as a parameter

With that, the basic idea is to create a wrapper for each C++ function we’d like to call from C. This includes: constructors, destructors and any methods we’d like to call within the C++ compiled object file. The last thing you’ll want to do is put it all together using a Makefile and your C/C++ compilers of choice.

Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CC  ?= gcc
CXX ?= g++

CFLAGS = -O0 -g -Wall -pedantic 
CXXFLAGS = -O0 -g -Wall -pedantic

OBJ	= main foo
OBJS  = $(addsuffix .o,$(OBJ))
all:
	make compile

compile:
	make $(OBJS)
	make fooexe

fooexe: $(OBJS) 
	$(CXX) -o fooexe $(OBJS)

main.o: main.c
	$(CC) $(CFLAGS) -c -o main.o main.c

foo.o: foo.cpp
	$(CXX) $(CXXFLAGS) -c -o foo.o foo.cpp

clean:
	rm -rf $(OBJS) fooexe *.dSYM

The Makefile above is a bit strange if you’ve never compiled everything first then linked it all together. Looking at line 9 we see that the rule all calls make compile. At line 12 we see that make compile calls make $(OBJS) which at this point stores main.o and foo.o. The rules for each at line 19 and 22 respectively show that they are compiled with C and C++ compilers and each has the -c flag which means only compile, don’t link. The rule for fooexe which is called at line 14 is what actually executes the linking of each object file into one executable object file fooexe.

Download

All code is available from here: https://goo.gl/tV3t4n