gpt4 book ai didi

c++ - 将 namespace 添加到具有 C header 的 C++ 实现

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:41:19 26 4
gpt4 key购买 nike

我们有一个包含 C 和 C++ 代码的大型项目。

对于每个 C++ 实现,除了 C++ header 之外,我们通常还提供一个 C header 以允许功能也可用于 .c 文件。

所以,我们的大部分文件看起来像这样:

foo.hpp:

class C { 
int foo();
};

foo.h:

#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:通过在每个 header 中添加一些代码来欺骗编译器:

foo.h

#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* );

这增加了 header 的一些复杂性,但保持实现不变。


  • 选项 2:用 C 结构包装命名空间:

    保持 header 简单但使实现更加复杂:

foo.h

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 API 可用于 C++ 代码,您可以将相关部分从 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;
}

此示例已使用以下编译器标志使用 gcc 4.6.1 的 MinGW 版本进行了测试:

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++ - 将 namespace 添加到具有 C header 的 C++ 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8115970/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com