我想要一种内联方式来指定哪些原型应该包含在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
但它非常不方便,因为相同功能的重载在不同的地方。我可以只有两个不同的头文件,但这也很痛苦。因此,我正在寻找像我上面提出的内联解决方案。有谁知道这样做的方法?
答案 0 :(得分: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))
答案 1 :(得分: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的答案显示了一种可能的方式。
答案 2 :(得分: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-callable函数。
答案 3 :(得分:1)
你显然可以滥用预处理器来破解你所问的内容,但为什么呢?就个人而言,如果我使用的是C ++,我宁愿输入mylst.insert(foop, 1);
而不是ArrayList_insert(mylst, foop, 1);
。换句话说,我认为使用C样式调用重载函数没什么好处,但是混合代码作者创建的函数调用样式也不是很完美。
我创建一个与C结构中的成员相同的ArrayList类,并且只要需要将类与C函数接口,就可以创建一个浅表副本并将其传递给C函数,然后复制从该结构返回到您班级的信息。
另一种方法是将结构包装在C ++类中,并将其用于C接口函数。
否则,您可能会尝试使该类继承C结构,假设结构的标记未命名为ArrayList,并且从C ++接口看不到该结构的typedef。然后你可以直接从成员函数中传递this指针,就好像它是实际的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
答案 4 :(得分: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);
)
我不认为我自己会这样做,但似乎足够安全,成为惯用语。