class QIPC; class QBiIPC; #ifndef _QIPC_IPC_HPP #define _QIPC_IPC_HPP #include #include #include #include #include #include #include class QIPC { public: struct Error : public std::runtime_error { using std::runtime_error::runtime_error; }; private: template 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 void send_raw(const T& data) { syscchk(write(get_out(), &data, sizeof(data))); } template 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(&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 void send_raw(const T& data) { out.send_raw(data); } template auto recv_raw() { return in.recv_raw(); } }; #endif