是否有一种内联方式来混合使用c和c ++原型?

时间:2013-07-01 18:43:08

标签: 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

但它非常不方便,因为相同功能的重载在不同的地方。我可以只有两个不同的头文件,但这也很痛苦。因此,我正在寻找像我上面提出的内联解决方案。有谁知道这样做的方法?

5 个答案:

答案 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);
)

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