#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include template constexpr void assertSerializable() { // monostate being zero ensures transactional reads wont break static_assert( std::is_same_v>, std::monostate>, "Serialization of variants without std::monostate at index 0 is disallowed." ); static_assert( sizeof...(Types) <= std::numeric_limits::max(), "Serialization of variants that can't fit the tag in a uint8 is disallowed." ); } template QDataStream& operator<<(QDataStream& stream, const std::variant& variant) { assertSerializable(); if (variant.valueless_by_exception()) { stream << static_cast(0); // must be monostate } else { stream << static_cast(variant.index()); std::visit([&](const T& value) { stream << value; }, variant); } return stream; } template constexpr bool forEachTypeIndex(const auto& f) { return [&](std::index_sequence) { return (f(std::in_place_index_t()) || ...); }(std::index_sequence_for()); } template std::variant createIndexedOrMonostate(size_t index, std::variant& variant) { assertSerializable(); const auto initialized = forEachTypeIndex([index, &variant](std::in_place_index_t) { if (index == Index) { variant.template emplace(); return true; } else { return false; } }); if (!initialized) { variant = std::monostate(); } return variant; } template QDataStream& operator>>(QDataStream& stream, std::variant& variant) { assertSerializable(); quint8 index = 0; stream >> index; createIndexedOrMonostate(index, variant); std::visit([&](T& value) { stream >> value; }, variant); return stream; } template QDataStream& streamInValues(QDataStream& stream, const Types&... types) { return (stream << ... << types); } template QDataStream& streamOutValues(QDataStream& stream, Types&... types) { return (stream >> ... >> types); } // NOLINTBEGIN #define DEFINE_SIMPLE_DATASTREAM_OPS(Type, ...) \ inline QDataStream& operator<<(QDataStream& stream, const Type& __VA_OPT__(data)) { \ return streamInValues(stream __VA_OPT__(, __VA_ARGS__)); \ } \ \ inline QDataStream& operator>>(QDataStream& stream, Type& __VA_OPT__(data)) { \ return streamOutValues(stream __VA_OPT__(, __VA_ARGS__)); \ } // NOLINTEND DEFINE_SIMPLE_DATASTREAM_OPS(std::monostate); namespace qs::ipc { Q_DECLARE_LOGGING_CATEGORY(logIpc); template class MessageStream { public: explicit MessageStream(QDataStream* stream, QLocalSocket* socket) : stream(stream) , socket(socket) {} template MessageStream& operator<<(V value) { *this->stream << T(value); this->socket->flush(); return *this; } private: QDataStream* stream; QLocalSocket* socket; }; class IpcServer: public QObject { Q_OBJECT; public: explicit IpcServer(const QString& path); static void start(); private slots: void onNewConnection(); private: QLocalServer server; }; class IpcServerConnection: public QObject { Q_OBJECT; public: explicit IpcServerConnection(QLocalSocket* socket, IpcServer* server); template void respond(const T& message) { this->stream << message; this->socket->flush(); } template MessageStream responseStream() { return MessageStream(&this->stream, this->socket); } // public for access by nonlocal handlers QLocalSocket* socket; QDataStream stream; private slots: void onDisconnected(); void onReadyRead(); }; class IpcClient: public QObject { Q_OBJECT; public: explicit IpcClient(const QString& path); [[nodiscard]] bool isConnected() const; void waitForConnected(); void waitForDisconnected(); void kill(); template void sendMessage(const T& message) { this->stream << message; this->socket.flush(); } template bool waitForResponse(T& slot) { while (this->socket.waitForReadyRead(-1)) { this->stream.startTransaction(); this->stream >> slot; if (!this->stream.commitTransaction()) continue; return true; } qCCritical(logIpc) << "Error occurred while waiting for response."; return false; } [[nodiscard]] static int connect(const QString& id, const std::function& callback); // public for access by nonlocal handlers QLocalSocket socket; QDataStream stream; signals: void connected(); void disconnected(); private slots: static void onError(QLocalSocket::LocalSocketError error); }; } // namespace qs::ipc