2013-07-01 24 views
10

我想要一種內聯的方式來指定哪些原型應該包含在C++中。例如:有沒有內聯的方式來混合c和C++原型?

 
     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
IS_CPP void ArrayList_insert(ArrayList *arrlst, char *data, int i); 
IS_CPP void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i); 

目前我做的:

 
#ifdef __cplusplus 
extern "C" { 
#endif 

....C HEADERS.. 

#ifdef __cplusplus 
} 

....C++ HEADERS... 

#endif 

但它非常不方便,因爲同樣的功能的重載是在不同的地方。我可能只有2個不同的頭文件,但這也很痛苦。因此,我正在尋找像我上面提出的內聯解決方案。有誰知道一種方法來做到這一點?

回答

14

這比您想象的要容易。

#ifdef __cplusplus 
#define IS_C(x) extern "C" x ; 
#define IS_CPP(x) x ; 
#else 
#define IS_C(x) x ; 
#define IS_CPP(x) 
#endif 

使用這種類型的頭信息:

IS_C (void ArrayList_insert(ArrayList *arrlst, void *data, int i)) 
IS_CPP (void ArrayList_insert(ArrayList *arrlst, char *data, int i)) 
IS_CPP (void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i)) 
+0

這會在用c編譯時導致錯誤,因爲c不支持函數重載。 – chacham15

+0

chacham15 - 你是對的。我確定了答案,因爲我誤解了這個問題。 –

+0

現在它相當於@Carl Norum的答案。 – chacham15

11

當然,你可以使用函數宏做到這一點就像你的例子:

#ifdef __cplusplus 
#define IS_CPP(x) x 
#else 
#define IS_CPP(x) 
#endif 

     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
IS_CPP(void ArrayList_insert(ArrayList *arrlst, char *data, int i)); 
IS_CPP(void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i)); 

現在,如果你編譯標頭,如C++,你會得到所有三個,但如果你的編譯C,你只會得到那個。如果你想在兩者之間共享一個庫,那麼編譯C++時需要在C函數中添加一些extern "C"限定符。 @ MarkLakata的答案顯示了一種可能的方式。

+0

+1但是,有沒有辦法做到這一點,而沒有拖尾關閉paren? (我只是有點偏心,sry) – chacham15

+0

我不這麼認爲,不。 –

+0

-1,這是缺少必要的語言鏈接。 – Potatoswatter

1

可以很明顯的濫用預處理劈在一起你問什麼,但爲什麼呢?如果我使用C++,我個人寧願輸入mylst.insert(foop, 1);而不是ArrayList_insert(mylst, foop, 1);。換句話說,我發現使用C風格調用重載函數幾乎沒有什麼好處,但將作爲代碼創建者創建的函數調用樣式混合起來也不完全相同。

我會創建一個ArrayList類,它具有與C結構中相同的成員,並且無論何時您需要將您的類與C函數接口,如果可能的話創建一​​個淺拷貝並將其傳遞給C函數,然後將來自該結構的信息複製回您的班級。

另一種方法是將結構包裝在C++類中並將其用於C接口函數。否則,假設結構的標記未命名爲ArrayList,並且該結構的typedef從C++接口不可見,則可以嘗試讓該類繼承C結構。然後你可以直接從成員函數中傳遞這個指針,就好像它是實際的C結構一樣。我不確定這種方法在所有情況下都是可移植的,所以如果可能的話,我會實現以前的想法,即使它需要來回複製數據。

所有想法都避免了代碼重複,C++接口看起來更像是C++代碼,而不是C函數和C++函數重載的混合。此外,接口保持分離。沒有額外的頭文件是必要的或者作爲C函數可以被包裹在extern「C」塊照例:

#ifdef __cplusplus 
extern "C" { 
#endif 

struct array_list_tag { 
    ... 
}; 

/* C functions here */ 

#ifdef __cplusplus 
} /* extern "C" */ 

class ArrayList ... 
#else /* !__cplusplus */ 
typedef struct array_list_tag ArrayList; 
#endif 
+0

