〈一〉如何?C中調用C++
如何用c語言調用c++做成的動態連結程式庫, 轉至
http://blog.donews.com/xzwenlan/archive/2005/05/31/405799.aspx
連結庫標頭檔:
//head.h
class A
{
public:
A();
virtual ~A();
int gt();
int pt();
private:
int s;
};
.cpp
//firstso.cpp
#include <iostream>
#include "head.h"
A::A(){}
A::~A(){}
int A::gt()
{
s=10;
}
int A::pt()
{
std::cout<<s<<std::endl;
}
編譯命令如下:
g++ -shared -o libmy.so firstso.cpp
這時候產生libmy.so檔案,將其拷貝到系統庫裡面:/usr/lib/
進行二次封裝:
.cpp
//secso.cpp
#include <iostream>
#include "head.h"
extern "C"
{
int f();
int f()
{
A a;
a.gt();
a.pt();
return 0;
}
}
編譯命令:
gcc -shared -o sec.so secso.cpp -L. -lmy
這時候產生第二個.so檔案,此時庫從一個類變成了一個c的介面.
拷貝到/usr/lib
下面開始調用:
//test.c
#include "stdio.h"
#include "dlfcn.h"
#define SOFILE "sec.so"
int (*f)();
int main()
{
void *dp;
dp=dlopen(SOFILE,RTLD_LAZY);
f=dlsym(dp,"f");
f();
return 0;
}
編譯命令如下:
gcc -rdynamic -s -o myapp test.c
運行Z$./myapp
10
$
關於這個文章的一點補充,轉至http://blogs.sun.com/lirincy/
這篇blog寫了怎樣用C調用C++的庫,我實驗了一下,在linux上成功,
有兩個地方要改一下,最後的編譯語句應該是:
gcc -rdynamic -s -o -ldl myapp test.c
還有就是test.c最後應該加上:
dlclose(dp);
否則會CoreDump。
實際上他是把類的方法變成了一個可以外部調用的C函數,用extern C。
二〉C++程式如何調用C語言寫的庫,如a.lib等,有對應的庫標頭檔a.h。假設a.h中定義了函數:
int WhyCoding(int a, float b);
做法是,
/* cpp_a.h */
extern "C" {
#include "a.h"
}
或
/* cpp_a.h */
extern "C" {
int WhyCoding(int a, float b); /* 重定義所有的C函數 */
}
從上面可以看出,extern "C" 是用在C和C++之間的橋樑。之所以需要這個橋樑是因為C編譯器編譯函數時不帶
函數的類型資訊,只包含函數符號名字,如C編譯器把函數int a(float x)編譯成類似_a這樣的符號,C連接器只要
找到了調用函數的符號,就可以串連成功,它假設參數類型資訊是正確的,這是C編譯連接器的缺點。而C++
編譯器為了實現函數重載,編譯時間會帶上函數的類型資訊,如他把上面的a函數可能編譯成_a_float這樣的
符號為了實現重載,注意它還是沒有帶傳回值得資訊,這也是為什麼C++不支援採用函數傳回值來區別函數
重載的原因之一,當然,函數的使用者對函數傳回值的處理方式(如忽略)也是重要原因。
基於以上,C調用C++,首先需要用封裝函數把對C++的類等的調用封裝成C函數以便C調用,於是extern "C" 的
作用是:讓編譯器知道這件事,然後以C語言的方式編譯和串連封裝函數.(通常是把封裝函數用C++編譯器按C++
方式編譯,用了extern "C" 後,編譯器便依C的方式編譯封裝介面,當然介面函數裡面的C++文法還是按C++方式
編譯;對於C語言部分--調用者,還是按C語言編譯;分別對C++介面部分和C部分編譯後,再串連就可以實現C
調用C++了).
相反,C++調用C函數,extern "C" 的作用是:讓C++連接器找調用函數的符號時採用C的方式,即使用_a而不是
_a_float來找調用函數。