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的函式編譯之後的修飾規則是不同的。