C++插件管理类(上)——概念背景小Dome

简介: C++插件管理类(上)——概念背景小Dome

@[toc]

一、插件管理类的好处

C++插件化管理库的好处有以下几点:

1.功能扩展: 插件化管理库可以实现动态加载和卸载插件,从而实现功能的动态扩展。通过插件化管理库,可以方便地添加、更新和删除插件,从而实现系统的灵活性和可扩展性。

2. 代码解耦: 插件化管理库可以将系统的核心功能与插件分离,从而实现代码的解耦。通过插件化管理库,可以将不同功能的代码分别实现在不同的插件中,减少代码之间的耦合度,提高代码的可读性和可维护性。

3. 系统稳定性: 插件化管理库可以实现插件的隔离和安全性控制,从而保证系统的稳定性和安全性。通过插件化管理库,可以对插件进行权限控制和安全检查,防止插件对系统的破坏和攻击,保证系统的稳定性和安全性。

4. 跨平台兼容: 插件化管理库可以实现跨平台兼容,从而提高代码的可移植性。通过插件化管理库,可以实现在不同平台上加载和卸载插件,从而实现代码的跨平台兼容。

5. 代码重用: 插件化管理库可以实现代码的重用,从而提高代码的效率和质量。通过插件化管理库,可以将相同的代码实现在不同的插件中,从而减少代码的重复,提高代码的效率和质量。

综上所述,C++插件化管理库可以实现系统的灵活性、可扩展性、稳定性、安全性、跨平台兼容性和代码重用性,是一种优秀的软件设计模式。

二、疑问:动态库已经是运行时候加载了,为啥还需要插件类来管理库?

虽然动态库在运行时加载,但是在程序中需要使用动态库中的函数时,仍需要使用动态库中的函数符号(symbol)来进行调用。而动态库中的函数符号是在编译时确定的,因此需要在编译时将动态库中的函数符号与程序中的函数进行绑定。

使用C++插件类来管理动态库,可以将动态库中的函数封装为插件接口,从而方便程序在运行时进行动态加载和卸载,同时也可以提供一些统一的管理和调用接口,使得程序的可扩展性和可维护性更好。

例如,在插件管理类中可以实现动态加载和卸载插件、查找插件、调用插件接口等功能,从而方便程序在运行时根据需要加载或卸载插件,同时也可以对插件进行统一的管理和调用。这样,程序就可以更加灵活地进行功能扩展和升级,而不需要重新编译整个程序。

三、如何实现一个插件管理类?

实现一个C++插件管理类的一般步骤如下:

1. 定义插件管理类: 定义一个类,用于管理插件的加载、卸载、查找等操作。

2. 定义插件类: 定义一个类,用于描述插件的基本信息,如名称、版本、作者等。

3. 定义插件接口: 定义一个接口类,用于规定插件需要实现的功能接口,如初始化、执行、销毁等。

4. 加载插件: 在插件管理类中实现加载插件的函数,通过动态链接库(DLL)加载插件,并将插件信息和接口保存在插件管理类中。

5. 卸载插件: 在插件管理类中实现卸载插件的函数,通过释放动态链接库(DLL)和插件信息和接口的内存空间实现卸载插件。

6. 查找插件: 在插件管理类中实现查找插件的函数,通过遍历已加载的插件列表,查找指定名称或版本的插件。

7. 调用插件接口: 在插件管理类中实现调用插件接口的函数,通过查找指定名称或版本的插件,调用其实现的功能接口。

8. 销毁插件管理类: 在程序退出时,通过插件管理类的析构函数,卸载所有已加载的插件,并释放插件管理类的内存空间。

需要注意的是,插件管理类的实现需要考虑插件的兼容性、安全性和性能等方面的问题,同时需要遵循C++的面向对象设计原则,提高代码的可读性可维护性

四、插件类实例

C++插件类可以通过动态链接库中导出的函数来实现插件的加载和卸载,同时也可以通过函数指针来调用插件提供的接口。

具体来说,可以在插件类中定义一个函数指针类型,用于指向插件中导出的函数。然后,在加载插件时,可以使用动态链接库中的函数来获取插件中导出函数的地址,并将其赋值给函数指针。这样,就可以通过函数指针来调用插件中的函数。

以下是一个简单的C++插件管理类,可以在WindowsLinux平台上使用。它可以用于加载和卸载动态链接库,并提供一个统一的接口来访问插件中的函数。

// PluginManager.h
#ifndef PLUGIN_MANAGER_H
#define PLUGIN_MANAGER_H

#include <string>
#include <vector>

#ifdef _WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif

class PluginManager
{
   
public:
    PluginManager();
    ~PluginManager();

    bool loadPlugin(const std::string& filename);
    bool unloadPlugin(const std::string& filename);

