如何在VC++2015全程序优化的情况下正确地从dll中公开接口



最近,我们在当前从VS2010移植到VS2015的遗留代码中遇到了一个有趣的效果。不幸的是,我无法创建一个小例子来显示这种效果,但我会尽可能准确地描述它。

我们有两个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的实现,并假设这是唯一的实现,因为IFxFoos没有被标记为来自不同的dll
  • 在头文件中创建"接口"的最佳方法是什么?我们过去常常这样做,但这似乎导致了一些问题
  • 其他编译器(gcc/clang)是否表现出类似的行为

谢谢你的帮助Tobias

使用LTO会导致编译器对任何能够看到完整调用图的函数进行剧烈调整。

您所看到的是预期的,在需要从单独模块使用的函数上使用__declspec(dllexport)extern,或将它们明确声明为DLL的一部分。def文件是解决问题的预期方法,因为编译器不再认为这些函数仅为内部函数。

最新更新