This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <string>
|
||||
|
||||
namespace sp {
|
||||
|
||||
template <typename T>
|
||||
std::string GetBasicClassName(const T& a_Value) {
|
||||
int status;
|
||||
char* demangled = abi::__cxa_demangle(typeid(a_Value).name(), 0, 0, &status);
|
||||
if (status != 0)
|
||||
return "";
|
||||
return std::string(demangled);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string GetBasicClassName() {
|
||||
int status;
|
||||
char* demangled = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
|
||||
if (status != 0)
|
||||
return "";
|
||||
return std::string(demangled);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Reflector {
|
||||
static std::string GetClassName() {
|
||||
return GetBasicClassName<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Reflector<std::string> {
|
||||
static std::string GetClassName() {
|
||||
return "std::string";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct Reflector<std::map<K, V>> {
|
||||
static std::string GetClassName();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Reflector<std::vector<T>> {
|
||||
static std::string GetClassName();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string Reflector<std::vector<T>>::GetClassName() {
|
||||
return "std::vector<" + Reflector<T>::GetClassName() + ">";
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
std::string Reflector<std::map<K, V>>::GetClassName() {
|
||||
return "std::map<" + Reflector<K>::GetClassName() + ", " + Reflector<V>::GetClassName() + ">";
|
||||
}
|
||||
|
||||
} // namespace sp
|
||||
@@ -7,12 +7,14 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
|
||||
namespace sp {
|
||||
|
||||
class DataBuffer;
|
||||
|
||||
using ReadFunc = std::function<void(std::uint8_t&)>;
|
||||
|
||||
/**
|
||||
* \class VarInt
|
||||
* \brief Variable-length format such that smaller numbers use fewer bytes.
|
||||
@@ -57,6 +59,7 @@ class VarInt {
|
||||
*/
|
||||
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
|
||||
|
||||
void Read(const ReadFunc& read);
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <sp/io/IOInterface.h>
|
||||
|
||||
namespace sp {
|
||||
namespace io {
|
||||
|
||||
struct FileTag {
|
||||
enum OpenMode {
|
||||
In = 1,
|
||||
Out = 1 << 1,
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
class IOInterface<FileTag> {
|
||||
private:
|
||||
std::unique_ptr<std::ifstream> m_FileInput;
|
||||
std::unique_ptr<std::ofstream> m_FileOutput;
|
||||
|
||||
public:
|
||||
IOInterface(const std::string& a_FilePath, unsigned int a_OpenMode);
|
||||
IOInterface(IOInterface&& other);
|
||||
|
||||
DataBuffer Read(std::size_t a_Amount);
|
||||
void Write(const sp::DataBuffer& a_Data);
|
||||
};
|
||||
|
||||
using File = IOInterface<FileTag>;
|
||||
|
||||
} // namespace io
|
||||
} // namespace sp
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sp/common/DataBuffer.h>
|
||||
|
||||
namespace sp {
|
||||
namespace io {
|
||||
|
||||
template <typename IOTag>
|
||||
class IOInterface {
|
||||
public:
|
||||
DataBuffer Read(std::size_t a_Amount);
|
||||
void Write(const DataBuffer& a_Data);
|
||||
};
|
||||
|
||||
template <typename TOption>
|
||||
class MessageEncapsulator {
|
||||
public:
|
||||
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option);
|
||||
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option);
|
||||
};
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
class Stream {
|
||||
protected:
|
||||
MessageDispatcher m_Dispatcher;
|
||||
IOInterface<IOTag> m_Interface;
|
||||
std::tuple<TOptions...> m_Options;
|
||||
|
||||
using MessageBase = typename MessageDispatcher::MessageBaseType;
|
||||
|
||||
public:
|
||||
Stream() {}
|
||||
Stream(IOInterface<IOTag>&& a_Interface, TOptions&&... a_Options);
|
||||
Stream(Stream&& a_Stream);
|
||||
|
||||
void RecieveMessages();
|
||||
void SendMessage(const MessageBase& a_Message);
|
||||
|
||||
|
||||
template <typename TOption>
|
||||
TOption& GetOption() {
|
||||
return std::get<TOption>(m_Options);
|
||||
}
|
||||
|
||||
MessageDispatcher& GetDispatcher() {
|
||||
return m_Dispatcher;
|
||||
}
|
||||
|
||||
private:
|
||||
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOptions&... a_Options);
|
||||
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOptions&... a_Options);
|
||||
};
|
||||
|
||||
} // namespace io
|
||||
} // namespace sp
|
||||
|
||||
#include <sp/io/IOInterfaceImpl.inl>
|
||||
@@ -1,129 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/extensions/Compress.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sp {
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename... TOptions>
|
||||
struct MessageEncapsulatorPack {};
|
||||
|
||||
template <>
|
||||
struct MessageEncapsulatorPack<> {
|
||||
static DataBuffer Encapsulate(const DataBuffer& a_Data) {
|
||||
return a_Data;
|
||||
}
|
||||
static DataBuffer Decapsulate(DataBuffer& a_Data) {
|
||||
return a_Data;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TOption, typename... TOptions>
|
||||
struct MessageEncapsulatorPack<TOption, TOptions...> {
|
||||
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) {
|
||||
DataBuffer data = io::MessageEncapsulator<TOption>::Encapsulate(a_Data, a_Option);
|
||||
return MessageEncapsulatorPack<TOptions...>::Encapsulate(data, a_Options...);
|
||||
}
|
||||
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) {
|
||||
DataBuffer data = io::MessageEncapsulator<TOption>::Decapsulate(a_Data, a_Option);
|
||||
return MessageEncapsulatorPack<TOptions...>::Decapsulate(data, a_Options...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
namespace io {
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Stream(IOInterface<IOTag>&& a_Interface, TOptions&&... a_Options) :
|
||||
m_Interface(std::move(a_Interface)), m_Options(std::make_tuple<TOptions...>(std::move(a_Options)...)) {}
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Stream(
|
||||
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>&& a_Stream) :
|
||||
m_Dispatcher(std::move(a_Stream.m_Dispatcher)), m_Interface(std::move(a_Stream.m_Interface)) {}
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
void Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::SendMessage(const MessageBase& a_Message) {
|
||||
DataBuffer data = a_Message.Write();
|
||||
TupleForEach([&data](const auto&... a_Options) {
|
||||
data = Encapsulate(data, a_Options...);
|
||||
}, m_Options);
|
||||
DataBuffer finalData;
|
||||
finalData.Reserve(data.GetSize() + sizeof(VarInt::MAX_VALUE));
|
||||
finalData << VarInt{data.GetSize()} << data;
|
||||
m_Interface.Write(finalData);
|
||||
}
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
void Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::RecieveMessages() {
|
||||
while (true) {
|
||||
// reading the first VarInt part byte by byte
|
||||
std::uint64_t lenghtValue = 0;
|
||||
unsigned int readPos = 0;
|
||||
|
||||
while (true) {
|
||||
static constexpr int SEGMENT_BITS = (1 << 7) - 1;
|
||||
static constexpr int CONTINUE_BIT = 1 << 7;
|
||||
|
||||
DataBuffer buffer = m_Interface.Read(sizeof(std::uint8_t));
|
||||
|
||||
// eof
|
||||
if (buffer.GetSize() == 0)
|
||||
return;
|
||||
|
||||
std::uint8_t part;
|
||||
buffer >> part;
|
||||
lenghtValue |= static_cast<std::uint64_t>(part & SEGMENT_BITS) << readPos;
|
||||
|
||||
if ((part & CONTINUE_BIT) == 0)
|
||||
break;
|
||||
|
||||
readPos += 7;
|
||||
|
||||
if (readPos >= 8 * sizeof(lenghtValue))
|
||||
throw std::runtime_error("VarInt is too big");
|
||||
}
|
||||
|
||||
// nothing to read
|
||||
if (lenghtValue == 0)
|
||||
return;
|
||||
|
||||
DataBuffer buffer;
|
||||
buffer = m_Interface.Read(lenghtValue);
|
||||
|
||||
TupleForEach([&buffer, lenghtValue](const auto&... a_Options) {
|
||||
buffer = Decapsulate(buffer, a_Options...);
|
||||
}, m_Options);
|
||||
|
||||
VarInt packetType;
|
||||
buffer >> packetType;
|
||||
|
||||
static const MessageFactory messageFactory;
|
||||
|
||||
std::unique_ptr<MessageBase> message = messageFactory.CreateMessage(packetType.GetValue());
|
||||
|
||||
assert(message != nullptr);
|
||||
|
||||
message->Read(buffer);
|
||||
|
||||
GetDispatcher().Dispatch(*message);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
DataBuffer Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Encapsulate(
|
||||
const DataBuffer& a_Data, const TOptions&... a_Options) {
|
||||
return details::MessageEncapsulatorPack<TOptions...>::Encapsulate(a_Data, a_Options...);
|
||||
}
|
||||
|
||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
||||
DataBuffer Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Decapsulate(
|
||||
DataBuffer& a_Data, const TOptions&... a_Options) {
|
||||
return details::MessageEncapsulatorPack<TOptions...>::Decapsulate(a_Data, a_Options...);
|
||||
}
|
||||
|
||||
} // namespace io
|
||||
} // namespace sp
|
||||
13
include/sp/io/IoInterface.h
Normal file
13
include/sp/io/IoInterface.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/common/DataBuffer.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
class IoInterface {
|
||||
public:
|
||||
virtual DataBuffer Read(std::size_t a_Amount) = 0;
|
||||
virtual void Write(const DataBuffer& a_Data) = 0;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/io/IOInterface.h>
|
||||
|
||||
namespace sp {
|
||||
namespace io {
|
||||
|
||||
struct MemoryTag {};
|
||||
|
||||
template <>
|
||||
class IOInterface<MemoryTag> {
|
||||
private:
|
||||
sp::DataBuffer m_VirtualIO;
|
||||
|
||||
public:
|
||||
sp::DataBuffer Read(std::size_t a_Amount);
|
||||
void Write(const sp::DataBuffer& a_Data);
|
||||
};
|
||||
|
||||
using Memory = IOInterface<MemoryTag>;
|
||||
|
||||
} // namespace io
|
||||
} // namespace sp
|
||||
16
include/sp/io/MessageEncapsulator.h
Normal file
16
include/sp/io/MessageEncapsulator.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/common/DataBuffer.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
class MessageEncapsulator {
|
||||
public:
|
||||
MessageEncapsulator() {}
|
||||
virtual ~MessageEncapsulator() {}
|
||||
|
||||
virtual DataBuffer Encapsulate(const DataBuffer& a_Data) = 0;
|
||||
virtual DataBuffer Decapsulate(DataBuffer& a_Data) = 0;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
36
include/sp/io/MessageIO.h
Normal file
36
include/sp/io/MessageIO.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/pfr.hpp>
|
||||
#include <sp/common/ByteSwapping.h>
|
||||
#include <sp/common/DataBuffer.h>
|
||||
|
||||
namespace sp {
|
||||
namespace details {
|
||||
|
||||
template <typename T>
|
||||
void WriteField(DataBuffer& a_Buffer, const T& a_Data) {
|
||||
T swapped = a_Data;
|
||||
ToNetwork(swapped);
|
||||
a_Buffer << swapped;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ReadField(DataBuffer& a_Buffer, T& a_Data) {
|
||||
a_Buffer >> a_Data;
|
||||
FromNetwork(a_Data);
|
||||
}
|
||||
|
||||
template <typename TData>
|
||||
DataBuffer WriteMessage(const TData& a_MessageData) {
|
||||
DataBuffer buffer;
|
||||
boost::pfr::for_each_field(a_MessageData, [&buffer](const auto& a_Field) { WriteField(buffer, a_Field); });
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <typename TData>
|
||||
void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) {
|
||||
boost::pfr::for_each_field(a_MessageData, [&a_Buffer](auto& a_Field) { ReadField(a_Buffer, a_Field); });
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace sp
|
||||
34
include/sp/io/MessageStream.h
Normal file
34
include/sp/io/MessageStream.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/common/DataBuffer.h>
|
||||
#include <sp/io/IoInterface.h>
|
||||
#include <sp/io/MessageEncapsulator.h>
|
||||
#include <vector>
|
||||
|
||||
namespace sp {
|
||||
|
||||
template <typename TMessageFactory>
|
||||
class MessageStream {
|
||||
private:
|
||||
std::vector<MessageEncapsulator> m_Encapsulators;
|
||||
std::shared_ptr<IoInterface> m_Stream;
|
||||
|
||||
using MessageBaseType = typename TMessageFactory::MessageBaseType;
|
||||
using MessageIdType = typename MessageBaseType::MessageIdType;
|
||||
|
||||
public:
|
||||
MessageStream(std::shared_ptr<IoInterface>&& a_Stream) : m_Stream(std::move(a_Stream)) {}
|
||||
|
||||
std::unique_ptr<MessageBaseType> ReadMessage();
|
||||
std::unique_ptr<MessageBaseType> ReadMessage(MessageIdType a_Id);
|
||||
|
||||
void WriteMessage(const MessageBaseType& a_Message, bool a_WriteId = true);
|
||||
|
||||
private:
|
||||
DataBuffer ReadAndDecapsulate();
|
||||
std::unique_ptr<MessageBaseType> MakeMessage(DataBuffer& buffer, MessageIdType a_Id);
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#include <sp/io/MessageStream.inl>
|
||||
63
include/sp/io/MessageStream.inl
Normal file
63
include/sp/io/MessageStream.inl
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/common/VarInt.h>
|
||||
#include <sp/protocol/MessageFactory.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
template <typename TMessageFactory>
|
||||
DataBuffer MessageStream<TMessageFactory>::ReadAndDecapsulate() {
|
||||
VarInt messageLength;
|
||||
messageLength.Read([this](std::uint8_t& data) { m_Stream->Read(1) >> data; });
|
||||
std::size_t amount = messageLength.GetValue();
|
||||
DataBuffer buffer = m_Stream->Read(amount);
|
||||
|
||||
for (MessageEncapsulator& enc : m_Encapsulators) {
|
||||
buffer = enc.Decapsulate(buffer);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <typename TMessageFactory>
|
||||
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::MakeMessage(DataBuffer& buffer, MessageIdType a_Id) {
|
||||
static const TMessageFactory FACTORY;
|
||||
auto message = FACTORY.CreateMessage(a_Id);
|
||||
message->Read(buffer);
|
||||
return message;
|
||||
}
|
||||
|
||||
template <typename TMessageFactory>
|
||||
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::ReadMessage(MessageIdType a_Id) {
|
||||
|
||||
DataBuffer buffer = ReadAndDecapsulate();
|
||||
|
||||
return MakeMessage(buffer, a_Id);
|
||||
}
|
||||
|
||||
template <typename TMessageFactory>
|
||||
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::ReadMessage() {
|
||||
DataBuffer buffer = ReadAndDecapsulate();
|
||||
|
||||
VarInt messageId;
|
||||
buffer >> messageId;
|
||||
|
||||
return MakeMessage(buffer, MessageIdType(messageId.GetValue()));
|
||||
}
|
||||
|
||||
template <typename TMessageFactory>
|
||||
void MessageStream<TMessageFactory>::WriteMessage(const MessageBaseType& a_Message, bool a_WriteId) {
|
||||
DataBuffer buffer;
|
||||
if (a_WriteId)
|
||||
buffer << VarInt{static_cast<std::uint64_t>(a_Message.GetId())};
|
||||
buffer << a_Message.Write();
|
||||
for (MessageEncapsulator& enc : m_Encapsulators) {
|
||||
buffer = enc.Encapsulate(buffer);
|
||||
}
|
||||
DataBuffer header;
|
||||
header << VarInt{buffer.GetSize()};
|
||||
m_Stream->Write(header);
|
||||
m_Stream->Write(buffer);
|
||||
}
|
||||
|
||||
} // namespace sp
|
||||
40
include/sp/io/StdIo.h
Normal file
40
include/sp/io/StdIo.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/io/IoInterface.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace sp {
|
||||
|
||||
class StdInput : public IoInterface {
|
||||
private:
|
||||
std::istream& m_Io;
|
||||
|
||||
public:
|
||||
StdInput(std::istream& a_Io) : m_Io(a_Io) {}
|
||||
|
||||
virtual DataBuffer Read(std::size_t a_Amount) override {
|
||||
DataBuffer buffer(a_Amount);
|
||||
m_Io.read(reinterpret_cast<char*>(buffer.data()), a_Amount);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
virtual void Write(const DataBuffer& a_Data) override {}
|
||||
};
|
||||
|
||||
class StdOuput : public IoInterface {
|
||||
private:
|
||||
std::ostream& m_Io;
|
||||
|
||||
public:
|
||||
StdOuput(std::ostream& a_Io) : m_Io(a_Io) {}
|
||||
|
||||
virtual DataBuffer Read(std::size_t a_Amount) override {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual void Write(const DataBuffer& a_Data) override {
|
||||
m_Io.write(reinterpret_cast<const char*>(a_Data.data()), a_Data.GetSize());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/protocol/MessageBase.h>
|
||||
#include <sp/protocol/MessageIO.h>
|
||||
#include <sp/io/MessageIO.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
@@ -9,7 +9,6 @@ template <typename TData, typename TMessageID, TMessageID ID, typename THandler>
|
||||
class ConcreteMessage : public MessageBase<TMessageID, THandler> {
|
||||
public:
|
||||
using DataType = TData;
|
||||
using ThisType = ConcreteMessage<TData, TMessageID, ID, THandler>;
|
||||
|
||||
template <typename... T>
|
||||
ConcreteMessage(const T&... args) : m_Data{args...} {}
|
||||
@@ -21,15 +20,15 @@ class ConcreteMessage : public MessageBase<TMessageID, THandler> {
|
||||
}
|
||||
|
||||
virtual void Dispatch(THandler& handler) const override {
|
||||
handler.Handle(static_cast<const ThisType&>(*this));
|
||||
handler.Handle(static_cast<const DataType&>(m_Data));
|
||||
}
|
||||
|
||||
virtual void Read(std::istream& a_Is) override {
|
||||
details::ReadMessage(a_Is, m_Data);
|
||||
virtual void Read(DataBuffer& a_Buffer) override {
|
||||
details::ReadMessage(a_Buffer, m_Data);
|
||||
}
|
||||
|
||||
virtual void Write(std::ostream& a_Os) const override {
|
||||
details::WriteMessage(a_Os, m_Data);
|
||||
virtual DataBuffer Write() const override {
|
||||
return details::WriteMessage(m_Data);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <sp/common/DataBuffer.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
@@ -17,8 +18,8 @@ class MessageBase {
|
||||
|
||||
virtual void Dispatch(HandlerType& handler) const = 0;
|
||||
|
||||
virtual void Read(std::istream& a_Is) = 0;
|
||||
virtual void Write(std::ostream& a_Os) const = 0;
|
||||
virtual void Read(DataBuffer& a_Buffer) = 0;
|
||||
virtual DataBuffer Write() const = 0;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
@@ -12,6 +12,7 @@ template <typename TBase, typename TTMessages>
|
||||
class MessageFactory {
|
||||
public:
|
||||
using IdType = typename TBase::MessageIdType;
|
||||
using MessageBaseType = TBase;
|
||||
|
||||
MessageFactory() {
|
||||
constexpr std::size_t messageCount = std::tuple_size_v<TTMessages>;
|
||||
|
||||
133
include/sp/protocol/MessageHandler.h
Normal file
133
include/sp/protocol/MessageHandler.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace sp
|
||||
{
|
||||
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
|
||||
|
||||
// TAll is all the message types, that need to be handled, bundled in std::tuple
|
||||
template <typename TAll>
|
||||
class MessageHandler;
|
||||
|
||||
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16, typename T17, typename T18, typename T19, typename T20,
|
||||
typename... TRest>
|
||||
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T11, T13, T14, T15, T16, T17, T18, T19, T20, TRest...> > : public MessageHandler<std::tuple<TRest...> >
|
||||
{
|
||||
using Base = MessageHandler<std::tuple<TRest...> >;
|
||||
public:
|
||||
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
virtual void Handle(const typename T2::DataType& msg) {}
|
||||
virtual void Handle(const typename T3::DataType& msg) {}
|
||||
virtual void Handle(const typename T4::DataType& msg) {}
|
||||
virtual void Handle(const typename T5::DataType& msg) {}
|
||||
virtual void Handle(const typename T6::DataType& msg) {}
|
||||
virtual void Handle(const typename T7::DataType& msg) {}
|
||||
virtual void Handle(const typename T8::DataType& msg) {}
|
||||
virtual void Handle(const typename T9::DataType& msg) {}
|
||||
virtual void Handle(const typename T10::DataType& msg) {}
|
||||
virtual void Handle(const typename T11::DataType& msg) {}
|
||||
virtual void Handle(const typename T12::DataType& msg) {}
|
||||
virtual void Handle(const typename T13::DataType& msg) {}
|
||||
virtual void Handle(const typename T14::DataType& msg) {}
|
||||
virtual void Handle(const typename T15::DataType& msg) {}
|
||||
virtual void Handle(const typename T16::DataType& msg) {}
|
||||
virtual void Handle(const typename T17::DataType& msg) {}
|
||||
virtual void Handle(const typename T18::DataType& msg) {}
|
||||
virtual void Handle(const typename T19::DataType& msg) {}
|
||||
virtual void Handle(const typename T20::DataType& msg) {}
|
||||
};
|
||||
|
||||
// 10 by 10
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename... TRest>
|
||||
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public MessageHandler<std::tuple<TRest...> >
|
||||
{
|
||||
using Base = MessageHandler<std::tuple<TRest...> >;
|
||||
public:
|
||||
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
virtual void Handle(const typename T2::DataType& msg) {}
|
||||
virtual void Handle(const typename T3::DataType& msg) {}
|
||||
virtual void Handle(const typename T4::DataType& msg) {}
|
||||
virtual void Handle(const typename T5::DataType& msg) {}
|
||||
virtual void Handle(const typename T6::DataType& msg) {}
|
||||
virtual void Handle(const typename T7::DataType& msg) {}
|
||||
virtual void Handle(const typename T8::DataType& msg) {}
|
||||
virtual void Handle(const typename T9::DataType& msg) {}
|
||||
virtual void Handle(const typename T10::DataType& msg) {}
|
||||
};
|
||||
|
||||
// 5 by 5
|
||||
template <
|
||||
typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename... TRest>
|
||||
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, TRest...> > : public MessageHandler<std::tuple<TRest...> >
|
||||
{
|
||||
using Base = MessageHandler<std::tuple<TRest...> >;
|
||||
public:
|
||||
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
virtual void Handle(const typename T2::DataType& msg) {}
|
||||
virtual void Handle(const typename T3::DataType& msg) {}
|
||||
virtual void Handle(const typename T4::DataType& msg) {}
|
||||
virtual void Handle(const typename T5::DataType& msg) {}
|
||||
};
|
||||
|
||||
// Deal with rest with 4 types
|
||||
template <typename T1, typename T2, typename T3, typename T4>
|
||||
class MessageHandler<std::tuple<T1, T2, T3, T4> >
|
||||
{
|
||||
public:
|
||||
virtual ~MessageHandler() {}
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
virtual void Handle(const typename T2::DataType& msg) {}
|
||||
virtual void Handle(const typename T3::DataType& msg) {}
|
||||
virtual void Handle(const typename T4::DataType& msg) {}
|
||||
};
|
||||
|
||||
// Deal with rest with 3 types
|
||||
template < typename T1, typename T2, typename T3>
|
||||
class MessageHandler<std::tuple<T1, T2, T3> >
|
||||
{
|
||||
public:
|
||||
virtual ~MessageHandler() {}
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
virtual void Handle(const typename T2::DataType& msg) {}
|
||||
virtual void Handle(const typename T3::DataType& msg) {}
|
||||
};
|
||||
|
||||
// Deal with rest with 2 types
|
||||
template <typename T1, typename T2>
|
||||
class MessageHandler<std::tuple<T1, T2> >
|
||||
{
|
||||
public:
|
||||
virtual ~MessageHandler() {}
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
virtual void Handle(const typename T2::DataType& msg) {}
|
||||
};
|
||||
|
||||
// Deal with rest with 1 type
|
||||
template <typename T1>
|
||||
class MessageHandler<std::tuple<T1> >
|
||||
{
|
||||
public:
|
||||
virtual ~MessageHandler() {}
|
||||
virtual void Handle(const typename T1::DataType& msg) {}
|
||||
};
|
||||
|
||||
// Deal with rest with 0 type
|
||||
template <>
|
||||
class MessageHandler<std::tuple<> >
|
||||
{
|
||||
public:
|
||||
virtual ~MessageHandler() {}
|
||||
};
|
||||
|
||||
} // sp
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/pfr.hpp>
|
||||
#include <iostream>
|
||||
#include <sp/common/ByteSwapping.h>
|
||||
|
||||
namespace sp {
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
void WriteField(std::ostream& a_Os, const T& a_Data) {
|
||||
T swapped = a_Data;
|
||||
ToNetwork(swapped);
|
||||
a_Os.write(reinterpret_cast<const char*>(&swapped), sizeof(a_Data));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ReadField(std::istream& a_Is, T& a_Data) {
|
||||
a_Is.read(reinterpret_cast<char*>(&a_Data), sizeof(a_Data));
|
||||
FromNetwork(a_Data);
|
||||
}
|
||||
|
||||
template <typename TData>
|
||||
void WriteMessage(std::ostream& a_Os, const TData& a_MessageData) {
|
||||
boost::pfr::for_each_field(a_MessageData, [&a_Os](const auto& a_Field) {
|
||||
WriteField(a_Os, a_Field);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename TData>
|
||||
void ReadMessage(std::istream& a_Is, TData& a_MessageData) {
|
||||
boost::pfr::for_each_field(a_MessageData, [&a_Is](auto& a_Field) {
|
||||
ReadField(a_Is, a_Field);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace sp
|
||||
Reference in New Issue
Block a user