如何不使用C头声明污染全局命名空间? 我试图用C++来封装C库,使之成为一个现代的、高级的、惯用的C++库。我想做的是,让C对象完全不透明和/或直接从C++代码中不可用,并用更高级别的替代来替换/替换它们。 我面临的问题很简单:我想把C报头只包含在C++源中,这样当包含的C++头不会包含C报头的声明,也就是说,它不会污染全局命名空间。
但是看起来头文件和源文件的正确分离不允许我这样做。下面是我的问题的一个非常愚蠢的版本,评论会告诉你剩下的:如何不使用C头声明污染全局命名空间? 我试图用C++来封装C库,使之成为一个现代的、高级的、惯用的C++库。我想做的是,让C对象完全不透明和/或直接从C++代码中不可用,并用更高级别的替代来替换/替换它们。 我面临的问题很简单:我想把C报头只包含在C++源中,这样当包含的C++头不会包含C报头的声明,也就是说,它不会污染全局命名空间。,c++,c++11,header,namespaces,wrapper,C++,C++11,Header,Namespaces,Wrapper,但是看起来头文件和源文件的正确分离不允许我这样做。下面是我的问题的一个非常愚蠢的版本,评论会告诉你剩下的: my_header.h: typedef枚举 { my_Consts_ALPHA=/*一些特殊值*/,, my_Consts_BETA=/*其他特殊值*/,, }我的妻子; 类型定义结构 { //成员们。。。 }我的类型; 无效的 my_Type_方法(my_Type*const, 我的名字); my_header.hpp: 名称空间我的 { enum class Consts;//
my_header.h:
typedef枚举
{
my_Consts_ALPHA=/*一些特殊值*/,,
my_Consts_BETA=/*其他特殊值*/,,
}我的妻子;
类型定义结构
{
//成员们。。。
}我的类型;
无效的
my_Type_方法(my_Type*const,
我的名字);
my_header.hpp:
名称空间我的
{
enum class Consts;//我不确定它的语言是否合法,但我认为extern“C”
只是用来解开函数的,所以只要将它们保存在.cpp文件中,就可以解决这个问题
这有点亵渎,但它似乎适用于GCC4.3.5。它表明您可以使用C函数,同时也可以将它们隐藏在命名空间中
我没有为继承struct而烦恼,但它可能会工作。我不知道您是否可以实现enum类
foo.h
#ifndef foo_H
#define foo_H
typedef enum {
ALPHA,
BETA
} enum_t;
typedef struct
{
int i;
} struct_t;
void printit(struct_t print_me);
#endif // foo_H
foo.c
#include <stdio.h>
#include "foo.h"
void printit (struct_t print_me)
{
printf ("Hello World %d!\n", print_me.i);
}
bar.cpp
#ifndef bar_HPP
#define bar_HPP
namespace _foo {
// Don't need extern "C" since we're not using functions
#include "foo.h"
}
struct based_on_struct_t // : public _foo:struct_t // Do you really have to derive? It might be possible, but it's ugly
{
_foo::struct_t i;
double j;
based_on_struct_t (int _i, double _j) : j(_j) { i.i = _i; }
void print(void); // Gonna call printit, MUST be in .cpp
};
#endif // bar_HPP
namespace _foo{
extern "C" {
#include "foo.h"
}
}
#include "bar.hpp"
#include <stdio.h>
void based_on_struct_t::print (void) {
// Call the old version...
printit(i);
// And do new crap
printf ("Goodbye World %d %f\n", i.i, j);
}
#include "bar.hpp"
int main (void) {
based_on_struct_t B(10, .1);
B.print();
return 0;
}
演示
$ gcc foo.c -c -O3
$ g++ foo.o bar.cpp driver.cpp
$ ./a.out
Hello World 10!
Goodbye World 10 0.100000
$
如果编写高级、通用C++包装的思想是为了带来安全、自动内存管理和方便的C++类型,如 STD::STIN < /代码>,我只将C标题包含到CPP文件>
提供干净的习惯C++接口,只在实现中使用C库。
<>不要害怕编写C函数到C++和后端的两个实用函数,如果C++类应该保存C特定数据,并且不可能用C++模拟替换它,使用它来保持干净的接口。
我不会因为这种包装而担心性能,直到我在探查器日志的顶部看到它。在大多数情况下,它不是一个瓶颈
同样,拆分接口和实现通常是一种胜利
更新
最初,我在考虑C项目的具体C++接口,而不是C语言库中的通用C++包装器。
将
extern“C”
包装到名称空间中的解决方案在我看来是正确的(参见C++11标准的§7.5)。但是,我从未在野外见过这种技术
您可以更进一步,添加嵌套的detail
命名空间,以避免污染带有C类型的my
命名空间。此技巧在仅标头的库中很流行:
namespace my
{
namespace detail
{
extern "C"
{
#include "my_header.h"
}
}
enum class Consts
{
ALPHA = detail::my_Consts_ALPHA,
BETA = detail::my_Consts_BETA,
};
class Type : public detail::my_Type
{
public:
void
method(Consts constant);
};
}
请注意,在链接静态库时,不能使C函数完全不透明,也不能将它们包装到单个名称空间中。它们有外部链接,对名称空间一无所知
namespace A {
extern "C" void my_Type_method(my_Type *const, my_Enum);
}
namespace B {
extern "C" void my_Type_method(my_Type *const, my_Enum);
}
extern "C" void my_Type_method(my_Type *const, my_Enum);
基本上,所有这些声明都引用同一个C函数。由于C不支持名称空间和重载,链接器通常使用函数名作为唯一标识符(甚至忽略参数类型)
无论如何,这种方法将有助于避免意外访问C接口。在问题的评论中讽刺地建议,应该使用#在命名空间
中包含C头。确认了它实际上是一个工作解决方案,现在我在自己的设置中测试了它,幸运的是它正在工作很好
(虽然我不知道这是否是特定于实现的,但我在g++
和clangg++
上都进行了测试,并且运行正常。)
它并没有解决不透明的问题,但至少它使直接访问原始C数据变得有点困难,因为它现在生活在一个单独的命名空间中,因此用户不能意外地访问,而是自愿地访问
因此,my_header.hpp
应该如下所示:
名称空间我的
{
外部“C”
{
#包括“my_header.h”
}
枚举类常量
{
α=我的常数α,
β=我的常数β,
};
类类型:公共my_类型
{
公众:
无效的
方法(常数);
};
}
因此,无论my_header.hpp
是#include
'd,用户只能访问C值,如下所示:
my::my_Consts\u ALPHA//包装的值为=>my::Consts::ALPHA
my::my_Type//包装的值为=>my::Type
my::my_Type_method(t,…)//包装的值为=>t.method(…)
名称空间m00{#包括“myheader.h”}
(是的,这是部分讽刺)使用如何?您正试图在cpp库的接口中重用c库中的类型。如果是这样,您显然无法隐藏这些类型。Pimpl是一个糟糕的主意。它会杀死大量优化。@PeterVaro我错误地记得,使用作用域枚举时,您无法获得基础值句号。(旁白:我能找到的作用域枚举和只包含非作用域枚举定义的结构在成本上的唯一区别是类型安全方面——我假设的是您关心的问题,结构类型的变量通常是一个字节,除了它作为基类包含在另一个类中之外,在这种情况下,您只能使用枚举的实例化…)谢谢你的回复,虽然你只是总结了我在这里做的事情。但是当我试着的时候,我碰到了一些墙,其中一个是上面的问题,不幸的是你没有回答。“PeterVaro hmm.……如果你不在C++界面中使用C结构和枚举,你就不需要把<代码> .h <代码>包含到<代码>