2011-09-02 38 views
44

我們開發了一些簡單的項目C(C99)。但是,我們在C++中有一個作爲源代碼的庫(數學庫)。我們需要這個庫,所以我想問一下,整合這個源代碼最優雅的方式是什麼?從C中優雅地調用C++

尺寸爲CC++之間的比率爲20:1因此不能選擇C++。我們應該使用靜態庫嗎? DLL? (全部在Windows上)。

+8

C++庫是否提供了一個合適的C接口(自由函數而不是成員函數,沒有例外,extern「C」等)? –

+0

混合C和C++的常見問題解答:http://www.parashift.com/c++-faq/mixing-c-and-cpp.html – Maciej

+1

[如何從C調用C++函數](http:// stackoverflow.com/questions/2744181/how-to-call-c-function-from-c) –

回答

50

編輯:基於在評論的討論,我要指出的是分離的東西到C兼容struct duck和派生class Duck可能是不必要的。您可能可以安全地將執行鏟入struct duck並消除class Duck,從而避免real(…)。但是我不太瞭解C++(尤其是它與C宇宙交互的方式),以便爲此提供明確的答案。


沒有理由你不能簡單地鏈接所有的C和C++代碼一起到一個單一的二進制文件。

與C++代碼的接口需要將C++ API封裝到C API中。你可以通過在編譯C++代碼時在extern "C" { ... }內聲明一堆函數,並且在編譯C客戶端代碼時不用extern聲明來實現。例如:

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct duck duck; 

duck* new_duck(int feet); 
void delete_duck(duck* d); 
void duck_quack(duck* d, float volume); 

#ifdef __cplusplus 
} 
#endif 

你可以在你的C++源定義鴨結構,甚至從它繼承了真正的Duck類:

struct duck { }; 

class Duck : public duck { 
public: 
    Duck(int feet); 
    ~Duck(); 

    void quack(float volume); 
}; 

inline Duck* real(duck* d) { return static_cast<Duck*>(d); } 

duck* new_duck(int feet) { return new Duck(feet); } 
void delete_duck(duck* d) { delete real(d); } 
void duck_quack(duck* d, float volume) { real(d)->quack(volume); } 
+0

很好的答案,謝謝! – Cartesius00

+0

國際海事組織空白指針會更適合在這裏 – Ulterior

+32

@Ulterior:不IMO。如果你把鴨子和青蛙當成空針,你可能會不小心讓青蛙嘎嘎作響,這會撕裂宇宙的結構。 –

7

的唯一原因想從鴨結構繼承會在C API中公開一些屬性,無論如何這通常被認爲是不好的風格。沒有繼承,你的C頭應該是這樣的:

struct Duck; 

struct Duck* new_Duck(int feet); 
void delete_Duck(struct Duck* d); 
void Duck_quack(struct Duck* d, float volume); 

,這將是相應的實施,而無需類型轉換:

extern "C" { 
#include "Duck.h" 
} 

class Duck { 
public: 
    Duck(int feet) : {} 
    ~Duck() {} 

    void quack(float volume) {} 
}; 

struct Duck* new_Duck(int feet) { return new Duck(feet); } 
void delete_Duck(struct Duck* d) { delete d; } 
void Duck_quack(struct Duck* d, float volume) { d->quack(volume); } 

以同樣的方式,一個C API可爲C++接口(純虛擬類)及其實現創建。在這種情況下,只有構造函數需要基於具體實現(例如new_RubberDuck(2))。析構函數和所有其他函數將自動在正確的實現上運行,與C++中的一樣。

+2

函數實現也應該被標記爲extern「C」。否則,你會得到鏈接錯誤,因爲「new_Duck :: i」或者你的平臺上的任何名字使得C++函數與頭文件的「new_Duck」不匹配。 – uliwitness

5

C++數學庫很可能在實用程序類(僅限靜態成員)中實現。在這種情況下,一個更簡單的方法可以採取:

class FPMath { 
public: 
    static double add(double, double); 
    static double sub(double, double); 
    static double mul(double, double); 
    static double div(double, double); 
}; 

針對C接口的頭然後將:

double FPMath_add(double, double); 
double FPMath_sub(double, double); 
double FPMath_mul(double, double); 
double FPMath_div(double, double); 

和相應的實現可能是:

double FPMath_add(double a, double b) { return FPMath::add(a, b); } 
double FPMath_sub(double a, double b) { return FPMath::sub(a, b); } 
double FPMath_mul(double a, double b) { return FPMath::mul(a, b); } 
double FPMath_div(double a, double b) { return FPMath::div(a, b); } 

但也許這是明顯的....

0

There is一種創建「黑客」的方法,允許您直接調用某些對象的成員函數。

您需要做的第一件事是創建一個extern "C"工廠函數,該函數返回一個指針(如void*)到對象。

您需要的第二件事是成員函數的損壞名稱。

然後,您可以使用mangled名稱並將工廠函數返回的指針作爲第一個參數傳遞給該函數。

注意事項:

當然
  • 將無法​​正常工作調用想要其他物體,或引用,或其他C++的東西,或返回對象或類型的功能與C類型不兼容的成員函數
  • 將無法​​正常工作虛擬成員函數,並且可能不會對他們有虛函數的對象,即使它沒有被調用
  • 錯位的名稱必須是一個有效的C符號
  • 任何許多許多......
  • 虛函數

這不是我推薦的,事實上恰恰相反。我強烈建議不要執行此答案中概述的內容。它不受支持,可能未定義行爲,並可能以怪異和不可預知的方式打破。