0

最近我们在我们的遗留代码中遇到了一个有趣的效果,即目前从 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?
  • 在头文件中创建“接口”的最佳方法是什么?我们曾经像上面那样做,但这似乎会导致一些问题。
  • 其他编译器(gcc / clang)是否表现出类似的行为?

感谢您的帮助托比亚斯

4

1 回答 1

0

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

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

于 2015-11-10T08:42:11.893 回答