「所有想法都避免代碼重複」的確如此。一個示例用例是,如果您正在使用C++編寫一個庫,這意味着要與C高度集成在一起。您自己喜歡調用重載函數,但是需要爲您要集成的C調用必要的方法功能也是如此。將類中的結構包裝起來或反過來會增加代碼重複的數量,並增加平臺間編碼風格的不一致性 – chacham15

+0

所以你說C應該能夠調用每個重載函數?然後,而不是像OP所請求的那樣僅爲C的ArrayList_insert,現在是ArrayList_insertBuffer,ArrayList_insertString,ArrayList_insertRawData等。那麼,C++接口至少會匹配...至於在類中包裝結構,你可能是對的。我確信在C++標準中有一些東西需要一個具有C連接的函數才能訪問C++類數據,只要沒有繼承,虛函數和混合類的所有其他注意事項,以及C. –

3

通常的做法是隻把它寫在最明顯的方法:

void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
#ifdef __cplusplus 
void ArrayList_insert(ArrayList *arrlst, char *data, int i); 
void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i); 
#endif /* __cplusplus */ 

正如@ chacham15所指出的,你還需要,在項目範圍內的頭,

#ifdef __cplusplus 
#define EXTERN_C extern "C" 
#endif /* __cplusplus */ 

,你需要用EXTERN_C裝飾C調用的函數。

+0

它並不那麼簡單,你錯過了c函數的'#ifdef __cplusplus extern「c」#endif'聲明,這讓IMO看起來非常糟糕。 – chacham15

+0

@ Chacham15:當你在別處評論時,'#ifdef __cplusplus #define IS_C extern「C」#else #define IS_C#endif'或類似的(我建議像「EXTERN_C」這樣的名字)可以做一次,然後皮特的方法看起來像非常乾淨...比在宏中包裝每一行更好,複製'))',要麼在宏內部使用分號,要麼在每行中使用分號。 –

1

如果你真的想擺脫樣板,並且你願意使用預處理器來做到這一點,那麼繼續寫下模式。你擁有的一般模式看起來像

extern "C" { 
    void C_accessible_declaration(); // this is all C sees 
} 

void Cxx_accessible_declaration_1(int); 
void Cxx_accessible_declaration_1(long); 

所以,你可以做一個宏,

#ifdef __cplusplus 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, CXX_DECLS) \ 
     extern "C" { C_DECLS } \ 
     CXX_DECLS 
#else 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, CXX_DECLS) \ 
     C_DECLS 
#endif 

這工作,因爲一個普通的函數聲明不能​​包含不被括號括起來的逗號。如果您希望它與模板一起使用(使用逗號分隔的模板參數),則可以使用可變參數宏,C99,C++ 11和各種編譯器支持的各種編譯器作爲擴展。

#ifdef __cplusplus 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, ...) \ 
     extern "C" { C_DECLS } \ 
     __VA_ARGS__ 
#else 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, ...) \ 
     C_DECLS 
#endif 

現在這個工作,只要C聲明不包含裸體逗號,這意味着你不應該在一個聲明中聲明多個對象。我稱之爲C_PORTABLE_FUNCTION_SET以強調它主要用於函數聲明是安全的,但請注意,您還需要在extern C之內聲明C可訪問的對象。共享struct定義不應該受到保護;它們受到C++ POD概念的保護,並且不會進行語言鏈接。

用法:

#ifdef __cplusplus 
template< typename T, typename U > 
class Buffer { // still use #ifdef for the general case 
    ... 
}; 
#endif 

C_PORTABLE_FUNCTION_SET (
     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
, /* C++ */ 
     void ArrayList_insert(ArrayList *arrlst, char *data, int i); 

     template< typename T, typename U > 
     void ArrayList_insert(ArrayList *arrlst, Buffer< T, U > &data, int i); 
) 

我不認爲我會這樣做我自己,但似乎足夠安全成爲習慣。