导入到命名空间和全局命名空间的C函数之间的冲突

4

在我的代码中,我使用命名空间引入OpenSSL头文件,如下所示:

#include <cstdio>
namespace OpenSSL {
    #include <openssl/ssl.h>
    #include <openssl/err.h>
}

但是我刚刚发现,如果在使用具有OpenSSL支持的Boost ASIO时尝试这样做,似乎会导致一些问题爆炸,因为它似乎将OpenSSL符号带入全局命名空间。我能做些什么吗?还是我必须让所有OpenSSL库的符号保留在全局命名空间中?

我刚才想到了在包括头文件后,在受影响的文件中尝试“using namespace OpenSSL”,但不幸的是,这会导致错误,例如:

/usr/include/openssl/x509v3.h:83:13: error: reference to ‘v3_ext_ctx’ is ambiguous
/usr/include/openssl/x509v3.h:71:8: error: candidates are: struct v3_ext_ctx
/usr/include/openssl/ossl_typ.h:160:16: error:                 struct OpenSSL::v3_ext_ctx

注意,OpenSSL是一个C库,而不是C++库,因此原始函数在未被引入C++编译单元之前不属于任何命名空间。斯特劳斯特鲁普在他的书《C++程序设计语言》特别版中推荐了我的技术。从第9.5节“建议”中可以看到:“[8] 在命名空间中包含C头文件以避免全局名称; §8.2.9.1, §9.2.2。”


你能详细解释一下那些“爆炸性的东西”吗?我创建的一个非常简短的样例似乎可以编译,但也许我漏掉了什么。 - Michał Górny
如果在C++文件中包含上述文件,然后跟着写上"#include <boost/asio.hpp>",你会看到问题。会产生很多错误消息,具体的集合取决于是否在asio之前或之后包含上述头文件。如果您在复制问题时遇到困难,请随时直接给我发电子邮件(我的地址在我的用户页面上)。 - cjs
我猜你指的是8.2.9(没有8.9.2),但它没有说清楚。 - Jonathan Wakely
2个回答

3
一般来说,这样做是行不通的,也不应该这样做。Boost.Asio可以(并且应该)期望能够将OpenSSL类型引用为全局命名空间中的类型,例如通过引用::buf_mem_st,但由于您已将其声明为OpenSSL::buf_mem_st,因此失败了。
此外,如果<openssl/ssl.h>包含另一个头文件,比如<stddef.h>(它确实包含),那么会发生什么情况?然后将size_t定义为OpenSSL::size_t。任何稍后包含<stddef.h>的代码都不会再次包含它,因为它有包含防护宏,现在您的程序永远无法使用::size_t,因为它被错误地声明为OpenSSL::size_t - 对于许多实现,如果在openssl包含之后包含,这将破坏大部分C++标准库。在您的情况下,可能<cstdio>已经包含了<stddef.h>,但对于任何由OpenSSL头文件包含的标准(即非OpenSSL)头文件,例如<sys/types.h>,都适用。您的程序定义了OpenSSL::pid_tOpenSSL::off_tOpenSSL::timeval等。
唯一解决该问题的方法是在进行命名空间包含之前包含每个标准C头文件,以便如果OpenSSL尝试再次包含该头文件,则已在全局命名空间中正确地包含。即使这样,其他引用OpenSSL的头文件(如Boost.Asio)也可能会出现问题。
坚决说不。

回答,“另外,如果<openssl/ssl.h>包含另一个头文件会发生什么...”:这就是为什么我使用了_#include <cstdio>。等等,我突然想到我可能需要类似于stddef.h和类似文件的#include <cstdio>_等价物。嗯,感谢您的见解。 - cjs
是的,您需要为openssl库可能包含的每个头文件都需要这个(现在或将来的任何版本!) - Jonathan Wakely
我认为你有点反应过度了。只要所有头文件正确引用它们的依赖关系,并且“命名空间C include”放在最后,就不应该有太多问题。当然,只要没有其他C++包含引用OpenSSL,这才有效。由于boost.asio会这样做,因此甚至尝试对其进行命名空间也没有意义,因为它仍将命中全局命名空间。 - Michał Górny
@MichałGórny,这只有在没有其他C++包含引用OpenSSL的情况下才有效,这正是OP的问题。实际上,您重复了我说的话:在openssl之前包含所有标头,如果其他任何内容引用openssl,则它将无法工作。 - Jonathan Wakely

0
问题似乎是这样的:OpenSSL的符号只能被引入一次;第二个 #include 不会起作用,因为有 include guards。这意味着它们只能被引入到编译单元中的一个命名空间中。
因此,如果您要在编译单元中使用 Boost.ASIO,需要将它们引入到全局命名空间中,您可以在 #include 之前自己将它们引入到全局命名空间中,或者让 #include 自动引入到全局命名空间中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接