c与c++相互调用机制分析与实现

c++通常被称为Better c,多数是因为c++程序可以很简单的调用c函数,语法上基本实现兼容。最常用的调用方式就是c++模块调用c实现的dll导出函数,很简单的用法,使用extern "C"将c头文件或者函数修饰下。

本文主要涉及到在c模块中如何调用c++函数,或者换个名字,extern "C"在c语言中的功能介绍

c++中的extern "C"

通常,我们在需要调用c函数或者c实现的模块时,需要使用extern "C"修饰下对应的部分代码,告诉c++编译器按照c的调用规约调用相关模块代码。常见的形式如下:

extern "C"
{
	// ffmpeg public header
	#include "avutil.h"
	#incluee "avcodec.h"

	// 函数声明,以c语言调用
	int Func(int param);
}

c语言中的extern "C"

近期在看JNI的调用实现机制,不自觉的在想c能调用c++模块吗?
基本的思路是来在于c++语言提供的extern "C"机制,既然可以在c++中写c模块,ok,那只需要一个中间层就可以让c调用c++的模块。

普通函数调用

在c++实现如下函数:

// in header file(.h)
extern "C" int FunCppDecorate(int param);

// in implenmentation file(.cpp)
int FunCppDecorate(int param)
{
	printf("decorating by extern c, you have rights to invoke cpp function in c\nwith input %d\n"
			, param);
	return (param + 1);
}

在c中按照下面方式调用

// declaration
int FunCppDecorate(int param);

// invoke
FunCppDecorate(1);

重载函数调用

由于c不支持重载函数,如果需要c调用c++重载函数需要显式的给出调用的方式,并在c声明时给出对应对应机制。
在c++实现如下函数:

// in header file(.h)
void OverloadFunc(int param, bool is_c=false);
void OverloadFunc(double param, bool is_c=false);

extern "C"
{
	void OverloadDecorate_i(int param);
	void OverloadDecorate_d(double param);
}
	

// in implenmentation file(.cpp)
// ...
void OverloadDecorate_i(int param)
{
	OverloadFunc(param, true);
}

void OverloadDecorate_d(double param)
{
	OverloadFunc(param, true);
}

在c中按照下面方式调用

// declaration
void OverloadDecorate_i(int param);
void OverloadDecorate_d(double param);

// invoke
OverloadDecorate_i(1);
OverloadDecorate_d(2.0);

类成员函数的调用

由于c++中类具有特殊的编译器附加的构造和析构函数,为了在c中可以访问c++的类,需要做一些c++编译器实现的功能,比如对象的构造和析构。c不能直接使用class名称,需要使用struct作为中转。实现调用如下:

// in header file(.h)
class AType
{
public:
	AType();
	~AType();
	
	void MemFunc(int value);
};

extern "C"
{
	struct TagAType * CreateInstance();
	void DestoryInstance(struct TagAType ** atype);
	void ClassMemFunc(struct TagAType * pthis, int param);
}
	

// in implenmentation file(.cpp)
// ...
extern "C" struct TagAType
{
	AType a;
};

struct TagAType * CreateInstance()
{
	return (TagAType*)malloc(sizeof(TagAType));
}
void DestoryInstance(struct TagAType ** atype) 
{
	if (NULL != atype && NULL != *atype)
	{
		free(*atype);
		atype = NULL;
	}
}
void ClassMemFunc(struct TagAType * pthis, int param)
{
	if(NULL != pthis)pthis->a.MemFunc(param);
}

在c中按照下面方式调用

// declaration
struct TagAType;
struct TagAType * CreateInstance();
void DestoryInstance(struct TagAType ** atype);
void ClassMemFunc(struct TagAType * pthis, int param);

// invoke
struct TagAType * obj = CreateInstance();
ClassMemFunc(obj, 12);
DestoryInstance(&obj);

小结

相关代码可以从我的git下载:https://git.oschina.net/Tocy/SampleCode.git ,位于c_c++目录下,名字前缀为1-c-invoke-cpp*。
其中四个文件,1-c-invoke-cpp.cpp(h)是c++中的实现文件(头文件),1-c-invoke-cpp-main.c(h)是c中的实现文件(头文件),其中包含主函数的测试代码。
编译和运行命令可以参考如下:

g++ -c 1-c-invoke-cpp.cpp
gcc -c 1-c-invoke-cpp-main.c
gcc 1-c-invoke-cpp.o 1-c-invoke-cpp-main.o -o invoke.exe
invoke
pause

针对c++实现中的extern "C"修饰符的作用,可以使用nm命令查看.o文件的输出格式,这是我使用gcc编译后的输出

nm 1-c-invoke-cpp.o
...
00000000000000ac T _Z12OverloadFuncdb
0000000000000041 T _Z12OverloadFuncib
0000000000000000 T _Z6printfPKcz
0000000000000000 T _Z8DenyFuncv
0000000000000168 T _ZN5AType7MemFuncEi
0000000000000150 T _ZN5ATypeC1Ev
0000000000000150 T _ZN5ATypeC2Ev
000000000000015c T _ZN5ATypeD1Ev
000000000000015c T _ZN5ATypeD2Ev
00000000000001e4 T ClassMemFunc
000000000000018f T CreateInstance
00000000000001a7 T DestoryInstance
U free
000000000000001b T FunCppDecorate
U malloc
000000000000012c T OverloadDecorate_d
000000000000008d T OverloadDecorate_i

从上面输出可以明显看出c++和c的函数编译之后的修饰规则是不同的。

原文链接: https://www.cnblogs.com/tocy/p/c-cpp-inter-invoke-analysis.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    c与c++相互调用机制分析与实现

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/225729

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 下午12:55
下一篇 2023年2月13日 下午12:55

相关推荐