2013-07-01 32 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); 
) 

我不认为我会这样做我自己,但似乎足够安全成为习惯。