利用条件编译 (#ifdef ALGLIB_EXPORTS
) 和 DLLAPI
宏的设置,可以让同一个头文件既适用于 DLL 库的编译,也适用于依赖该 DLL 的客户端代码。具体来说:
-
在编译 DLL 时:头文件中的
DLLAPI
通过条件编译定义为__declspec(dllexport)
,标记这些函数是要被 DLL 导出的。这意味着,当编译 DLL 时,这些函数会包含在 DLL 的导出表中,可以被外部程序使用。 -
在使用 DLL 的客户端代码中:头文件中的
DLLAPI
会被定义为__declspec(dllimport)
,标记这些函数是从 DLL 中导入的。这告诉编译器,这些函数的实际实现位于 DLL 中,而不是当前客户端的代码里。
举个例子
假设你有一个名为 mylib
的库,这个库需要生成一个 DLL 文件 mylib.dll
,并且有一个头文件 mylib.h
,头文件内容如下:
// mylib.h
#pragma once
#ifdef ALGLIB_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif
DLLAPI void DFT_fc_Direct(/* 参数列表 */);
场景1:编译 DLL 时
-
在 DLL 项目中(比如
mylib.dll
项目),编译器设置了ALGLIB_EXPORTS
宏(可以在项目设置中定义)。 -
这样,头文件中的
DLLAPI
被替换为__declspec(dllexport)
,即:__declspec(dllexport) void DFT_fc_Direct(/* 参数列表 */);
-
__declspec(dllexport)
告诉编译器将DFT_fc_Direct
函数导出到mylib.dll
中,使其成为 DLL 的一部分。其他程序可以通过该 DLL 调用DFT_fc_Direct
。
场景2:在客户端代码中使用 DLL
-
在依赖
mylib.dll
的客户端项目中(即一个使用mylib.dll
的应用程序项目),ALGLIB_EXPORTS
宏未定义。 -
这样,头文件中的
DLLAPI
被替换为__declspec(dllimport)
,即:__declspec(dllimport) void DFT_fc_Direct(/* 参数列表 */);
-
__declspec(dllimport)
告诉编译器,DFT_fc_Direct
的实现位于mylib.dll
中,而不在当前的客户端代码中。当客户端代码调用DFT_fc_Direct
时,链接器会从mylib.dll
中导入它的实现。
为什么这样设计?
这种设计的目的是为了复用头文件。同一个头文件 mylib.h
可以用于:
- 编译
mylib.dll
自身,导出函数。 - 在客户端项目中引用
mylib.dll
,导入函数。
这种复用避免了编写两个不同的头文件(一个用于 DLL 编译,另一个用于客户端导入),简化了项目管理。
总结
- 同一个头文件通过条件编译控制,可以在 DLL 编译时导出函数,也可以在客户端使用时导入函数。
__declspec(dllexport)
在 DLL 编译时使用,导出函数;__declspec(dllimport)
在客户端使用时使用,导入函数。- 这样做的好处是保持头文件的通用性和一致性,无论是编译 DLL 还是在客户端项目中使用,都可以直接引用这个头文件。