在动态链接库(DLL)中封装静态库

 流纸香p_455 发布于 2023-02-06 15:40

我正在努力增加对基本库链接,依赖关系等的理解.我创建了一个包含三个项目的Visual Studio解决方案

    静态lib使用/MTd单个类(Foo),一个方法int GetNum() { return 5; }

    与单个class()共享dll使用,一个方法/MDdBarint GetNum() { Foo f; return f.GetNum(); }

    Win32控制台应用程序.那叫Bar b; std::cout << b.GetNum() << std::endl

当我试图构建它时,它抱怨它无法找到我的dll的关联库.做了一点研究,看到我需要添加__declspec(dllexport)到我的GetNum()方法中,我会得到一个.lib.凉.

接下来是控制台应用程序说它无法找到静态库Foo.我把它添加到我的引用中,它都构建并运行良好.

我的问题是 - 为什么我的exe需要知道什么Foo?我想在所有依赖项中有效地"烘焙"到dll中,所以我可以分享它,链接到它,并且很高兴去.

这不是语言的工作方式或我缺少的设置/模式吗?我的最终目标是能够构建一个封装第三方.lib的使用的DLL,而不是让客户端应用程序需要担心添加对所有这些的引用.

更新

这是大部分代码.

    // ---------------------- 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;
    }

Alexander Sh.. 9

用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)导入库.MathDllDLL.在MSVC上,您可以通过添加/DCONSOLETEST_API到编译器的调用来实现此目的.最后,在构建这个DLL时,肯定会将它与之前构建的静态库链接起来MathLib.lib.

注意:最好像我上面那样导出整个类class CONSOLETEST_API MathDll,而不是单独导出所有方法.

main.cpp:

#include "MathDll.hpp"

#include 

int _tmain(int argc, _TCHAR* argv[]) {
  MathDll m;
  std::cout << "num is " << m.GetNumFromDyn() << std::endl;
  return 0;
}

现在构建main.cpp控制台应用程序,将其与先前构建的DLL导入库链接MathDll.lib.

请注意问题是如何消失的,因为我已经摆脱了传递依赖MathLib(MathDll.hppfrom)from main.cpp,因为现在#include "MathLib.hpp"包含是在翻译单元中完成的MathDll.cpp(因为它实际上只是根据上述规则需要),因此被构建为二进制工件(在这种情况下为DLL)并且不存在于其接口中.

了解所有这些对于使用C/C++进行适当的本机软件开发非常重要,所以事先提出这个问题真是太好了.我遇到了那些经常不了解/不了解这一点的人,这对他们(业余爱好者)和我们来说是彻头彻尾的噩梦,当我们不得不处理他们写的那些糟糕的软件时......

1 个回答
  • 用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)导入库.MathDllDLL.在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.

    请注意问题是如何消失的,因为我已经摆脱了传递依赖MathLib(MathDll.hppfrom)from main.cpp,因为现在#include "MathLib.hpp"包含是在翻译单元中完成的MathDll.cpp(因为它实际上只是根据上述规则需要),因此被构建为二进制工件(在这种情况下为DLL)并且不存在于其接口中.

    了解所有这些对于使用C/C++进行适当的本机软件开发非常重要,所以事先提出这个问题真是太好了.我遇到了那些经常不了解/不了解这一点的人,这对他们(业余爱好者)和我们来说是彻头彻尾的噩梦,当我们不得不处理他们写的那些糟糕的软件时......

    2023-02-06 15:43 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有