如何在C代码中模拟C ++命名空间功能?

时间:2014-08-21 17:27:08

标签: c++ c namespaces

我正在开发一个C ++软件,但它通过包含通信协议的共享头文件与C app进行通信。由于C比C ++“更基本”,我总是需要在C代码中编写头文件(所以C ++也可以得到它);否则它不适用于第二个应用程序。

问题是我需要使用范围调整器,例如C ++ namespace s - 它们在C中不存在。

什么是所有模拟C中namespace功能的选项?

到目前为止,我见过的唯一可能是this SO question中显示的那个,但不幸的是答案不够明确,我当然想知道是否还有其他选择。我还尝试使用struct来完成这项工作,但没有成功(至少考虑structenum执行者一起)。

4 个答案:

答案 0 :(得分:9)

您可以隐藏在定义级别使用static从模块导出的所有导出函数,这样就不会在全局空间中放置任何名称,而是将它们放在唯一的结构中模块提供的东西。

E.g。 foo.h中:

struct ns_t {
    int (*a)(int, int);
    void (*b)(float, float);
};
extern struct ns_t ns;

foo.c的:

#include "foo.h"

static int a(int x, int y) {
    ...
}

static void b(float x, float y) {
    ...
}

struct ns_t ns = { .a = a, .b = b };

bar.c:

#include "foo.h"
....
ns.b(4.5, 6.8);
....

答案 1 :(得分:6)

根本不要在C中模拟名称空间,请按照C方式:

  • 使用前缀而不是名称空间。
  • 使用后缀而不是重载。
    • (可选)使用带_Generic的宏来模拟参数类型的重载。

您的包含文件应该在C ++的内部详细命名空间中定义那些C函数(由于C链接,不会更改函数实际标识),然后从C函数的C函数中删除前缀和后缀。<登记/> 它看起来像这样:

#ifndef MY_HEADER_GUARD_unique_suffix
#define MY_HEADER_GUARD_unique_suffix
#ifdef __cplusplus
namespace my_module {
namespace detail {
extern "C" {
#endif
// Defines for common structs and functions here
// Also inline functions written in the common intersection of C and C++
#ifdef __cplusplus
}
}
using init = detail::my_module_init;
using close = detail::my_module_close;
}
#endif
#endif

您可能还希望将成员函数添加到C ++接口的某些C结构中,这些结构可能是委托给共享函数的内联函数。

答案 2 :(得分:2)

首先,首先使用命名空间为所有导出的符号(包括预处理器定义和枚举成员的名称)添加前缀。例如,您可以使用函数声明

void foo_bar_baz(void);

在C ++方面,这些需要包装在extern "C" { … }中,然后应该使用正确的命名空间进行注册。假设C ++ 11,在函数的情况下,这应该像

一样简单
namespace foo {
    namespace bar {
        constexpr auto baz = foo_bar_baz;
    }
}

在C方面,您可以定义缩写名称,如

#define baz foo_bar_baz

使用特定于编译器的属性来注册别名或添加常量声明

static void (*const baz)(void) = foo_bar_baz;

这很好用,因为调用函数实际上是使用函数指针(而不是指示符)。

您可以根据需要将所有内容放入一个包含#ifdef的单个标头中,或者您可以为C ++提供类似foo/bar.hxx的小包装器,并为{C}提供缩短的C名称foo/bar-import.h。 {1}}包含实际的前缀声明,并将包含在其他标题中。

答案 3 :(得分:0)

我一直在研究一个非常依赖命名空间的系统(构建类型插入的那些),从我看C,你可以不完美地模拟C++命名空间(无论如何在Linux上)通过收集静态链接的导出符号,在其前面添加objcopy,然后对标题代码(不包括)进行宏转换,以匹配前缀符号集。

问题在于宏不尊重范围,因此假设您的库导出void foo()(或简称为foo) - 并且您将其添加到{{1}之前然后,将mylib_foo翻译成foo的宏也会不加区别地翻译名为mylib_foo的结构成员,即使这些成员不应被翻译。

我认为做得对,实际上需要对编译器进行黑客攻击(有人这样做!:)。)。