C++ DLL - 打破循环依赖项

C++ DLLs - breaking a circular dependency

本文关键字:循环 依赖 DLL C++      更新时间:2023-10-16

我正在努力重构一个 5.5k 行的 DLL C++,将其拆分为许多较小的 DLL。 不幸的是,很多代码都是捆绑在一起的,在拆分 GiantDLL 的过程中,我引入了几个循环引用。

更具体地说,

//In emergeOSLib.h:
DLL_EXPORT std::wstring ELGetProcessIDApp(DWORD processID, bool fullName);

//In a couple of functions in emergeOSLib.cpp:
ELMessageBox(GetDesktopWindow(), messageText, (WCHAR*)TEXT("Emerge Desktop"),                       ELMB_OK|ELMB_ICONERROR|ELMB_MODAL);

//In emergeUtilityLib.h:
DLL_EXPORT int ELMessageBox(HWND hwnd, std::wstring messageText, std::wstring messageTitle, DWORD msgFlags);

//In a function in emergeUtilityLib.cpp:
out << ELGetProcessIDApp(GetCurrentProcessId(), false) << TEXT(": ") << debugText << std::endl;

瞧,一个循环参考。我很确定还有更多,这只是我现在正在处理的问题。

我做了一些研究,发现前向声明似乎是要走的路:
解析标头包含循环依赖项
C++中的循环依赖

第二个链接甚至表明前向声明比#include更可取

我现在有两个问题。首先,如何向前声明另一个 DLL 中的函数?我的理解是编译器仍然无法找到前向声明的函数(因为它没有将第二个 DLL 编译为第一个 DLL 的一部分)并且会抱怨。其次,哪个通常被认为是更好的做法,#include声明还是前瞻性声明?

前向声明与#include只有助于声明循环。 但是你有定义循环性。 如果您将所有定义链接在一起,多通道链接器将解决此问题......但你不是。

基本上有两种方法可以做到这一点。 首先,您可以将ELGetProcessIDApp更改为实用程序库中的函数指针。 最初,它指向实用程序库中的某个存根值,当操作系统支持库加载时,它会使用特定于操作系统的实现覆盖该函数指针。

或者,可以使用链接器定义文件在 DLL 实际存在之前生成导入库。 这将通过创建一组循环依赖的 DLL 来解决链接器问题。 它可以工作,但它也可能导致令人惊讶的加载顺序效应,并表现出全局初始化排序惨败。

或者,只使用单个 DLL,并将重构限制为拆分源文件。

如何向前声明另一个 DLL 中的函数?

你不,它不起作用。您可以声明它,但链接阶段将失败。

相反,让 DLL A 正常链接到 DLL B,DLL B 在运行时从 DLL A 接收函数指针(或具有虚拟函数的对象)。