10

我想要一种内联方式来指定哪些原型应该包含在 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
外部“C”{
#万一

....C 标题..

#ifdef __cplusplus
}

....C++ 头文件...

#万一

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

4

5 回答 5

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))
于 2013-07-01T18:56:08.647 回答
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,您将只获得一个。如果要在两者之间共享一个库,则需要extern "C"在为 C++ 编译时向 C 函数添加一些限定符。@MarkLakata 的回答显示了一种可能的方法。

于 2013-07-01T18:45:04.957 回答
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 */

并且您需要用 . 装饰 C 可调用函数EXTERN_C

于 2013-07-02T13:39:53.690 回答
1

您显然可以滥用预处理器来破解您所要求的内容,但为什么要这样做呢?就我个人而言,我宁愿键入mylst.insert(foop, 1);而不是ArrayList_insert(mylst, foop, 1);使用 C++。换句话说,我认为使用 C 风格调用重载函数没有什么好处,但是混合作为代码作者创建的函数调用的风格也不是很漂亮。

我会创建一个 ArrayList 类,它具有与 C 结构中相同的成员,并且无论何时您需要将您的类与 C 函数接口,如果可能,创建一个浅拷贝并将其传递给 C 函数,然后从那个结构回到你的班级。

另一种方法是将结构包装在 C++ 类中,并将其用于 C 接口函数。

否则,您可能会尝试使类继承 C 结构,假设结构的标记未命名为 ArrayList 并且该结构的 typedef 从 C++ 接口不可见。然后,您可以希望直接从成员函数中传递 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
于 2013-07-01T21:51:30.417 回答
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为了强调它主要用于函数声明是安全的,但请注意,您还需要在其中声明 C 可访问对象extern 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);
)

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

于 2013-07-03T06:44:40.110 回答