我正在开发一个C ++软件,但它通过包含通信协议的共享头文件与C app进行通信。由于C比C ++“更基本”,我总是需要在C代码中编写头文件(所以C ++也可以得到它);否则它不适用于第二个应用程序。
问题是我需要使用范围调整器,例如C ++ namespace
s - 它们在C中不存在。
什么是所有模拟C中namespace
功能的选项?
到目前为止,我见过的唯一可能是this SO question中显示的那个,但不幸的是答案不够明确,我当然想知道是否还有其他选择。我还尝试使用struct
来完成这项工作,但没有成功(至少考虑struct
与enum
执行者一起)。
答案 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
的结构成员,即使这些成员不应被翻译。
我认为做得对,实际上需要对编译器进行黑客攻击(有人这样做!:)。)。