    template<typename T>
    T* getFunction(const std::string& filename, const std::string& functionName)
    {
   
        PluginHandle handle = getPluginHandle(filename);
        if (handle == NULL)
        {
   
            return NULL;
        }

#ifdef _WIN32
        FARPROC proc = GetProcAddress(handle, functionName.c_str());
#else
        void* proc = dlsym(handle, functionName.c_str());
#endif

        if (proc == NULL)
        {
   
            return NULL;
        }

        return reinterpret_cast<T*>(proc);
    }

private:
    typedef void* PluginHandle;

    struct PluginInfo
    {
   
        std::string filename;
        PluginHandle handle;
    };

    std::vector<PluginInfo> plugins_;

    PluginHandle getPluginHandle(const std::string& filename);
};

#endif

// PluginManager.cpp
#include "PluginManager.h"

PluginManager::PluginManager()
{
   
}

PluginManager::~PluginManager()
{
   
    for (auto& plugin : plugins_)
    {
   
#ifdef _WIN32
        FreeLibrary(plugin.handle);
#else
        dlclose(plugin.handle);
#endif
    }
}

bool PluginManager::loadPlugin(const std::string& filename)
{
   
    PluginHandle handle = NULL;

#ifdef _WIN32
    handle = LoadLibrary(filename.c_str());
#else
    handle = dlopen(filename.c_str(), RTLD_LAZY);
#endif

    if (handle == NULL)
    {
   
        return false;
    }

    PluginInfo info;
    info.filename = filename;
    info.handle = handle;

    plugins_.push_back(info);

    return true;
}

bool PluginManager::unloadPlugin(const std::string& filename)
{
   
    for (auto it = plugins_.begin(); it != plugins_.end(); ++it)
    {
   
        if (it->filename == filename)
        {
   
#ifdef _WIN32
            FreeLibrary(it->handle);
#else
            dlclose(it->handle);
#endif
            plugins_.erase(it);
            return true;
        }
    }

    return false;
}

PluginManager::PluginHandle PluginManager::getPluginHandle(const std::string& filename)
{
   
    for (auto& plugin : plugins_)
    {
   
        if (plugin.filename == filename)
        {
   
            return plugin.handle;
        }
    }

    PluginHandle handle = NULL;

#ifdef _WIN32
    handle = LoadLibrary(filename.c_str());
#else
    handle = dlopen(filename.c_str(), RTLD_LAZY);
#endif

    if (handle == NULL)
    {
   
        return NULL;
    }

    PluginInfo info;
    info.filename = filename;
    info.handle = handle;

    plugins_.push_back(info);

    return handle;
}

在这个示例中,PluginManager类封装了动态链接库的加载和卸载过程,并提供了一个模板函数getFunction,用于获取动态链接库中的函数指针。在Windows平台上,使用LoadLibraryGetProcAddress函数加载和获取函数指针;在Linux平台上,使用dlopendlsym函数加载和获取函数指针。

使用示例:

// main.cpp
#include <iostream>
#include "PluginManager.h"

typedef int (*myFunction)(int);

int main()
{
   
    PluginManager pluginManager;
    pluginManager.loadPlugin("MyPlugin.dll"); // Windows平台下的插件
    pluginManager.loadPlugin("libMyPlugin.so"); // Linux平台下的插件

    myFunction myFunctionPtr = pluginManager.getFunction<myFunction>("MyPlugin.dll", "myFunction");
    if (myFunctionPtr != NULL)
    {
   
        int result = myFunctionPtr(42);
        std::cout << "Result: " << result << std::endl;
    }

    myFunctionPtr = pluginManager.getFunction<myFunction>("libMyPlugin.so", "myFunction");
    if (myFunctionPtr != NULL)
    {
   
        int result = myFunctionPtr(42);
        std::cout << "Result: " << result << std::endl;
    }

    pluginManager.unloadPlugin("MyPlugin.dll");
    pluginManager.unloadPlugin("libMyPlugin.so");

    return 0;
}

五、提供一个动态函数库

以下是一个简单的C++动态库,其中包含两个函数:

// MyLibrary.h
#ifndef MYLIBRARY_H
#define MYLIBRARY_H

#ifdef MYLIBRARY_EXPORTS
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API __declspec(dllimport)
#endif

MYLIBRARY_API int add(int a, int b);
MYLIBRARY_API int subtract(int a, int b);

#endif

// MyLibrary.cpp
#include "MyLibrary.h"

MYLIBRARY_API int add(int a, int b)
{
   
    return a + b;
}

MYLIBRARY_API int subtract(int a, int b)
{
   
    return a - b;
}

这个动态库包含两个函数,一个是加法函数add,一个是减法函数subtract。在头文件MyLibrary.h中,使用了预处理指令#ifdef#ifndef来判断是否在动态库中导出函数,以及防止头文件被重复包含。在函数定义前,使用了MYLIBRARY_API宏,它根据MYLIBRARY_EXPORTS宏的定义来决定是导出函数还是导入函数。

