#pragma once #include "client.hpp" #include "logging.hpp" #include "cmix.pb.h" #include struct Send; struct Receive; struct SendReceive; template class ProtobufClient; typedef ProtobufClient Receiver; typedef ProtobufClient Sender; typedef ProtobufClient SenderReceiver; template class ProtobufClient { Client client; #define MESSAGE_SETTER(TYPE, NAME) \ void message_setter(cmix_proto::CMixMessage& m, cmix_proto::TYPE const& v) { \ *m.mutable_##NAME() = v; \ } \ MESSAGE_SETTER(Initialization, initialization) MESSAGE_SETTER(ImANode, imanode) MESSAGE_SETTER(ImAClient, imaclient) MESSAGE_SETTER(Bye, bye) MESSAGE_SETTER(KeyExchange, keyexchange) #undef MESSAGE_SETTER public: /*! * \brief ProtobufClient * \param socket An rvalue reference to a socket it will now own and receive from. */ ProtobufClient(boost::asio::ip::tcp::socket&& socket) : client(std::move(socket)) {} /*! * \brief Move constructor for ProtobufClient. */ ProtobufClient(ProtobufClient&& c) = default; ProtobufClient(Client&& c) : client(std::move(c)) {} template ProtobufClient(ProtobufClient&& r) : client(std::move(r.client)) {} /*! * \brief Move assignment for ProtobufClient. */ ProtobufClient& operator=(ProtobufClient&&) = default; /*! * \brief async_connect Asynchronously connects to next_host:port and calls on_connect * \param next_host The host to connect to * \param next_port The port to connect to * \param on_connect The callback to call on a succes. */ template void async_connect(std::string next_host, std::string next_port, F on_connect) { client.async_connect(next_host, next_port, on_connect); } template::value || std::is_same::value), void>::type* = nullptr> void async_send(V value, F on_sent) { cmix_proto::CMixMessage m; message_setter(m, value); client.async_send(m.SerializeAsString(), on_sent); } /*! * \brief send sends the string prefixed with it's length over the socket asynchronously. * \param message The string to be sent. */ template::value || std::is_same::value)>::type* = nullptr> void async_send(V value) { cmix_proto::CMixMessage m; message_setter(m, value); client.async_send(m.SerializeAsString(), []{}); } /*! * \brief receive * \param message_handler The function to call when a message has been received. */ template ::value || std::is_same::value)>::type* = nullptr> void receive(F message_handler) { auto f = [message_handler](std::vector const& buffer) { cmix_proto::CMixMessage message; if(!message.ParseFromArray(buffer.data(), buffer.size())) { BOOST_LOG_TRIVIAL(error) << "Received something which was not a CMixMessage"; throw std::runtime_error("Network communication was disrupted in a unrecoverable way."); } message_handler(message); }; client.receive(f); } /*! * \brief close Closes the underlying socket. */ void close(){ client.close(); } /*! * \brief on_done sets the done callback. * \param f The new done callback function. */ template void on_done(F f) { client.on_done(f); } friend Receiver make_receiver(Receiver&& r); friend SenderReceiver make_sender_receiver(SenderReceiver&& r); friend SenderReceiver make_sender_receiver(Receiver&& r); }; inline Receiver make_receiver(Receiver&& r) { return Receiver(std::move(r.client)); } inline SenderReceiver make_sender_receiver(SenderReceiver&& r) { return SenderReceiver(std::move(r.client)); } inline SenderReceiver make_sender_receiver(Receiver&& r) { return SenderReceiver(std::move(r.client)); }