以前寫過一篇 c++利用RTTR實現插件式加載動態庫html
今天在這個基礎上結合http服務器,實現一個插件式動態擴展的http服務端,須要一個httplib.h的頭文件來提供http的功能,在java spring框架中能夠直接在方法上加上一個RequestMapping就能夠增長一個web api接口,本文目的也是如此,引入一個http服務,而後經過RTTR添加相對應的元數據直接將方法注入到http服務上去,並藉助rttr的提供json序列化功能自動完成數據的轉換,直接將代碼轉換爲http api.項目自己依賴glog和RTTR.java
TestAPI.hc++
#include <string> #include <rttr/registration.h> #include <ostream> struct Parameter { std::string name; int age; Parameter(); friend std::ostream &operator<<(std::ostream &os, const Parameter ¶meter) { os << "name: " << parameter.name << " age: " << parameter.age; return os; } RTTR_ENABLE(); }; struct Result : Parameter { std::string result; Result(); RTTR_ENABLE(Parameter); }; struct TestAPI { TestAPI(); virtual Result call(Parameter parameter) = 0; virtual ~TestAPI() {}; virtual void test() = 0; };
此次咱們加入了Parameter和Result兩個對象參數.git
TestAPI.cppgithub
#include "TestAPI.h" Parameter::Parameter() { } Result::Result() { }
建議將構造函數寫入到cpp文件,哪怕是空的這樣能保證相RTTR會正確連接.web
Registration.cpp算法
#include "TestAPI.h" RTTR_REGISTRATION { rttr::registration::class_<Parameter>("Parameter") . constructor<>()( rttr::policy::ctor::as_object ) .property("age", &Parameter::age) .property("name", &Parameter::name); rttr::registration::class_<Result>("Result") . constructor<>()( rttr::policy::ctor::as_object ) .property("result", &Result::result); }
此次加入Parameter和Result對象的RTTR支持,這樣就可使用JSON的系列化功能了.rttr::policy::ctor::as_object注意這裏是以對象方式建立的,RTTR支持對象或者是std::shared_ptr建立,若是類型錯誤可能致使一些反射功能代碼不能正常運行.spring
class Imple1Api : public TestAPI { public: Imple1Api(); ~Imple1Api(); virtual void test(); virtual Result call(Parameter parameter); };
#include "Imple1Api.h"
TestAPI::TestAPI() {
}
Imple1Api::Imple1Api() {
}
Imple1Api::~Imple1Api() {
}
void Imple1Api::test() {
LOG_INFO << "hello i am Imple1Api";
}
Result Imple1Api::call(Parameter parameter) {
LOG_INFO << "Imple1Api call" << parameter;
Result result;
result.name = parameter.name;
result.age = parameter.age;
result.result = "Imple1Api result ";
return result;
}
Registration.cppjson
#include "Imple1Api.h"
RTTR_PLUGIN_REGISTRATION {
rttr::registration::class_<Imple1Api>("Imple1Api")
(
rttr::metadata("API_VENDOR", "Imple1Api"),
rttr::metadata("API_VERSION", "v1"),
rttr::metadata("API_TYPE", "WEB")
)
.constructor<>()
.method("test",
rttr::select_overload<void()>(&TestAPI::test)
).method("call",
rttr::select_overload<Result(Parameter)>(&TestAPI::call)
);
};
關鍵點:此次將call方法註冊到RTTR,並加上了API_VERSION API_TYPE 這兩個元數據支持web api,實際上這裏的元數據能夠根據本身業務須要進行擴展,好比多是驅動api,多是算法api,而後每一種api都會被不一樣的管理器管理起來,以實現不一樣的訪問控制功能,這裏的代碼就是隻簡單實現了一個WEB服務的管理器,提供web的api,能夠想象的到這種擴張方式是很是靈活的.api
Imple2API
和Imple1API幾乎同樣.
class Imple2Api : public TestAPI { public: Imple2Api(); ~Imple2Api(); virtual void test(); virtual Result call(Parameter parameter); };
#include "Imple2Api.h"
TestAPI::TestAPI() {
}
Imple2Api::Imple2Api() {
}
Imple2Api::~Imple2Api() {
}
void Imple2Api::test() {
LOG_INFO << "hello i am Imple2Api";
}
Result Imple2Api::call(Parameter parameter) {
LOG_INFO << "Imple2Api call";
Result result;
result.name = parameter.name;
result.age = parameter.age;
result.result = "Imple2Api result ";
return result;
}
RTTR_PLUGIN_REGISTRATION { rttr::registration::class_<Imple2Api>("Imple2Api") ( rttr::metadata("API_VENDOR", "Imple2Api"), rttr::metadata("API_VERSION", "v2"), rttr::metadata("API_TYPE", "WEB") ) .constructor<>() .method("test", rttr::select_overload<void()>(&TestAPI::test) ).method("call", rttr::select_overload<Result(Parameter)>(&TestAPI::call) ) ; };
#include <string> #include <vector> #include <rttr/registration.h> #include <rttr/library.h> #include <map> class Context { public: Context(); template<typename T> const std::shared_ptr<T> create(std::string vendor) { for (auto pair:_typeMap) { 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.push_back(t); } } _typeMap.insert({lib, temp}); } }; std::vector<rttr::type> getAllType() { return _types; } private: std::map<std::shared_ptr<rttr::library>, std::vector<rttr::type>> _typeMap; std::vector<rttr::type> _types; };
#include "Context.h"
Context::Context() {
}
加載器基本沒有變化,直接添加一個新的數據結構以方便調用.
#include <TestAPI.h> #include "Context.h" #include "httplib.h" void initWebServer(std::vector<rttr::type> types, httplib::Server &svr) { for (int i = 0; i < types.size(); ++i) { if (types[i].get_metadata("API_TYPE") == "WEB") { std::string version = types[i].get_metadata("API_VERSION").to_string(); auto obj2 = types[i].create(); auto ms = types[i].get_methods(); for (auto m:ms) { std::string methodName = m.get_name().data(); std::string url = "/" + version + "/" + methodName; LOG_INFO << url; rttr::array_range<rttr::parameter_info> infos = m.get_parameter_infos(); if (infos.size() > 1) { LOG_ERROR << "web api only support one json object"; continue; } svr.Post(url.data(), [infos, obj2, m](const httplib::Request &req, httplib::Response &res) { if (infos.size() == 1) { rttr::variant var = infos.begin()->get_type().create(); std::string text = req.body; parseJSonObject(text, var);//請參照官方代碼的jsondemo std::vector<rttr::argument> args{var}; rttr::variant result = m.invoke_variadic(obj2, args); //請參照官方代碼的轉爲json的demo res.set_content(toJSonString(result), "application/json"); } else { rttr::instance result = m.invoke(obj2); res.set_content(toJSonString(result), "application/json"); } }); } } } } int main() { httplib::Server svr; Context context; context.loadLibrary( "libImpl1API.so"); context.loadLibrary( "libImpl2API.so"); initWebServer(context.getAllType(), svr); svr.set_error_handler([](const httplib::Request & /*req*/, httplib::Response &res) { const char *fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>"; char buf[BUFSIZ]; snprintf(buf, sizeof(buf), fmt, res.status); res.set_content(buf, "text/html"); }); svr.listen("0.0.0.0", 8080); return 0; }
能夠看到分別將兩個庫中註冊的類型和方法,按照元數據指定的方式直接添加到http 服務註冊成api.甚至改成熱加載也不是不很是複雜的事情.更爲關鍵的是這種擴展方式是徹底的水平方向擴展,對於開發者也很是友善,只要實現指定的api接口,填寫上本身的api相關元數據既可.沒有繁瑣宏定義,也沒有框架上的限制,api接口,以及元數據的擴展都是動態的能夠擴展的