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的機制實現接口抽象插件機制已是很是簡單了,而已功能足夠強大,稍微作改動實現動態加載新插件也是很是簡單的,由於這個測試代碼只要修改一下順序,就是動態加載插件了.框架

相關文章
相關標籤/搜索