为带有C头文件的c++实现添加名称空间
Adding namespaces to C++ implementations that have a C header
我们有一个C和c++代码的大项目。
对于每个c++实现,除了c++头文件,我们通常还提供了一个C头文件来允许。C文件的功能可用。
所以,我们的大多数文件看起来像这样:
foo.hpp:
class C {
int foo();
};
foo:
#ifdef __cplusplus
extern "C" {
typedef struct C C; // forward declarations
#else
class C;
#endif
int foo( C* ); // simply exposes a member function
C* utility_function( C* ); // some functionality *not* in foo.hpp
#ifdef __cplusplus
}
#endif
foo.cpp:
int C::foo() { /* implementation here...*/ }
extern "C"
int foo( C *p ) { return p->foo(); }
extern "C"
C* utility_function ( C* ) { /* implementation here...*/ }
问题:
假设我想给类添加一个命名空间,如下所示:
foo.hpp:
namespace NS {
class C {
int foo();
};
}
在c头文件中遵循的最佳方案是什么?
我考虑了几个选项,但我想要最优雅,安全,易于阅读。有标准的方法吗?
以下是我考虑过的选项:(为了简单起见,我使用了
extern "C"
结构)
- 选项1:通过在每个头文件中添加一些代码来欺骗编译器:
foo。
#ifdef __cplusplus
namespace NS { class C; } // forward declaration for C++
typedef NS::C NS_C;
#else
struct NS_C; // forward declaration for C
#endif
int foo( NS_C* );
NS_C* utility_function( NS_C* );
这给头文件增加了一些复杂性,但保持了实现的不变。
选项2:用c结构体包装命名空间:
保持标题简单,但使实现更复杂:
foo。
struct NS_C; // forward declaration of wrapper (both for C++ and C)
int foo( NS_C* );
NS_C* utility_function( NS_C* );
foo.cpp
namespace NS {
int C::foo() { /* same code here */ }
}
struct NS_C { /* the wrapper */
NS::C *ptr;
};
extern "C"
int foo( NS_C *p ) { return p->ptr->foo(); }
extern "C"
NS_C *utility_function( NS_C *src )
{
NS_C *out = malloc( sizeof( NS_C ) ); // one extra malloc for the wrapper here...
out->ptr = new NS::C( src->ptr );
...
}
这些是唯一的方案吗?这些方法是否有隐藏的缺点?
我发现以某种方式分解代码更容易,这样foo.h
只包含最少的c++细节,而foo.hpp
则负责粗砾位。
文件foo.h包含C API,不应该直接从c++代码中包含:
#ifndef NS_FOO_H_
#define NS_FOO_H_
// an incomplete structure type substitutes for NS::C in C contexts
#ifndef __cplusplus
typedef struct NS_C NS_C;
#endif
NS_C *NS_C_new(void);
void NS_C_hello(NS_C *c);
#endif
foo.hpp文件包含实际的c++ API,并负责将foo.h包含到c++文件中:
#ifndef NS_FOO_HPP_
#define NS_FOO_HPP_
namespace NS {
class C {
public:
C();
void hello();
};
}
// use the real declaration instead of the substitute
typedef NS::C NS_C;
extern "C" {
#include "foo.h"
}
#endif
实现文件foo.cpp是用c++编写的,因此包含foo.hpp,它也拉入foo.h:
#include "foo.hpp"
#include <cstdio>
using namespace NS;
C::C() {}
void C::hello() {
std::puts("hello world");
}
C *NS_C_new() {
return new C();
}
void NS_C_hello(C *c) {
c->hello();
}
如果你不想让c++代码可以使用C API,你可以把相关的部分从foo.hpp移到foo.cpp。
作为使用C API的一个例子,基本文件main.c:
#include "foo.h"
int main(void)
{
NS_C *c = NS_C_new();
NS_C_hello(c);
return 0;
}
这个例子已经在MinGW版本的gcc 4.6.1中使用以下编译器标志进行了测试:
g++ -std=c++98 -pedantic -Wall -Wextra -c foo.cpp
gcc -std=c99 -pedantic -Wall -Wextra -c main.c
g++ -o hello foo.o main.o
代码假设类型NS::C *
和struct NS_C *
具有兼容的表示和对齐要求,这实际上应该在任何地方都是如此,但据我所知,c++标准并不能保证(如果我在这里错了,请随时纠正我)。
从C语言的角度来看,代码实际上调用了未定义的行为,因为您在技术上通过不兼容类型的表达式调用函数,但这是没有包装结构和指针强制转换的互操作性的代价:
由于C不知道如何处理c++类指针,可移植的解决方案是使用void *
,您可能应该将其包装在结构中以恢复某种程度的类型安全:
typedef struct { void *ref; } NS_C_Handle;
这将在具有统一指针表示的平台上添加不必要的样板文件:
NS_C_Handle NS_C_new() {
NS_C_Handle handle = { new C() };
return handle;
}
void NS_C_hello(NS_C_Handle handle) {
C *c = static_cast<C *>(handle.ref);
c->hello();
}
另一方面,它会摆脱foo.h中的#ifndef __cplusplus
,所以它实际上并没有那么糟糕,如果你关心可移植性,我建议使用它。
我不完全明白你想做什么,但这可能会有所帮助:
如果你想让C仍然可以访问它,那么这样做:
void foo();
namespace ns {
using ::foo;
}
或者使用宏:
#ifdef __cplusplus
#define NS_START(n) namespace n {
#define NS_END }
#else
#define NS_START(n)
#define NS_END
#endif
NS_START(ns)
void foo();
NS_END
你的头乱糟糟的
你可能想要更像:
struct C;
#ifdef __cplusplus
extern "C" {
#else
typedef struct C C;
#endif
/* ... */
#ifdef __cplusplus
}
#endif
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 如何在 x86 处理器上实现"锁定添加"
- 为什么 MSVC _count_of实现将 0 添加到 sizeof 的结果中?
- C++二叉搜索树实现不会添加每个元素
- C QT4:添加使用QObject实现的插槽
- 如何向 C++ 中无法访问其实现的类添加新函数
- 如何在模板化列表类中实现高效添加C++
- 链接列表C 类,这两个添加节点实现之间的差异是什么?
- C 实现CNN错误添加符号:命令行中缺少DSO
- 如何添加实现C 接口的标签
- 如何在openssl中添加自己的rand_poll实现
- 实现矩阵类添加运算符重载奇怪的输出
- 使用C++实现 AVL 添加
- 将边添加到使用链表实现的有向图(邻接列表)C++
- 实现弹出最常添加的项目的堆栈
- 链接列表的添加、删除和打印实现
- 为带有C头文件的c++实现添加名称空间
- c++ - OOP实现链表,我不知道为什么添加到末尾不适合我,请建议
- 实现在单链表中添加和排序数据的函数(addsort)
- 您将如何重构这种多态设计,以便在添加新实现时使其更加灵活?