|
|
#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 {
|
|
|
// Для входящих сообщений считаем "пустым" то, у которого
|
|
|
// больше не осталось непрочитанных данных во входном потоке.
|
|
|
//
|
|
|
// Для свежесозданного сообщения (ещё не инициализирован in_)
|
|
|
// поведение остаётся прежним: пусто == raw_.empty().
|
|
|
if (!in_initialized_) {
|
|
|
return raw_.empty();
|
|
|
}
|
|
|
|
|
|
// Если поток уже инициализирован, смотрим, остались ли данные.
|
|
|
// peek() вернёт EOF, когда всё прочитано.
|
|
|
return in_.peek() == EOF;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
std::string raw_;
|
|
|
std::ostringstream out_;
|
|
|
mutable std::istringstream in_;
|
|
|
mutable bool in_initialized_{false};
|
|
|
|
|
|
void ensureInput() const {
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|