以前写过一篇 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接口,以及元数据的扩展都是动态的能够扩展的