将静态库封装在动态链接库(DLL)中
Encapsulating static libraries in dynamic-link libraries (DLL)
我正在努力提高对基本库链接、依赖项等的理解。我创建了一个包含三个项目的Visual Studio解决方案
-
静态
lib
使用/MTd
和单个类(Foo
),一种方法int GetNum() { return 5; }
-
使用
/MDd
与单个类(Bar
)共享dll
,一种方法int GetNum() { Foo f; return f.GetNum(); }
-
Win32控制台应用程序。称为
Bar b; std::cout << b.GetNum() << std::endl
当我试图构建它时,它抱怨找不到我的dll的关联库。做了一点研究,发现我需要将__declspec(dllexport)
添加到我的GetNum()
方法中,我会得到一个.lib
。凉的
下一个问题是控制台应用程序说它找不到Foo
的静态库。我把它添加到了我的参考资料中,所有的构建和运行都很好。
我的问题是,为什么我的exe需要了解Foo
?我想有效地将我所有的依赖项"烘焙"到dll中,这样我就可以共享它,链接到它,然后就可以开始了。
这不是语言的工作方式,还是我缺少的设置/模式?我的最终目标是能够构建一个dll来封装第三方.lib的使用,而不需要客户端应用程序担心添加对所有这些应用程序的引用。
更新
以下是大部分代码。
// ---------------------- Lib (e.g. Foo)
#pragma once
class MathLib
{
public:
MathLib(void);
~MathLib(void);
int GetNum() { return 83; }
};
// ---------------------- DLL (e.g. Bar)
#pragma once
#ifdef CONSOLETEST_EXPORT
#define CONSOLETEST_API __declspec(dllexport)
#else
#define CONSOLETEST_API __declspec(dllimport)
#endif
#include "MathLib.h"
class MathDll
{
public:
__declspec(dllexport) MathDll(void);
__declspec(dllexport) ~MathDll(void);
__declspec(dllexport) int GetNumFromDyn()
{
MathLib m;
return m.GetNum();
}
};
// ---------------------- exe
int _tmain(int argc, _TCHAR* argv[])
{
MathDll m;
std::cout << "num is " << m.GetNumFromDyn() << std::endl;
return 0;
}
使用C/C++,在头(例如h
、hpp
、hxx
、h++
等)和翻译单元之间(通常称为源,例如c
、cpp
、cxx
、c++
等)正确地构建代码非常重要,您应该不断思考什么属于它的接口(即,应该被消费者看到),什么属于其实现的(即,不应该被消费者看到)。
记住经验法则-任何标头中存在的所有符号都将被使用者看到(如果包括在内),因此,使用者需要在稍后的某个时间点在链接阶段进行解析!
这基本上就是你在玩具示例中发生的事情。因此,让我们使用一个简单的规则来解决这个问题,您应该记住:将尽可能多的信息放入翻译单元中,即保持标题最小化。现在让我们用你的例子来展示它是如何工作的:
MathLib.hpp
:
#pragma once
class MathLib {
public:
MathLib();
~MathLib();
int GetNum();
};
MathLib.cpp
:
#include "MathLib.hpp"
MathLib::MathLib() {}
MathLib::~MathLib() {}
int MathLib::GetNum() { return 83; }
现在将MathLib.cpp
构建为静态库。
MathDll.hpp
:
#pragma once
#ifdef CONSOLETEST_EXPORT
# define CONSOLETEST_API __declspec(dllexport)
#else
# define CONSOLETEST_API __declspec(dllimport)
#endif
class CONSOLETEST_API MathDll {
public:
MathDll();
~MathDll();
int GetNumFromDyn();
};
MathDll.cpp
:
#include "MathDll.hpp"
#include "MathLib.hpp"
MathDll::MathDll() {}
MathDll::~MathDll() {}
int MathDll::GetNumFromDyn() {
MathLib m;
return m.GetNum();
}
现在将MathDll.cpp
构建为动态链接库(DLL),并且不要忘记在其构建过程中添加定义CONSOLETEST_EXPORT
,因此CONSOLETEST_API
就是__declspec(dllexport)
,因此,将为DLL生成带有导出符号(即MathDll
类及其方法)的导入库。在MSVC上,您可以通过将/DCONSOLETEST_API
添加到编译器的调用中来实现这一点。最后,在构建这个DLL时,一定要将它与以前构建的静态库MathLib.lib
链接起来。
注意:最好像上面使用class CONSOLETEST_API MathDll
那样导出整个类,而不是单独导出所有方法。
main.cpp
:
#include "MathDll.hpp"
#include <iostream>
int _tmain(int argc, _TCHAR* argv[]) {
MathDll m;
std::cout << "num is " << m.GetNumFromDyn() << std::endl;
return 0;
}
现在将main.cpp
构建为控制台应用程序,并且仅将其与以前构建的DLL导入库MathDll.lib
链接。
注意这个问题是如何解决的,因为我已经从main.cpp
中消除了对MathLib
(通过MathDll.hpp
)的传递依赖,因为现在#include "MathLib.hpp"
的包含是在翻译单元MathDll.cpp
中完成的(因为根据上面的规则,它实际上只需要在那里),因此它被构建到二进制工件(在这种情况下是DLL)中,而不存在于它的接口中。
了解所有这些对于使用C/C++进行正确的本机软件开发非常重要,所以提前问这个问题真的很好。我经常遇到不知道/不理解这一点的人,这对他们(业余爱好者)和我们来说都是一场噩梦,当我们不得不处理他们编写的糟糕软件时。。。
当MathLib是MathDll类的一部分时,请考虑这种情况。
//MathDll.h
#include "MathLib.h"
class MathDll
{
private:
MathLib m;
public:
__declspec(dllexport) MathDll(void);
__declspec(dllexport) ~MathDll(void);
__declspec(dllexport) int GetNumFromDyn()
{
return m.GetNum();
}
};
您现在必须将MathLib.h包含到您的MathDll.h中,它也会传播到控制台应用程序。
你可以避免这种情况。。。
通过使用PIMPL习惯用法将所有内容封装到DLL中。在头中提供类MathLib的正向声明,并在Dll中提供隐藏的其余实现。您也可以考虑导出整个类。
//------------MathDll.h
// we do not include "MathLib.h" here. include it in the MathDll.cpp only
class MathLib;
class __declspec(dllexport) MathDll
{
private:
MathLib* m;
public:
MathDll(void);
~MathDll(void);
int GetNumFromDyn();
};
//--------------MathDll.cpp
#include "MathLib.h"
#include "MathDll.h"
MathDll::MathDll(void)
{
m = new MathLib();
}
MathDll::~MathDll(void)
{
delete m;
}
int MathDll::GetNumFromDyn()
{
return m->GetNum();
}
- g++用户定义的动态链接库上的全局new和delete运算符
- 如何使用C++导出制作动态链接库
- 动态链接库中C++回调函数
- 调用函数一次用于动态链接库,一次从可执行文件调用函数
- 将函数传递给动态链接库
- 如何将动态链接库与CMake一起使用
- JNI 不满意链接错误: 动态链接库 (DLL) 初始化例程失败
- Ordinal 3283不能位于动态链接库libmysql.dll中
- SDL_AudioStreamFlush无法在动态链接库SDL2_mixer.dll中找到
- 在 Win 7 SP 中创建 exe vcxproj:过程入口点<function>无法位于动态链接库中KERNAL32.dll
- 多个动态链接库(DLL)是否可以从静态库(LIB)共享线程本地存储
- 将静态库封装在动态链接库(DLL)中
- 动态链接库 (.dll) 中的对象是否跨进程共享
- Windows(dll)中动态链接库的属性
- 在动态链接库libstdc++-6.dll中找不到过程入口点_gxx_personality_v0.错误
- 过程入口点无法在动态链接库中找到-查找错误的DLL
- 在动态链接库libstdc++-6.dll中无法找到过程入口点_gxx_personality_v0
- 程序入口点__gxx_personality_v0无法在动态链接库libstdc++-6.dll中找到
- 一个文件可以同时是可执行文件(EXE)和动态链接库(DLL)吗?
- 过程入口点EnumerateLoadedModulesW64不能位于动态链接库dbghelp.dll中