mirror of
https://gitlab.com/niansa/quickipc.git
synced 2025-03-06 20:49:18 +01:00
153 lines
3.3 KiB
C++
153 lines
3.3 KiB
C++
class QIPC;
|
|
class QBiIPC;
|
|
|
|
#ifndef _QIPC_IPC_HPP
|
|
#define _QIPC_IPC_HPP
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <stdexcept>
|
|
|
|
#include <cstring>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
class QIPC {
|
|
public:
|
|
struct Error : public std::runtime_error {
|
|
using std::runtime_error::runtime_error;
|
|
};
|
|
|
|
private:
|
|
template<typename T>
|
|
static T syscchk(T code) {
|
|
// Check if system call result code is -1, if so throw exception
|
|
if (code < 0) {
|
|
throw Error(strerror(errno));
|
|
}
|
|
return code;
|
|
}
|
|
|
|
int raw_fds[2] = {-1, -1};
|
|
|
|
public:
|
|
static constexpr size_t default_max_buflen = 4000;
|
|
size_t max_buflen;
|
|
|
|
auto get_in() const {
|
|
return raw_fds[0];
|
|
}
|
|
auto get_out() const {
|
|
return raw_fds[1];
|
|
}
|
|
|
|
QIPC(int in, int out, size_t max_buflen = default_max_buflen)
|
|
: max_buflen(max_buflen) {
|
|
raw_fds[0] = in;
|
|
raw_fds[1] = out;
|
|
}
|
|
QIPC(size_t max_buflen = default_max_buflen) : max_buflen(max_buflen) {}
|
|
QIPC(const QIPC&) = delete;
|
|
QIPC(const QIPC&& o) {
|
|
QIPC(o.get_in(), o.get_out(), o.max_buflen);
|
|
}
|
|
|
|
auto operator =(const QIPC& o) {
|
|
QIPC(o.get_in(), o.get_out());
|
|
}
|
|
|
|
void create() {
|
|
syscchk(pipe(raw_fds));
|
|
}
|
|
|
|
void close() {
|
|
if (raw_fds[0] != -1) {
|
|
::close(raw_fds[0]);
|
|
}
|
|
if (raw_fds[1] != -1) {
|
|
::close(raw_fds[1]);
|
|
}
|
|
}
|
|
|
|
void send(std::string_view str) {
|
|
size_t len = str.size();
|
|
syscchk(write(get_out(), &len, sizeof(len))); // Write length
|
|
syscchk(write(get_out(), str.data(), len)); // Write data
|
|
}
|
|
|
|
auto recv() {
|
|
// Read len
|
|
size_t len;
|
|
syscchk(read(get_in(), &len, sizeof(len)));
|
|
// Check len
|
|
if (len > 2000) {
|
|
throw Error("Data length limit exceeded");
|
|
}
|
|
// Read data
|
|
auto data = new char[len];
|
|
syscchk(read(get_in(), data, len));
|
|
// Construct string
|
|
auto fres = std::string(data, data+len);
|
|
// Clean up
|
|
delete []data;
|
|
// Return final result
|
|
return fres;
|
|
}
|
|
|
|
template<typename T>
|
|
void send_raw(const T& data) {
|
|
syscchk(write(get_out(), &data, sizeof(data)));
|
|
}
|
|
|
|
template<typename T>
|
|
T recv_raw() {
|
|
// Disclaimer: Bad practice, but what if T does not have a default constructor?
|
|
// This allows us to not construct the object at all!
|
|
// Skipping the constructor might even improve performance and prevent UB!
|
|
char fres[sizeof(T)];
|
|
syscchk(read(get_in(), &fres, sizeof(fres)));
|
|
return *reinterpret_cast<T*>(&fres);
|
|
}
|
|
};
|
|
|
|
struct QBiIPC {
|
|
QIPC in, out;
|
|
|
|
void create() {
|
|
in.create();
|
|
out.create();
|
|
}
|
|
|
|
void swap() {
|
|
std::swap(in, out);
|
|
}
|
|
|
|
auto get_in() const {
|
|
return in.get_in();
|
|
}
|
|
auto get_out() const {
|
|
return out.get_out();
|
|
}
|
|
|
|
void send(std::string_view str) {
|
|
out.send(str);
|
|
}
|
|
|
|
auto recv() {
|
|
return in.recv();
|
|
}
|
|
|
|
template<typename T>
|
|
void send_raw(const T& data) {
|
|
out.send_raw<T>(data);
|
|
}
|
|
|
|
template<typename T>
|
|
auto recv_raw() {
|
|
return in.recv_raw<T>();
|
|
}
|
|
};
|
|
|
|
#endif
|