c++利用RTTR实现插件式加载动态库

                    在java中大量的框架都使用了一种只要给一个接口实现自动匹配一个实现了该接口的的对象,或者调用者直接按照接口定义选择一个对应的实现对象,这在实际的框架扩展中很是有帮助,好比一个很简单的过程,读取数据,解析数据,录入数据,这三个操做均可以是一个独立插件,随着业务扩展可能刚开始是从网络读取数据,而后还多是mysql,以及其余的扩展,同理解析也同样,能够解析为对象,xml,json,录入数据可能关系型数据库,非关系型数据库,存在大量的扩展,可是过程不变只是插件在替换.而在其余的软件好比视觉识别,识别不一样的物提可能也是一个单独插件.java

                   本文主要主要的目的在于构建一个很是简单的c++的抽象接口实现插件机制,你只要具有基本的c++知识我想你就应该能够理解了.mysql

               

API定义

         本文主要是简单的说明插件加载机制,因此只设计一个最简单的test接口.c++

struct TestAPI {
    TestAPI();
    virtual ~TestAPI() {};
    virtual void test() = 0;
};

     第一个实现sql

class Imple1Api : public TestAPI {
public:
    Imple1Api();
    ~Imple1Api();
    virtual void test(){
       LOG_INFO<<"hello i am  Imple1Api";
    };
};
RTTR_PLUGIN_REGISTRATION {
    rttr::registration::class_<Imple1Api>("Imple1Api")
            (
                    rttr::metadata("API_VENDOR", "Imple1Api")
            )
            .constructor<>()
            .method("test",
                    rttr::select_overload<void()>(&TestAPI::test)
            );
};

将这两个文件编译为libImpl1API.so数据库

 

 

第二个实现json

class Imple2Api : public TestAPI {
public:
    Imple2Api();
    ~Imple2Api();
    virtual void test(){
        LOG_INFO<<"hello i am  Imple2Api";
    };
};
RTTR_PLUGIN_REGISTRATION {
    rttr::registration::class_<Imple2Api>("Imple2Api")
            (
                    rttr::metadata("API_VENDOR", "Imple2Api")
            )
            .constructor<>()
            .method("test",
                    rttr::select_overload<void()>(&TestAPI::test)
            )
            ;
};

将这两个文件编译为libImpl2API.soapi

类加载器

class Context {
public:
    Context(){};

    template<typename T>
    const std::shared_ptr<T> create(std::string vendor) {
        for (auto pair:types) {
            for (auto t: pair.second) {
                if (t.is_derived_from<T>()) {
                    if (t.get_metadata("API_VENDOR").to_string() == vendor) {
                        const rttr::variant &var = t.create();
                        return var.get_value<std::shared_ptr<T>>();
                    }
                }
            }
        }
        throw std::invalid_argument ("can not find api for " + vendor);
    };

    void loadLibrary(std::string libPath) {
        std::shared_ptr<rttr::library> lib = std::shared_ptr<rttr::library>(new rttr::library(libPath));

        if (!lib->load()) {
            LOG_ERROR << "load library error " << lib->get_error_string();
        }
        {
            std::vector<rttr::type> temp;
            for (auto t : lib->get_types()) {
                if (t.is_class() && !t.is_wrapper()) {
                    LOG_INFO << "find class " << t.get_name();
                    temp.push_back(t);
                }
            }
            types.insert({lib, temp});
        }
    };

private:
    std::map<std::shared_ptr<rttr::library>, std::vector<rttr::type>> types;
};

 

测试代码

#include <TestAPI.h>
#include "Context.h"

int main() {
    Context context;
    context.loadLibrary(
            "libImpl1API.so");
    context.loadLibrary(
            "libImpl2API.so");
    auto api = context.create<TestAPI>("Imple1Api");
    if (api) {
        api->test();
    } else {
        LOG_INFO << "API OBJECT empty";
    }
    api = context.create<TestAPI>("Imple2Api");
    if (api) {
        api->test();
    } else {
        LOG_INFO << "API OBJECT empty";
    }
    api = context.create<TestAPI>("Imple3Api");
    api->test();
}

运行结果以下:网络

I0106 13:59:41.553443  5549 Context.h:39] find class Imple1Api
I0106 13:59:41.553531  5549 Context.h:39] find TestAPI
I0106 13:59:41.555182  5549 Context.h:39] find class Imple2Api
I0106 13:59:41.555248  5549 Impl1.API::Imple1Api.cpp:20] hello i am  Imple1Api
I0106 13:59:41.555301  5549 Impl2.API::Imple2Api.cpp:20] hello i am Imple2Api
terminate called after throwing an instance of 'std::invalid_argument'
  what():  can not find api for Imple3Apiapp

 

总结

    经过以上的代码,能够发现利用rttr的机制实现接口抽象插件机制已是很是简单了,而已功能足够强大,稍微作改动实现动态加载新插件也是很是简单的,由于这个测试代码只要修改一下顺序,就是动态加载插件了.框架

相关文章
相关标签/搜索