现在的位置: 首页 > 综合 > 正文

如何在C中使用C++的类

2018年05月19日 ⁄ 综合 ⁄ 共 3196字 ⁄ 字号 评论关闭

Today
I had a little chat with Michiel on #gnome-nl regarding the use of C++ classes in C code (he started learning C again ;-)).


I
was fascinated (well, sort of) by this, and tried to get something working. Here's the result:


*
First we need a C++ class, using one header file (Test.hh)


class
Test {

public:
void
testfunc();

Test(int
i);


private:
int
testint;

};

and
one implementation file (Test.cc)


#include
<iostream>

#include
"Test.hh"


using
namespace std;


Test::Test(int
i) {

this->testint
= i;

}

void
Test::testfunc() {

cout
<< "test " << this->testint << endl;

}

This
is just basic C++ code.

*
Then we need some glue code. This code is something in-between C and C++. Again, we got one header file (TestWrapper.h, just .h as it doesn't contain any C++ code)


typedef
void CTest;


#ifdef
__cplusplus

extern
"C" {

#endif
CTest
* test_new(int i);

void
test_testfunc(const CTest *t);

void
test_delete(CTest *t);

#ifdef
__cplusplus

}
#endif

and
the function implementations (TestWrapper.cc, .cc as it contains C++ code):


#include
"TestWrapper.h"

#include
"Test.hh"


extern
"C" {


CTest
* test_new(int i) {

Test
*t = new Test(i);


return
(CTest *)t;

}

void
test_testfunc(const CTest *test) {

Test
*t = (Test *)test;

t->testfunc();
}

void
test_delete(CTest *test) {

Test
*t = (Test *)test;


delete
t;

}
}

Some
things you should notice:

1.
typedef void CTest

We
typedef CTest to void. This way we can use "CTest *" in our C code as if it's a normal C type, whilst we have compile-time type checks (sort of at least :-)), and it's cleaner than always using "void *"

2.
The use of "extern "C" { }" around all functions (both definitions and implementations). We need this so the compiler won't name-mangle the resulting binaries. If you want to see what name-mangling is:


$
cat test.c

#include
<iostream>

using
namespace std;


void
test() {

cout
<< "test" << endl;

}

int
main(int argc, char *argv[]) {

test();
return
0;

}
$
g++ -o nmtest test.c

$
./nmtest

test
$
nm nmtest

*blablabla*
08048818
t _Z41__static_initialization_and_destruction_0ii

080487c4
T _Z4testv

U
_ZNKSs4sizeEv@@GLIBCXX_3.4

U
_ZNKSsixEj@@GLIBCXX_3.4

*blablabla*

As
you can see, our "test()" function has been renamed to "_Z4testv" by the compiler. This is needed to allow polyphormism in C++, but we don't want this in our C wrapper of course, as we want to know the name of the function we will call!

This
implies we need another function name for every polyphormistic (SP?) class function of our C++ class in the C wrapper. 

*
At last, we need some code to test our work (main.c):


#include
<stdio.h>

#include
"TestWrapper.h"


int
main() {

CTest
*t = NULL;


t
= test_new(5);

test_testfunc(t);
test_delete(t);
t
= NULL;


return
0;

}

This
is, once more, braindead simple (C) code, where we use the functions defined in TestWrapper.h.

*
Last but not least, we need to compile everything. I made a basic Makefile to do this (Makefile):


CFLAGS=-Wall
-Werror -g -ansi -pedantic -std=c89

CCFLAGS=-Wall
-Werror -g

LDFLAGS=-g
-Wall -lstd
c++

OBJS=Test.o
TestWrapper.o main.o

PROG=test

all:
$(PROG)

default:
all


%.o:
%.cc

$(CC)
$(CCFLAGS) -c $<


%.o:
%.c

$(CC)
$(CFLAGS) -c $<


$(PROG):
$(OBJS)

$(CC)
$(OBJS) $(LDFLAGS) -o $@


clean:
rm
-f $(OBJS)

rm
-f $(PROG)


Now
we can simply call "make" to build the project:


$
make

cc
-Wall -Werror -g -c Test.cc

cc
-Wall -Werror -g -c TestWrapper.cc

cc
-Wall -Werror -g -ansi -pedantic -std=c89 -c main.c

cc
Test.o TestWrapper.o main.o -g -Wall -lstd
c++ -o
test


Finally,
we test the resulting binary:


$
./test

test
5


which
is the expected result.


Obviously,
writing a wrapper like this by hand can be a boring task. It might be possible to automate/s cript this, but I don't know whether the result is worth the time one puts into it. Just use plain C, we don't need C++ 


from:http://blog.eikke.com/index.php/ikke/2005/11/03/using_c_classes_in_c

【上篇】
【下篇】

抱歉!评论已关闭.