如何在 VC++ 2015 全程序优化存在的情况下正确暴露来自 dll 的接口
How to correctly expose interfaces from a dll in the presence of VC++ 2015 whole program optimization
最近我们在目前从 VS2010 移植到 VS2015 的遗留代码中遇到了一个有趣的效果。不幸的是,我无法创建一个小示例来展示这种效果,但我会尽可能准确地描述它。
我们有 2 个 dll(我称它们为 dll A 和 dll B)。 dll A 的项目定义接口 IFoo 和派生接口 IFxFoo
class __declspec(novtable) IFoo {
public:
virtual int GetType() = 0;
virtual ~IFoo() {}
};
class __declspec(novtable) IFxFoo : public IFoo {
public:
virtual int GetSlot() = 0;
};
在dll B中,两个接口都用到了。
class CBImpl : public IFxFoo {
public:
...
void processFoo(IFoo* f) {
...
if (f->GetType() == IFXFOO) {
IFxFoo* fx = static_cast<IFxFoo>(f); //downcast
fill(fx);
}
}
void fill(IFxFoo* fx) {
m_slot = fx->GetSlot();
}
private:
int m_slot;
};
processFoo() 将使用不同的 IFoo 实现调用。一些来自 dll A,一些来自 dll B。
现在发生的事情如下:
- 如果我们在编译 dll B 时打开了整个程序优化,函数 fill() 中对虚函数 GetSlot() 的调用将被 Visual C++ 去虚拟化。这导致我们的程序崩溃。
如果我们可以解决这个问题
- 整个程序优化
- 填充优化轮
- 或者用 __declspec(dllimport) / __declspec(dllexport)
标记我们的接口
我现在的问题是:
- 我们的假设是否正确,因为优化器在 dll B 中只看到了 IFxFoo 的一个实现,并假设这是唯一一个,因为 IFxFoo 没有被标记为来自不同的 dll?
- 在头文件中创建 "interfaces" 的最佳方法是什么?我们曾经像上面那样做,但这似乎会导致一些问题。
- 其他编译器 (gcc / clang) 是否表现出类似的行为?
感谢您的帮助
托拜厄斯
使用 LTO 会导致编译器对它能够看到完整调用图的任何函数进行大幅调整。
您所看到的是预期的,并且在需要从单独模块使用的函数上使用 __declspec(dllexport)
或 extern
或将它们显式声明为 DLL .def 文件的一部分是解决问题的预期方法,因为编译器将不再将函数视为仅供内部使用。
最近我们在目前从 VS2010 移植到 VS2015 的遗留代码中遇到了一个有趣的效果。不幸的是,我无法创建一个小示例来展示这种效果,但我会尽可能准确地描述它。
我们有 2 个 dll(我称它们为 dll A 和 dll B)。 dll A 的项目定义接口 IFoo 和派生接口 IFxFoo
class __declspec(novtable) IFoo {
public:
virtual int GetType() = 0;
virtual ~IFoo() {}
};
class __declspec(novtable) IFxFoo : public IFoo {
public:
virtual int GetSlot() = 0;
};
在dll B中,两个接口都用到了。
class CBImpl : public IFxFoo {
public:
...
void processFoo(IFoo* f) {
...
if (f->GetType() == IFXFOO) {
IFxFoo* fx = static_cast<IFxFoo>(f); //downcast
fill(fx);
}
}
void fill(IFxFoo* fx) {
m_slot = fx->GetSlot();
}
private:
int m_slot;
};
processFoo() 将使用不同的 IFoo 实现调用。一些来自 dll A,一些来自 dll B。
现在发生的事情如下: - 如果我们在编译 dll B 时打开了整个程序优化,函数 fill() 中对虚函数 GetSlot() 的调用将被 Visual C++ 去虚拟化。这导致我们的程序崩溃。 如果我们可以解决这个问题
- 整个程序优化
- 填充优化轮
- 或者用 __declspec(dllimport) / __declspec(dllexport) 标记我们的接口
我现在的问题是:
- 我们的假设是否正确,因为优化器在 dll B 中只看到了 IFxFoo 的一个实现,并假设这是唯一一个,因为 IFxFoo 没有被标记为来自不同的 dll?
- 在头文件中创建 "interfaces" 的最佳方法是什么?我们曾经像上面那样做,但这似乎会导致一些问题。
- 其他编译器 (gcc / clang) 是否表现出类似的行为?
感谢您的帮助 托拜厄斯
使用 LTO 会导致编译器对它能够看到完整调用图的任何函数进行大幅调整。
您所看到的是预期的,并且在需要从单独模块使用的函数上使用 __declspec(dllexport)
或 extern
或将它们显式声明为 DLL .def 文件的一部分是解决问题的预期方法,因为编译器将不再将函数视为仅供内部使用。