Visual Studio中,可以使用以下步骤来编译这个动态库:

  1. 创建一个新的Win32控制台应用程序项目。

  2. 在项目中添加MyLibrary.hMyLibrary.cpp文件。

  3. 在项目属性中,选择“配置属性”->“常规”,将“配置类型”设置为“动态库(.dll)”。

  4. MyLibrary.cpp文件中,使用“生成解决方案”命令来编译动态库。

编译完成后,就可以在输出目录中找到生成的动态库文件MyLibrary.dll。可以使用dumpbin命令来查看动态库中的函数列表。

六、使用GetProcAddress函数获取动态库中的函数地址为空的原因

GetProcAddress函数用于在动态链接库中获取一个导出函数的地址。如果GetProcAddress函数返回NULL,表示无法找到指定的函数。以下是可能导致GetProcAddress函数返回NULL的原因:

1. 函数名错误:GetProcAddress函数需要传入正确的函数名,如果函数名错误或者大小写不匹配,就无法找到指定的函数。在使用GetProcAddress函数时,建议直接从动态库的导出函数列表中复制函数名,避免出现拼写错误。

2. 函数未导出:GetProcAddress函数只能获取动态库中已经导出的函数的地址。如果函数没有被导出,就无法使用GetProcAddress函数来获取它的地址。在动态库中导出函数时,需要使用__declspec(dllexport)或者.def文件来明确指定导出的函数。

3. 调用方式错误:GetProcAddress函数返回的是函数指针,如果调用方式不正确,就会导致程序崩溃或者出现其他错误。在调用GetProcAddress函数获取函数地址后,需要使用正确的调用方式来调用函数,包括参数个数、参数类型、返回值类型等。

4. 动态库加载失败: 如果动态库加载失败,就无法使用GetProcAddress函数来获取函数地址。在使用LoadLibrary函数加载动态库时,需要确保动态库文件存在、路径正确、权限足够等。如果动态库加载失败,可以使用GetLastError函数来获取错误码和错误信息,以便定位问题。

5. 平台不匹配GetProcAddress函数只能在同一平台上加载和调用动态库。如果动态库和应用程序在不同的平台上编译,或者使用不同的编译器或者编译选项,就无法正确加载和调用动态库。在使用GetProcAddress函数时,需要确保动态库和应用程序在同一平台上编译,并且使用相同的编译器和编译选项。

七、动态库如何给插件类提供自己的各类信息?

动态库可以提供插件类自己的各类信息,通常的做法是在动态库中定义一个导出函数,用于返回插件类的信息。以下是一个简单的示例:

// MyLibrary.h
#ifndef MYLIBRARY_H
#define MYLIBRARY_H

#ifdef MYLIBRARY_EXPORTS
#define MYLIBRARY_API __declspec(dllexport)
#else
#define MYLIBRARY_API __declspec(dllimport)
#endif

class PluginInfo
{
   
public:
    std::string name;
    std::string version;
    std::string author;
    std::string description;
};

MYLIBRARY_API PluginInfo getPluginInfo();

#endif

// MyLibrary.cpp
#include "MyLibrary.h"

MYLIBRARY_API PluginInfo getPluginInfo()
{
   
    PluginInfo info;
    info.name = "MyPlugin";
    info.version = "1.0";
    info.author = "John Doe";
    info.description = "A demo plugin.";
    return info;
}

在这个示例中,定义了一个PluginInfo类,用于存储插件类的各类信息。然后在动态库中定义了一个导出函数getPluginInfo,用于返回插件类的信息。在使用这个动态库的应用程序中,可以使用GetProcAddress函数获取getPluginInfo函数的地址,并调用它来获取插件类的信息。

// main.cpp
#include <Windows.h>
#include <iostream>
#include "MyLibrary.h"

int main()
{
   
    HMODULE hModule = LoadLibrary(TEXT("MyLibrary.dll"));
    if (hModule == NULL)
    {
   
        std::cout << "Failed to load library." << std::endl;
        return 1;
    }

    typedef PluginInfo (*getPluginInfo_func)();

    getPluginInfo_func getPluginInfo = (getPluginInfo_func)GetProcAddress(hModule, "getPluginInfo");

    if (getPluginInfo == NULL)
    {
   
        std::cout << "Failed to get function address." << std::endl;
        return 1;
    }

    PluginInfo info = getPluginInfo();

    std::cout << "Plugin name: " << info.name << std::endl;
    std::cout << "Plugin version: " << info.version << std::endl;
    std::cout << "Plugin author: " << info.author << std::endl;
    std::cout << "Plugin description: " << info.description << std::endl;

    FreeLibrary(hModule);
    return 0;
}

在这个示例中,使用GetProcAddress函数获取getPluginInfo函数的地址,并调用它来获取插件类的信息。然后输出各类信息。

八、 具体实例

请参考C++插件管理类(下)
相关文章
|
2天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
15 2
|
8天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
35 5
|
15天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
46 4
|
16天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
43 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
17 0
|
1月前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)