[c_api] added c api for libs
changed project structure, now relying on shared libraries
This commit is contained in:
parent
b28c1b8e7f
commit
e0ac93c657
@ -2,7 +2,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "export.h"
|
||||
namespace cloud_point_rpc {
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ namespace cloud_point_rpc {
|
||||
* @param port Server Port
|
||||
* @return int exit code
|
||||
*/
|
||||
int run_cli(std::istream &input, std::ostream &output, const std::string &ip,
|
||||
int CRPC_EXPORT run_cli(std::istream &input, std::ostream &output, const std::string &ip,
|
||||
int port);
|
||||
|
||||
} // namespace cloud_point_rpc
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
#include <jsonrpccxx/client.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace cloud_point_rpc {
|
||||
|
||||
class RpcClient : public jsonrpccxx::JsonRpcClient {
|
||||
|
||||
@ -5,14 +5,27 @@
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include "export.h"
|
||||
extern "C" {
|
||||
|
||||
struct rpc_string {
|
||||
rpc_string(const char* data, uint64_t size) : s(data,size) {}
|
||||
rpc_string() = default;
|
||||
|
||||
std::string s;
|
||||
};
|
||||
}
|
||||
|
||||
namespace cloud_point_rpc {
|
||||
|
||||
class RpcServer {
|
||||
class CRPC_EXPORT RpcServer {
|
||||
public:
|
||||
using Handler = std::function<nlohmann::json(const nlohmann::json &)>;
|
||||
using callback_t = rpc_string*(*)(rpc_string*);
|
||||
|
||||
void register_method(const std::string &name, Handler handler);
|
||||
void register_method(const std::string &name, callback_t handler);
|
||||
///@param request_str - json rpc 2.0 formatted string
|
||||
[[nodiscard]] std::string process(const std::string &request_str);
|
||||
|
||||
private:
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
#include "cloud_point_rpc/config.hpp"
|
||||
#include <vector>
|
||||
|
||||
#include "export.h"
|
||||
namespace cloud_point_rpc {
|
||||
|
||||
class Service {
|
||||
class CRPC_EXPORT Service {
|
||||
public:
|
||||
explicit Service(const TestData &data = {});
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <cloud_point_rpc/tcp_read.hpp>
|
||||
#include <glog/logging.h>
|
||||
#include <string>
|
||||
|
||||
#include "export.h"
|
||||
namespace cloud_point_rpc {
|
||||
/**
|
||||
* TCPConnector main purpose is to implement jsonrpccxx::IClientConnector Send
|
||||
@ -13,7 +13,7 @@ namespace cloud_point_rpc {
|
||||
* the json "expected size" of a packet in case when the packet was for some
|
||||
* reason on any level fractured.
|
||||
*/
|
||||
class TCPConnector : public jsonrpccxx::IClientConnector {
|
||||
class CRPC_EXPORT TCPConnector : public jsonrpccxx::IClientConnector {
|
||||
public:
|
||||
TCPConnector(const std::string &ip, size_t port) noexcept(false)
|
||||
: io_context_(), socket_(io_context_) {
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
#include <glog/logging.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "export.h"
|
||||
namespace cloud_point_rpc {
|
||||
|
||||
class TcpServer {
|
||||
class CRPC_EXPORT TcpServer {
|
||||
public:
|
||||
using RequestProcessor = std::function<std::string(const std::string &)>;
|
||||
|
||||
|
||||
14
include/export.h
Normal file
14
include/export.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#if defined(_WIN32)
|
||||
#ifdef CRPC_SERVER_API_EXPORT
|
||||
#define CRPC_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define CRPC_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#elif defined(__GNUC__) // GCC, Clang, etc.
|
||||
// Linux, macOS, etc.
|
||||
#define CRPC_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define CRPC_EXPORT
|
||||
#pragma warning Unknown dynamic link import/export semantics.
|
||||
#endif
|
||||
38
include/server_api.h
Normal file
38
include/server_api.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef CRPC_SERVER_API
|
||||
#define CRPC_SERVER_API
|
||||
|
||||
#include <cstdint>
|
||||
#include "export.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //cpp
|
||||
/**
|
||||
* @brief basically just std::string wrapper:
|
||||
* struct rpc_string {
|
||||
* std::string s;
|
||||
* };
|
||||
* has internal gc and would be automatically deallocated on deinit call
|
||||
* but it is better to call destroy manually, to prevent exceeding memory usage
|
||||
*/
|
||||
struct CRPC_EXPORT rpc_string;
|
||||
|
||||
CRPC_EXPORT const char* crpc_str_get_data(const rpc_string*);
|
||||
CRPC_EXPORT uint64_t crpc_str_get_size(const rpc_string*);
|
||||
|
||||
CRPC_EXPORT rpc_string* crpc_str_create(const char* data, uint64_t size);
|
||||
CRPC_EXPORT void crpc_str_destroy(rpc_string*);
|
||||
|
||||
typedef rpc_string*(*callback_t)(rpc_string*);
|
||||
|
||||
CRPC_EXPORT void crpc_init(const char* config_path);
|
||||
CRPC_EXPORT void crpc_deinit();
|
||||
|
||||
CRPC_EXPORT void crpc_add_method(callback_t cb, rpc_string* name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //cpp
|
||||
|
||||
#endif //CRPC_SERVER_API
|
||||
22
include/test_api.h
Normal file
22
include/test_api.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef CRPC_TEST_API
|
||||
#define CRPC_TEST_API
|
||||
|
||||
#include <cstdint>
|
||||
#include "export.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //cpp
|
||||
|
||||
struct CRPC_EXPORT rpc_string;
|
||||
typedef rpc_string*(*callback_t)(rpc_string*);
|
||||
|
||||
CRPC_EXPORT void crpc_test_init();
|
||||
CRPC_EXPORT void crpc_test_deinit();
|
||||
|
||||
CRPC_EXPORT void crpc_test_add_method(callback_t cb, rpc_string* name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //cpp
|
||||
|
||||
#endif //CRPC_SERVER_API
|
||||
@ -1,10 +1,12 @@
|
||||
add_project_arguments('-DCRPC_SERVER_API_EXPORT -pthread', language: 'cpp')
|
||||
|
||||
cloud_point_rpc_sources = files(
|
||||
'rpc_server.cpp',
|
||||
'service.cpp',
|
||||
'cli.cpp'
|
||||
'server_api.cpp'
|
||||
)
|
||||
|
||||
libcloud_point_rpc = static_library('cloud_point_rpc',
|
||||
libcloud_point_rpc = shared_library('cloud_point_rpc',
|
||||
cloud_point_rpc_sources,
|
||||
include_directories : inc,
|
||||
dependencies : [json_dep, thread_dep, glog_dep, yaml_dep, asio_dep],
|
||||
@ -15,14 +17,39 @@ cloud_point_rpc_dep = declare_dependency(
|
||||
link_with : libcloud_point_rpc,
|
||||
dependencies : [json_dep, glog_dep, yaml_dep, asio_dep])
|
||||
|
||||
# Test lib
|
||||
libcloud_point_rpc_test = shared_library('test_cloud_point',
|
||||
'test_api.cpp',
|
||||
dependencies: cloud_point_rpc_dep,
|
||||
install : true)
|
||||
|
||||
cloud_point_rpc_test_dep = declare_dependency(
|
||||
include_directories: inc,
|
||||
link_with: libcloud_point_rpc_test,
|
||||
dependencies: [cloud_point_rpc_dep]
|
||||
)
|
||||
|
||||
libcloud_point_rpc_cli = shared_library('libcloud_point_rpc_cli',
|
||||
'cli.cpp',
|
||||
include_directories : inc,
|
||||
dependencies : [json_dep, thread_dep, glog_dep, yaml_dep, asio_dep, cloud_point_rpc_dep],
|
||||
install : true)
|
||||
|
||||
cloud_point_rpc_cli_dep = declare_dependency(
|
||||
include_directories: inc,
|
||||
link_with: libcloud_point_rpc_cli,
|
||||
dependencies: [cloud_point_rpc_dep]
|
||||
)
|
||||
|
||||
# Client/CLI tool (legacy stdin/stdout)
|
||||
executable('cloud_point_rpc_cli',
|
||||
'main.cpp',
|
||||
dependencies : cloud_point_rpc_dep,
|
||||
['main.cpp', ],
|
||||
dependencies : cloud_point_rpc_cli_dep,
|
||||
install : true)
|
||||
|
||||
# Server executable (TCP)
|
||||
executable('cloud_point_rpc_server',
|
||||
'server_main.cpp',
|
||||
dependencies : cloud_point_rpc_dep,
|
||||
link_args : '-pthread',
|
||||
install : true)
|
||||
|
||||
@ -21,12 +21,22 @@ void RpcServer::register_method(const std::string &name, Handler handler) {
|
||||
handlers_[name] = std::move(handler);
|
||||
}
|
||||
|
||||
void RpcServer::register_method(const std::string& name, callback_t handler) {
|
||||
handlers_[name] = [handler](const nlohmann::json& j) -> nlohmann::json {
|
||||
rpc_string tmp;
|
||||
tmp.s = j.dump();
|
||||
rpc_string* res = handler(&tmp);
|
||||
return {res->s};
|
||||
};
|
||||
}
|
||||
|
||||
std::string RpcServer::process(const std::string &request_str) {
|
||||
LOG(INFO) << request_str;
|
||||
json request;
|
||||
try {
|
||||
request = json::parse(request_str);
|
||||
} catch (const json::parse_error &) {
|
||||
LOG(ERROR) << "json parse error" << __func__;
|
||||
LOG(ERROR) << "json parse error; " << __func__;
|
||||
return create_error(-32700, "Parse error").dump();
|
||||
}
|
||||
|
||||
|
||||
68
src/server_api.cpp
Normal file
68
src/server_api.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "cloud_point_rpc/config.hpp"
|
||||
#include "cloud_point_rpc/rpc_server.hpp"
|
||||
#include "cloud_point_rpc/tcp_server.hpp"
|
||||
#include <glog/logging.h>
|
||||
#include "server_api.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
static std::list<std::unique_ptr<rpc_string>> gc;
|
||||
cloud_point_rpc::RpcServer rpc_server;
|
||||
std::unique_ptr<cloud_point_rpc::TcpServer> server = nullptr;
|
||||
|
||||
extern "C" {
|
||||
|
||||
const char* crpc_str_get_data(const rpc_string* that) {
|
||||
return that->s.c_str();
|
||||
}
|
||||
|
||||
uint64_t crpc_str_get_size(const rpc_string* that){
|
||||
return that->s.size();
|
||||
}
|
||||
|
||||
rpc_string* crpc_str_create(const char* data, uint64_t size){
|
||||
gc.push_back(std::make_unique<rpc_string>(data, size));
|
||||
return gc.back().get();
|
||||
}
|
||||
|
||||
void crpc_str_destroy(rpc_string* that){
|
||||
auto it = std::ranges::find(gc, that, &std::unique_ptr<rpc_string>::get);
|
||||
if(it != gc.end())
|
||||
gc.erase(it);
|
||||
}
|
||||
|
||||
|
||||
void crpc_init(const char* config_path) {
|
||||
google::InitGoogleLogging("CloudPointRPC");
|
||||
if(config_path == nullptr) {
|
||||
LOG(INFO) << "config_path was not provided";
|
||||
}
|
||||
try {
|
||||
auto config = cloud_point_rpc::ConfigLoader::load(config_path);
|
||||
LOG(INFO) << "Loaded config from " << config_path;
|
||||
|
||||
server = std::make_unique<cloud_point_rpc::TcpServer>(config.server.ip, config.server.port,
|
||||
[&](const std::string &request) {
|
||||
return rpc_server.process(
|
||||
request);
|
||||
});
|
||||
server->start();
|
||||
} catch (const std::exception &e) {
|
||||
LOG(ERROR) << "Fatal error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void crpc_deinit() {
|
||||
if(server)
|
||||
server->join();
|
||||
server.reset();
|
||||
gc.clear();
|
||||
}
|
||||
|
||||
void crpc_add_method(callback_t cb, rpc_string* name) {
|
||||
rpc_server.register_method(name->s, cb);
|
||||
}
|
||||
}
|
||||
|
||||
97
src/test_api.cpp
Normal file
97
src/test_api.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "test_api.h"
|
||||
#include "server_api.h"
|
||||
#include "cloud_point_rpc/rpc_server.hpp"
|
||||
#include <glog/logging.h>
|
||||
#include <mutex>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <list>
|
||||
#include "server_api.h"
|
||||
|
||||
class TestThread {
|
||||
std::string make_jsonrpc(const std::string& method_name) {
|
||||
return std::format(R"(
|
||||
{{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "{}",
|
||||
"params": {{}},
|
||||
"id": 1
|
||||
}}
|
||||
)", method_name);
|
||||
}
|
||||
public:
|
||||
TestThread() = default;
|
||||
~TestThread() {
|
||||
join();
|
||||
}
|
||||
|
||||
void routine() {
|
||||
std::unique_lock lock(mtx);
|
||||
std::stop_token stoken = thr.get_stop_token();
|
||||
lock.unlock();
|
||||
while(!stoken.stop_requested()) {
|
||||
lock.lock();
|
||||
DLOG(INFO) << methods.size() << " ; Size of methods";
|
||||
if(!methods.empty()) {
|
||||
auto it = methods.begin();
|
||||
std::advance(it, distance % methods.size());
|
||||
DLOG(INFO) << *it << " : Method at this position";
|
||||
LOG(INFO) << server.process(make_jsonrpc(*it));
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
LOG(INFO) << "Stopped";
|
||||
}
|
||||
void start() {
|
||||
thr = std::jthread(&TestThread::routine, this);
|
||||
}
|
||||
void join() {
|
||||
DLOG(INFO) << "Requested thread stop";
|
||||
thr.request_stop();
|
||||
if(thr.joinable()) {
|
||||
thr.join();
|
||||
}
|
||||
}
|
||||
void add_method(callback_t cb, rpc_string* name) {
|
||||
LOG(INFO) << "Trying to add method: " << name->s;
|
||||
std::lock_guard lock(mtx);
|
||||
methods.emplace_back(name->s);
|
||||
server.register_method(name->s,cb);
|
||||
}
|
||||
|
||||
void call(rpc_string* name) {
|
||||
std::lock_guard lock(mtx);
|
||||
LOG(INFO) << server.process(name->s);
|
||||
}
|
||||
private:
|
||||
std::list<std::string> methods;
|
||||
size_t distance;
|
||||
std::jthread thr;
|
||||
std::mutex mtx;
|
||||
cloud_point_rpc::RpcServer server;
|
||||
} test;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void crpc_test_init() {
|
||||
if(!google::IsGoogleLoggingInitialized())
|
||||
google::InitGoogleLogging("TestRPC");
|
||||
try {
|
||||
test.start();
|
||||
} catch (const std::exception &e) {
|
||||
LOG(ERROR) << "Fatal error: " << e.what();
|
||||
}
|
||||
}
|
||||
void crpc_test_deinit() {
|
||||
test.join();
|
||||
crpc_deinit();
|
||||
}
|
||||
|
||||
void crpc_test_add_method(callback_t cb, rpc_string* name){
|
||||
test.add_method(cb, name);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
test_sources = files(
|
||||
'test_rpc.cpp',
|
||||
'test_integration.cpp',
|
||||
'test_tcp.cpp',
|
||||
'test_cli.cpp',
|
||||
'test_tcp.cpp'
|
||||
'test_c_api.cpp'
|
||||
)
|
||||
|
||||
test_exe = executable('unit_tests',
|
||||
test_sources,
|
||||
dependencies : [cloud_point_rpc_dep, gtest_dep, gtest_main_dep, gmock_dep])
|
||||
dependencies : [cloud_point_rpc_dep, cloud_point_rpc_cli_dep, cloud_point_rpc_test_dep, json_dep, gtest_dep, gtest_main_dep, gmock_dep])
|
||||
|
||||
test('unit_tests', test_exe)
|
||||
|
||||
45
tests/test_c_api.cpp
Normal file
45
tests/test_c_api.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <future>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <glog/logging.h>
|
||||
#include "cloud_point_rpc/rpc_server.hpp"
|
||||
#include "test_api.h"
|
||||
#include "server_api.h"
|
||||
class TestCApi : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
EXPECT_NO_THROW(crpc_test_init());
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
crpc_test_deinit();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestCApi, Base) {
|
||||
FLAGS_logtostderr = 1;
|
||||
rpc_string name;
|
||||
name.s = "test";
|
||||
static std::promise<bool> task;
|
||||
std::future<bool> called = task.get_future();
|
||||
LOG(INFO) << "Test";
|
||||
crpc_test_add_method(+[](rpc_string*)-> rpc_string* {
|
||||
static bool installed = false;
|
||||
DLOG(INFO) << "Trying to do something";
|
||||
if(!installed){
|
||||
LOG(INFO) << "Trying to install";
|
||||
installed = true;
|
||||
task.set_value(installed);
|
||||
}
|
||||
DLOG(INFO) << "Go out";
|
||||
return crpc_str_create("res", sizeof("res"));
|
||||
}, &name);
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
called.wait_for(500ms);
|
||||
|
||||
EXPECT_EQ(called.valid(), true);
|
||||
EXPECT_EQ(called.get(), true);
|
||||
|
||||
LOG(INFO) << "DONE";
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user