refactor
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "IpcMessage.h"
|
||||
|
||||
// Абстракция IPC‑канала, работающего с IpcMessage.
|
||||
// Живёт отдельно от RPC‑кода, чтобы транспорт не зависел от конкретной RPC‑реализации.
|
||||
class IpcChannel {
|
||||
public:
|
||||
virtual ~IpcChannel() = default;
|
||||
|
||||
virtual void send(const IpcMessage& msg) = 0;
|
||||
virtual IpcMessage receive() = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
// Примитивное IPC‑сообщение с API add<T>() / get<T>().
|
||||
// Под капотом пока текстовый формат с типовыми тегами, но снаружи интерфейс не завязан на std::string.
|
||||
|
||||
class IpcMessage {
|
||||
public:
|
||||
IpcMessage() = default;
|
||||
|
||||
explicit IpcMessage(const std::string& raw)
|
||||
: raw_(raw) {}
|
||||
|
||||
IpcMessage(const IpcMessage& other)
|
||||
: raw_(other.raw_) {}
|
||||
|
||||
IpcMessage& operator=(const IpcMessage& other) {
|
||||
if (this != &other) {
|
||||
raw_ = other.raw_;
|
||||
in_.clear();
|
||||
in_initialized_ = false;
|
||||
out_.str(std::string{});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Конструирование исходящего сообщения.
|
||||
template<typename T>
|
||||
void add(const T& v);
|
||||
|
||||
// Чтение входящего сообщения по частям.
|
||||
template<typename T>
|
||||
T get();
|
||||
|
||||
const std::string& raw() const {
|
||||
return raw_;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return raw_.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string raw_;
|
||||
std::ostringstream out_;
|
||||
std::istringstream in_;
|
||||
bool in_initialized_{false};
|
||||
|
||||
void ensureInput() {
|
||||
if (!in_initialized_) {
|
||||
in_.str(raw_);
|
||||
in_initialized_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ===== Специализации по типам =====
|
||||
|
||||
// int
|
||||
template<>
|
||||
inline void IpcMessage::add<int>(const int& v) {
|
||||
out_ << 'i' << ' ' << v << ' ';
|
||||
raw_ = out_.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int IpcMessage::get<int>() {
|
||||
ensureInput();
|
||||
char tag{};
|
||||
in_ >> tag;
|
||||
// в PoC просто доверяем, что тип совпадает
|
||||
int v{};
|
||||
in_ >> v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// std::string
|
||||
template<>
|
||||
inline void IpcMessage::add<std::string>(const std::string& v) {
|
||||
// формат: 's' <len> <bytes...>
|
||||
out_ << 's' << ' ' << v.size() << ' ';
|
||||
out_.write(v.data(), static_cast<std::streamsize>(v.size()));
|
||||
out_ << ' ';
|
||||
raw_ = out_.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string IpcMessage::get<std::string>() {
|
||||
ensureInput();
|
||||
char tag{};
|
||||
in_ >> tag;
|
||||
// ожидаем 's'
|
||||
std::size_t len{};
|
||||
in_ >> len;
|
||||
// съесть одиночный пробел перед данными
|
||||
in_.get();
|
||||
std::string res(len, '\0');
|
||||
in_.read(&res[0], static_cast<std::streamsize>(len));
|
||||
// съесть завершающий пробел
|
||||
in_.get();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "IpcChannel.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// IPC‑канал поверх именованных pipe.
|
||||
// Инкапсулирует работу с файловыми дескрипторами и обмен сообщениями IpcMessage.
|
||||
// readPipe — тот FIFO, который этот endpoint читает; writePipe — тот, в который пишет.
|
||||
|
||||
class IpcPipeChannel : public IpcChannel {
|
||||
public:
|
||||
IpcPipeChannel(const char* readPipe, const char* writePipe) {
|
||||
// Канал не создаёт FIFO, только открывает.
|
||||
// Открываем оба конца как O_RDWR, чтобы избежать блокировок на open(O_RDONLY/O_WRONLY).
|
||||
// При этом логически читаем только из readPipe, а пишем только в writePipe.
|
||||
fdIn_ = ::open(readPipe, O_RDWR);
|
||||
fdOut_ = ::open(writePipe, O_RDWR);
|
||||
}
|
||||
|
||||
~IpcPipeChannel() override {
|
||||
if (fdIn_ >= 0) {
|
||||
::close(fdIn_);
|
||||
}
|
||||
if (fdOut_ >= 0) {
|
||||
::close(fdOut_);
|
||||
}
|
||||
}
|
||||
|
||||
void send(const IpcMessage& msg) override {
|
||||
if (fdOut_ < 0) {
|
||||
return;
|
||||
}
|
||||
const std::string& data = msg.raw();
|
||||
::write(fdOut_, data.c_str(), data.size());
|
||||
::write(fdOut_, "\n", 1);
|
||||
}
|
||||
|
||||
IpcMessage receive() override {
|
||||
if (fdIn_ < 0) {
|
||||
return IpcMessage{};
|
||||
}
|
||||
char buf[4096];
|
||||
const int n = ::read(fdIn_, buf, sizeof(buf) - 1);
|
||||
if (n <= 0) {
|
||||
return IpcMessage{};
|
||||
}
|
||||
buf[n] = 0;
|
||||
return IpcMessage(std::string(buf));
|
||||
}
|
||||
|
||||
private:
|
||||
int fdIn_{-1};
|
||||
int fdOut_{-1};
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <ipc/IpcChannel.h>
|
||||
|
||||
class ProxyMarshaller {
|
||||
public:
|
||||
explicit ProxyMarshaller(IpcChannel& ch) : channel(ch) {}
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
Ret call(const std::string& method, const Args&... args) {
|
||||
IpcMessage msg;
|
||||
|
||||
// имя метода
|
||||
msg.add(method);
|
||||
|
||||
// аргументы
|
||||
(msg.add(args), ...);
|
||||
|
||||
// отправить
|
||||
channel.send(msg);
|
||||
|
||||
// получить ответ
|
||||
IpcMessage resp = channel.receive();
|
||||
return resp.template get<Ret>();
|
||||
}
|
||||
|
||||
private:
|
||||
IpcChannel& channel;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <ipc/IpcMessage.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
class RpcInvoker {
|
||||
public:
|
||||
template<typename Obj, typename Ret, typename... Args>
|
||||
void registerMethod(Obj* instance,
|
||||
const std::string& name,
|
||||
Ret (Obj::*method)(Args...)) {
|
||||
handlers[name] =
|
||||
[instance, method](const IpcMessage& req) -> IpcMessage {
|
||||
IpcMessage msg = req;
|
||||
|
||||
// пропустить имя метода
|
||||
(void)msg.template get<std::string>();
|
||||
|
||||
// читать аргументы и вызвать метод
|
||||
return callMethod<Ret, Obj, Args...>(instance, method, msg);
|
||||
};
|
||||
}
|
||||
|
||||
IpcMessage dispatch(const IpcMessage& request) {
|
||||
IpcMessage tmp = request;
|
||||
const std::string method = tmp.get<std::string>();
|
||||
|
||||
auto it = handlers.find(method);
|
||||
if (it == handlers.end()) {
|
||||
return IpcMessage{};
|
||||
}
|
||||
|
||||
return it->second(request);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Ret, typename Obj, typename... Args>
|
||||
static IpcMessage callMethod(Obj* obj,
|
||||
Ret (Obj::*method)(Args...),
|
||||
IpcMessage& msg) {
|
||||
auto tuple = readArgs<Args...>(msg);
|
||||
Ret result =
|
||||
std::apply(method, std::tuple_cat(std::make_tuple(obj), tuple));
|
||||
|
||||
IpcMessage out;
|
||||
out.add(result);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static std::tuple<Args...> readArgs(IpcMessage& msg) {
|
||||
return std::tuple<Args...>{msg.template get<Args>()...};
|
||||
}
|
||||
|
||||
std::unordered_map<std::string,
|
||||
std::function<IpcMessage(const IpcMessage&)>>
|
||||
handlers;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
class RpcSerializer {
|
||||
public:
|
||||
template<typename T>
|
||||
static void write(std::ostringstream& out, const T& v) {
|
||||
out << v << ' ';
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T read(std::istringstream& in) {
|
||||
T v;
|
||||
in >> v;
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __clang__
|
||||
# define RPC_EXPORT __attribute__((annotate("export")))
|
||||
#else
|
||||
# define RPC_EXPORT
|
||||
#endif
|
||||
Reference in New Issue
Block a user