我正在尝试使用不使用命名空间的第三方C ++库,并导致符号冲突。冲突的符号用于我的代码没有使用的类,所以我考虑为第三方库创建自定义头文件,其中类声明仅包括我的代码使用的公共成员,而忽略使用冲突类的任何成员。基本上创建一个界面。
我有三个问题:
如果对.obj文件的编译有效,当我进行链接时,这种技术是否会导致符号冲突?
如果这不是问题,那么不同的类声明会在链接时引起问题吗?例如,链接器是否验证每个.obj文件使用的类的声明是否具有相同数量的成员?
如果这些都不是问题而且我能够链接.obj文件,它会在调用方法时引起问题吗?我不确切知道C ++是如何工作的,但如果它使用索引指向类方法,并且那些索引从一个.obj文件到另一个不同,我猜这种方法会在运行时爆炸。 / p>
答案 0 :(得分:1)
理论上,你需要相同的声明才能发挥作用。
在实践中,您肯定需要确保您的声明包含:
您也需要按正确的声明顺序完成所有这些。
您可能会伪造数据成员,但需要确保放入具有相同大小的存根。
如果你不这样做,你就不会得到相同的对象布局,即使链接有效,它也会在运行时严重失败。
如果你这样做,它对我来说似乎仍然存在风险,并且最糟糕的情况似乎可行,但运行时间很短。
“如果它使用索引”:在某种程度上,虚拟函数的确切工作方式是实现定义的,但通常它会使用虚函数表中的索引。
您可以做的是:
答案 1 :(得分:1)
为了解释的目的,接下来是简化的解释。
c ++允许您使用声明的函数。你所做的是将多个定义放在多个翻译单元的单个声明中。如果在头文件中公开类声明,则编译器会在每个转换单元中看到它,包括头文件。
因此,您自己的类函数必须完全按照它们声明的方式定义(相同的函数名称相同的参数)。 如果没有调用该函数,则允许不定义它,因为编译器不知道它是否可以在另一个转换单元中定义。
编译会导致在目标代码中为每个定义的函数(符号)创建标签。另一方面,为每个引用的符号(调用站点,变量使用)创建一个未解析的标签。
因此,如果您遵循此规则,您应该达到代码编译但无法链接的程度。链接器是将每个转换单元中定义的符号映射到符号引用的工具。
如果链接在一起的目标文件对同一个函数有多个定义,则链接器无法创建完全匹配,因此无法链接。
在实践中,您最有可能想要提供一个库并享受使用自己的类而不必担心用户可能定义的内容。尽管程序员特别注意将事物放入命名空间,但两个用户仍可能为命名空间选择相同的名称。这将导致链接失败,因为编译器公开了符号并且应该链接它们。
gcc
添加了一个显式标记符号的属性,不应该向链接器公开。 (称为attribute hidden (see this SO question))
这使得可以具有相同名称的类的多个定义。
为了使它能够跨编译单元工作,你必须确保类声明不会在接口头中公开,因为它可能导致多个不匹配的声明。
答案 2 :(得分:1)
我建议使用包装器封装第三方库。
Wrapper.h
#ifndef WRAPPER_H_
#define WRAPPER_H_
#include <memory>
class third_party;
class Wrapper
{
public:
void wrappedFunction();
Wrapper();
private:
// A better choice would be a unique_ptr but g++ and clang++ failed to
// compile due to "incomplete type" which is the whole point
std::shared_ptr<third_party> wrapped;
};
#endif
Wrapper.cpp
#include "Wrapper.h"
#include <third_party.h>
void Wrapper::wrappedFunction()
{
wrapped->command();
}
Wrapper::Wrapper():wrapped{std::make_shared<third_party>()}
{
}
此处解释了unique_ptr不起作用的原因:std::unique_ptr with an incomplete type won't compile
答案 3 :(得分:1)
您可以使用巧妙的技巧将整个库移动到命名空间中。所有import指令都将相关代码复制到当前的“翻译单元”(当前代码的花哨名称)。你可以利用这个
我从用户JohnB的另一个答案中大量借用,后来被他删除了。
// my_thirdparty.h
namespace ThirdParty {
#include "thirdparty.h"
//... Include all the headers here that you need to use for thirdparty.
}
// my_thirdparty.cpp / .cc
namespace ThirdParty {
#include "thirdparty.cpp"
//... Put all .cpp files in here that are currently in your project
}
最后,从项目中删除第三方库中的所有.cpp文件。只编译my_thirdparty.cpp。
警告:如果您从单个my_thirdparty.cpp中包含许多库文件,则可能会因个别.cpp文件之间的交互而引入编译器问题。诸如include命名空间或错误的define / include指令之类的事情可能导致这种情况。解析或创建多个my_thirdparty.cpp文件,在它们之间拆分库。