working dispatcher + factory
All checks were successful
Linux arm64 / Build (push) Successful in 1m17s

This commit is contained in:
2025-06-25 19:33:11 +02:00
parent 392fcb3d17
commit 10b49b34dd
35 changed files with 315 additions and 1472 deletions

View File

@@ -4,17 +4,28 @@
#include <sp/protocol/Field.h> #include <sp/protocol/Field.h>
#include <sp/protocol/MessageBase.h> #include <sp/protocol/MessageBase.h>
enum class DisconnectFieldsE {
Reason = 0 struct DisconnectPacketData {
std::string m_Reason;
}; };
using DisconnectFields = std::tuple<std::string /*Reason*/>; class DisconnectPacket : public sp::MessageBase<sp::PacketMessage, sp::option::DispatchImpl<DisconnectPacket>> {
private:
DisconnectPacketData m_Data;
DeclarePacket(Disconnect){ public:
public: template<typename ... T>
PacketConstructor(Disconnect) DisconnectPacket(T... args) : m_Data{args...} {}
const std::string& GetReason() const { const std::string& GetReason() const {
return GetField<DisconnectFieldsE, DisconnectFieldsE::Reason>(); return m_Data.m_Reason;
}
virtual sp::PacketID GetId() const {
return Disconnect;
} }
}; };
void ff() {
sizeof(std::string);
}

View File

@@ -4,19 +4,40 @@
#include <sp/protocol/Field.h> #include <sp/protocol/Field.h>
#include <sp/protocol/MessageBase.h> #include <sp/protocol/MessageBase.h>
enum class KeepAliveFieldsE {
KeepAliveId = 0, template <sp::PacketID ID, typename TData>
class ConcreteMessage {
public:
using DataType = TData;
template<typename... T>
ConcreteMessage(const T&... args) : m_Data {args ...};
private:
DataType m_Data;
virtual sp::PacketID GetId() const {
return ID;
}
}; };
using KeepAliveFields = std::tuple<
std::uint64_t //<- KeepAliveId
>;
DeclarePacket(KeepAlive){
public:
PacketConstructor(KeepAlive)
std::uint64_t GetKeepAliveId() const {
return GetField<KeepAliveFieldsE, KeepAliveFieldsE::KeepAliveId>();
struct KeepAlivePacket {
std::uint64_t m_AliveId;
};
class KeepAliveMessage : public sp::MessageBase<sp::PacketMessage, sp::option::DispatchImpl<DisconnectPacket>> {
private:
KeepAlivePacket m_Data;
public:
template <typename... T>
KeepAliveMessage(T... args) : m_Data{args...} {}
virtual sp::PacketID GetId() const {
return KeepAlive;
} }
}; };

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
enum PacketId { enum PacketIds {
KeepAlive = 0, KeepAlive = 0,
Disconnect, Disconnect,
UpgradeTower, UpgradeTower,

View File

@@ -4,30 +4,36 @@
#include <sp/protocol/Field.h> #include <sp/protocol/Field.h>
#include <sp/protocol/MessageBase.h> #include <sp/protocol/MessageBase.h>
struct UpgradeTowerPacketData {
enum class UpgradeTowerFieldsE { sp::BitField<std::uint16_t,
m_Tower = 0, sp::Field<std::uint16_t, 12>, // std::uint16_t m_Tower : 12;
m_Upgrade, sp::Field<std::uint8_t, 4> // std::uint8_t m_Upgrade : 4;
> m_TowerAndUpgrade;
sp::VarInt m_Test;
std::map<std::string, std::vector<int>> m_Test2;
}; };
using UpgradeTowerFields = std::tuple< class UpgradeTowerPacket : public sp::MessageBase<sp::PacketMessage, sp::option::DispatchImpl<UpgradeTowerPacket>> {
sp::BitField<std::uint16_t, private:
sp::Field<std::uint16_t, 12>, //<- m_Tower UpgradeTowerPacketData m_Data;
sp::Field<std::uint8_t, 4> //<- m_Upgrade
>,
sp::VarInt, //<- just for testing
std::map<std::string, std::vector<int>>
>;
DeclarePacket(UpgradeTower){ public:
public: template <typename... T>
PacketConstructor(UpgradeTower) UpgradeTowerPacket(T... args) : m_Data{args...} {}
std::uint16_t GetTowerId() const { std::uint16_t GetTowerId() const {
return GetField<0>().GetField<UpgradeTowerFieldsE, UpgradeTowerFieldsE::m_Tower>(); return m_Data.m_TowerAndUpgrade.GetField<0>();
} }
std::uint8_t GetTowerUpgrade() const { std::uint8_t GetTowerUpgrade() const {
return GetField<0>().GetField<UpgradeTowerFieldsE, UpgradeTowerFieldsE::m_Upgrade>(); return m_Data.m_TowerAndUpgrade.GetField<1>();
} }
};
virtual sp::PacketID GetId() const {
return UpgradeTower;
}
UpgradeTowerPacketData& GetData() {
return m_Data;
}
};

View File

@@ -8,7 +8,6 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <ostream> #include <ostream>
#include <sp/protocol/Field.h>
namespace sp { namespace sp {
@@ -58,7 +57,6 @@ class VarInt {
*/ */
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
friend std::ostream& operator<<(std::ostream& a_Stream, const PrintableField<VarInt>& a_VarInt);
}; };
} // namespace sp } // namespace sp

View File

@@ -3,16 +3,15 @@
#include <sp/protocol/GenericHandler.h> #include <sp/protocol/GenericHandler.h>
#include <sp/protocol/Message.h> #include <sp/protocol/Message.h>
namespace sp { namespace sp {
class PacketHandler; class PacketHandler;
using PacketID = std::uint8_t;
using PacketMessage = Message< using PacketMessage = Message<
option::MsgIdType<std::uint8_t>, // add id() operation option::MsgIdType<PacketID>, // add id() operation
option::ReadOperations, // add read() operation option::Handler<PacketHandler> // add dispatch() operation
option::WriteOperations, // add write() operation
option::WriteId, // write id before data
option::Handler<PacketHandler>, // add dispatch() operation
option::DebugPrint // add ToString() operator
>; >;
#define PacketConstructor(packetName) \ #define PacketConstructor(packetName) \
@@ -23,7 +22,7 @@ using PacketMessage = Message<
} }
#define DeclarePacket(packetName) \ #define DeclarePacket(packetName) \
class packetName##Packet : public sp::MessageBase<sp::PacketMessage, sp::option::StaticNumIdImpl<packetName>, \ class packetName##Packet : public sp::MessageBase<sp::PacketMessage, \
sp::option::DispatchImpl<packetName##Packet>, sp::option::FieldsImpl<packetName##Fields>>, \ sp::option::DispatchImpl<packetName##Packet>, sp::option::FieldsImpl<packetName##Fields>>, \
sp::option::ToStringImpl<packetName##Packet> sp::option::ToStringImpl<packetName##Packet>

View File

@@ -0,0 +1,29 @@
#pragma once
#include <sp/protocol/MessageBase.h>
namespace sp {
template <typename TData, typename TMessageID, TMessageID ID, typename THandler>
class ConcreteMessage : public MessageBase<TMessageID, THandler> {
public:
using DataType = TData;
template <typename... T>
ConcreteMessage(const T&... args) : m_Data{args...} {}
virtual ~ConcreteMessage() {}
virtual constexpr TMessageID GetId() const override {
return ID;
}
virtual void Dispatch(THandler& handler) const override {
handler.Handle(static_cast<const ConcreteMessage<TData, TMessageID, ID, THandler>&>(*this));
}
private:
DataType m_Data;
};
} // namespace sp

View File

@@ -1,134 +0,0 @@
#pragma once
#include <sp/common/Templates.h>
namespace sp {
/**
* \brief Example usage :
* sp::BitField<std::uint16_t, sp::Field<std::uint16_t, 12>, sp::Field<std::uint8_t, 4>>;
*/
template <typename TContainer, typename... TFields>
class BitField {
using AllFields = std::tuple<TFields...>;
public:
template <typename... T>
BitField(const std::tuple<T...>& args) {
Apply<0, T...>(args);
}
BitField() {}
template <typename... T>
BitField& operator=(const std::tuple<T...>& args) {
Apply<0, T...>(args);
return *this;
}
AllFields& GetFields() {
return m_Fields;
}
const AllFields& GetFields() const {
return m_Fields;
}
template <std::size_t FIndex>
auto& GetField() {
return std::get<FIndex>(this->GetFields()).GetValue();
}
template <std::size_t FIndex>
const auto& GetField() const {
return std::get<FIndex>(this->GetFields()).GetValue();
}
// allow use of enums
template <typename E, E FIndex>
const auto& GetField() const {
return std::get<static_cast<std::size_t>(FIndex)>(this->GetFields()).GetValue();
}
private:
template <int IOffset, typename... T, std::enable_if_t<IOffset >= sizeof...(T), bool> = true>
void Apply(const std::tuple<T...>& args) {}
template <int IOffset, typename... T, std::enable_if_t<!(IOffset >= sizeof...(T)), bool> = true>
void Apply(const std::tuple<T...>& args) {
this->GetField<IOffset>() = std::get<IOffset>(args);
Apply<1 + IOffset, T...>(args);
}
TContainer m_Value;
AllFields m_Fields;
};
/**
*
* \tparam ValueType the type of the value to store
* \tparam IAlignment 0 means no alignment
*/
template <typename ValueType, std::size_t IAlignment>
class Field {
public:
using StorageType = ValueType;
static constexpr std::size_t AlignmentValue = IAlignment;
// Provide an access to the stored value
StorageType& GetValue() {
return m_Value;
}
const StorageType& GetValue() const {
return m_Value;
}
Field& operator=(const StorageType& value) {
m_Value = value;
return *this;
}
constexpr std::size_t GetAlignment() const {
return IAlignment;
}
private:
StorageType m_Value;
};
template <typename T>
class PrintableField {
public:
PrintableField(const T& a_Value) : m_Value(a_Value) {}
const T& GetValue() const {
return m_Value;
}
private:
const T& m_Value;
};
namespace details {
template <typename... TFields>
struct FieldsBuilder {};
template <>
struct FieldsBuilder<> {
using Type = std::tuple<>;
};
template <typename... TFields>
struct FieldsBuilder<std::tuple<TFields...>> {
using Type = typename FieldsBuilder<TFields...>::Type;
};
template <typename TField, typename... TFields>
struct FieldsBuilder<TField, TFields...> {
using Type = sp::tuple_cat_t<std::tuple<Field<TField, 0>>, typename FieldsBuilder<TFields...>::Type>;
};
} // namespace details
} // namespace sp

View File

@@ -8,134 +8,132 @@ namespace sp
// TCommon is common interface class for all the messages // TCommon is common interface class for all the messages
// TAll is all the message types, that need to be handled, bundled in std::tuple // TAll is all the message types, that need to be handled, bundled in std::tuple
template <typename TCommon, typename TAll> template <typename TAll>
class GenericHandler; class GenericHandler;
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage // Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
template <typename TCommon, template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10, typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15, typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20, typename T16, typename T17, typename T18, typename T19, typename T20,
typename... TRest> typename... TRest>
class GenericHandler<TCommon, 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 GenericHandler<TCommon, std::tuple<TRest...> > class GenericHandler<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 GenericHandler<std::tuple<TRest...> >
{ {
using Base = GenericHandler<TCommon, std::tuple<TRest...> >; using Base = GenericHandler<std::tuple<TRest...> >;
public: public:
using Base::Handle; // Don't hide all Handle() functions from base classes using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T2& msg) {}
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T3& msg) {}
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T4& msg) {}
virtual void Handle(const T5& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T5& msg) {}
virtual void Handle(const T6& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T6& msg) {}
virtual void Handle(const T7& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T7& msg) {}
virtual void Handle(const T8& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T8& msg) {}
virtual void Handle(const T9& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T9& msg) {}
virtual void Handle(const T10& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T10& msg) {}
virtual void Handle(const T11& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T11& msg) {}
virtual void Handle(const T12& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T12& msg) {}
virtual void Handle(const T13& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T13& msg) {}
virtual void Handle(const T14& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T14& msg) {}
virtual void Handle(const T15& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T15& msg) {}
virtual void Handle(const T16& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T16& msg) {}
virtual void Handle(const T17& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T17& msg) {}
virtual void Handle(const T18& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T18& msg) {}
virtual void Handle(const T19& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T19& msg) {}
virtual void Handle(const T20& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T20& msg) {}
}; };
// 10 by 10 // 10 by 10
template <typename TCommon, template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10, typename T6, typename T7, typename T8, typename T9, typename T10,
typename... TRest> typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> > class GenericHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public GenericHandler<std::tuple<TRest...> >
{ {
using Base = GenericHandler<TCommon, std::tuple<TRest...> >; using Base = GenericHandler<std::tuple<TRest...> >;
public: public:
using Base::Handle; // Don't hide all Handle() functions from base classes using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T2& msg) {}
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T3& msg) {}
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T4& msg) {}
virtual void Handle(const T5& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T5& msg) {}
virtual void Handle(const T6& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T6& msg) {}
virtual void Handle(const T7& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T7& msg) {}
virtual void Handle(const T8& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T8& msg) {}
virtual void Handle(const T9& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T9& msg) {}
virtual void Handle(const T10& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T10& msg) {}
}; };
// 5 by 5 // 5 by 5
template <typename TCommon, template <
typename T1, typename T2, typename T3, typename T4, typename T5, typename T1, typename T2, typename T3, typename T4, typename T5,
typename... TRest> typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> > class GenericHandler<std::tuple<T1, T2, T3, T4, T5, TRest...> > : public GenericHandler<std::tuple<TRest...> >
{ {
using Base = GenericHandler<TCommon, std::tuple<TRest...> >; using Base = GenericHandler<std::tuple<TRest...> >;
public: public:
using Base::Handle; // Don't hide all Handle() functions from base classes using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T2& msg) {}
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T3& msg) {}
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T4& msg) {}
virtual void Handle(const T5& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T5& msg) {}
}; };
// Deal with rest with 4 types // Deal with rest with 4 types
template <typename TCommon, typename T1, typename T2, typename T3, typename T4> template <typename T1, typename T2, typename T3, typename T4>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4> > class GenericHandler<std::tuple<T1, T2, T3, T4> >
{ {
public: public:
virtual ~GenericHandler() {} virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T2& msg) {}
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T3& msg) {}
virtual void Handle(const T4& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T4& msg) {}
virtual void Handle(const TCommon&) { } //Nothing to do // virtual void Handle(const TCommon&) {} //Nothing to do
}; };
// Deal with rest with 3 types // Deal with rest with 3 types
template <typename TCommon, typename T1, typename T2, typename T3> template < typename T1, typename T2, typename T3>
class GenericHandler<TCommon, std::tuple<T1, T2, T3> > class GenericHandler<std::tuple<T1, T2, T3> >
{ {
public: public:
virtual ~GenericHandler() {} virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T2& msg) {}
virtual void Handle(const T3& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T3& msg) {}
virtual void Handle(const TCommon&) { } //Nothing to do // virtual void Handle(const TCommon&) {} //Nothing to do
}; };
// Deal with rest with 2 types // Deal with rest with 2 types
template <typename TCommon, typename T1, typename T2> template <typename T1, typename T2>
class GenericHandler<TCommon, std::tuple<T1, T2> > class GenericHandler<std::tuple<T1, T2> >
{ {
public: public:
virtual ~GenericHandler() {} virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const T2& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T2& msg) {}
virtual void Handle(const TCommon&) { } //Nothing to do // virtual void Handle(const TCommon&) {} //Nothing to do
}; };
// Deal with rest with 1 type // Deal with rest with 1 type
template <typename TCommon, typename T1> template <typename T1>
class GenericHandler<TCommon, std::tuple<T1> > class GenericHandler<std::tuple<T1> >
{ {
public: public:
virtual ~GenericHandler() {} virtual ~GenericHandler() {}
virtual void Handle(const T1& msg) { this->Handle(static_cast<const TCommon&>(msg)); } virtual void Handle(const T1& msg) {}
virtual void Handle(const TCommon&) { } //Nothing to do // virtual void Handle(const TCommon&) {} //Nothing to do
}; };
// Deal with rest with 0 type // Deal with rest with 0 type
template <typename TCommon> template <>
class GenericHandler<TCommon, std::tuple<> > class GenericHandler<std::tuple<> >
{ {
public: public:
virtual ~GenericHandler() {} virtual ~GenericHandler() {}
virtual void Handle(const TCommon&) { } //Nothing to do // virtual void Handle(const TCommon&) {} //Nothing to do
}; };
} // sp } // sp

View File

@@ -1,16 +0,0 @@
#pragma once
// Inspired by
// https://alex-robenko.gitbook.io/comms-protocols-cpp
#include <sp/protocol/message/MessageInterfaceBuilder.h>
namespace sp {
template <typename... TOptions>
class Message : public details::MessageInterfaceBuilder<TOptions...>::Type {
public:
using ParsedOptions = typename details::MessageInterfaceBuilder<TOptions...>::ParsedOptions;
};
} // namespace sp

View File

@@ -1,18 +1,19 @@
#pragma once #pragma once
#include <sp/protocol/Message.h>
#include <sp/protocol/message/MessageImplOptions.h>
#include <sp/protocol/message/MessagesImpl.h>
#include <sp/protocol/message/MessageImplProcess.h>
#include <sp/protocol/message/MessageImplBuilder.h>
namespace sp { namespace sp {
template <typename TBase, typename... TOptions> template <typename TMessageID, typename THandler>
class MessageBase : public details::MessageImplBuilder<TBase, TOptions...>::Type {}; class MessageBase {
public:
using HandlerType = THandler;
using MessageIdType = TMessageID;
MessageBase() {}
virtual ~MessageBase() {}
virtual MessageIdType GetId() const = 0;
virtual void Dispatch(HandlerType& handler) const = 0;
};
} // namespace sp } // namespace sp
#include <sp/protocol/message/MessagePrinterImpl.h>

View File

@@ -7,6 +7,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <vector>
namespace sp { namespace sp {
@@ -14,13 +15,12 @@ namespace sp {
* \class MessageDispatcher * \class MessageDispatcher
* \brief Class used to dispatch messages * \brief Class used to dispatch messages
*/ */
template <typename MessageIdType, typename MessageBase, typename MessageHandler> template <typename MessageBase>
class MessageDispatcher { class MessageDispatcher {
private:
std::map<MessageIdType, std::vector<MessageHandler*>> m_Handlers;
public: public:
using MessageBaseType = MessageBase; using MessageBaseType = MessageBase;
using MessageIdType = typename MessageBase::MessageIdType;
using MessageHandler = typename MessageBase::HandlerType;
/** /**
* \brief Constructor * \brief Constructor
@@ -39,21 +39,24 @@ class MessageDispatcher {
* \param handler The packet handler * \param handler The packet handler
*/ */
void RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler); void RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler);
/** /**
* \brief Unregister a packet handler * \brief Unregister a packet handler
* \param type The packet type * \param type The packet type
* \param handler The packet handler * \param handler The packet handler
*/ */
void UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler); void UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler);
/** /**
* \brief Unregister a packet handler * \brief Unregister a packet handler
* \param handler The packet handler * \param handler The packet handler
*/ */
void UnregisterHandler(MessageHandler* a_Handler); void UnregisterHandler(MessageHandler* a_Handler);
private:
std::map<MessageIdType, std::vector<MessageHandler*>> m_Handlers;
}; };
#include <sp/protocol/message/MessageDispatcherImpl.inl>
} // namespace sp } // namespace sp
#include <sp/protocol/MessageDispatcherImpl.inl>

View File

@@ -0,0 +1,43 @@
#pragma once
#include <algorithm>
#include <cassert>
namespace sp {
template <typename MessageBase>
void MessageDispatcher<MessageBase>::RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) {
assert(a_Handler);
auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler);
if (found == m_Handlers[a_MessageType].end())
m_Handlers[a_MessageType].push_back(a_Handler);
}
template <typename MessageBase>
void MessageDispatcher<MessageBase>::UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) {
auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler);
if (found != m_Handlers[a_MessageType].end())
m_Handlers[a_MessageType].erase(found);
}
template <typename MessageBase>
void MessageDispatcher<MessageBase>::UnregisterHandler(MessageHandler* a_Handler) {
for (auto& pair : m_Handlers) {
if (pair.second.empty())
continue;
MessageIdType type = pair.first;
pair.second.erase(std::remove(pair.second.begin(), pair.second.end(), a_Handler), pair.second.end());
}
}
template <typename MessageBase>
void MessageDispatcher<MessageBase>::Dispatch(const MessageBase& a_Message) {
MessageIdType type = a_Message.GetId();
for (auto& handler : m_Handlers[type]) {
a_Message.Dispatch(*handler);
}
}
} // namespace sp

View File

@@ -2,28 +2,36 @@
#include <array> #include <array>
#include <functional> #include <functional>
#include <iostream>
#include <memory> #include <memory>
#include <sp/protocol/message/ArrayFillerImpl.h> #include <sp/common/Tuples.h>
namespace sp { namespace sp {
template <typename TBase, typename TTMessages> template <typename TBase, typename TTMessages>
class MessageFactory { class MessageFactory {
public: public:
using IdType = typename TBase::MsgIdType; using IdType = typename TBase::MessageIdType;
MessageFactory() : m_Factory(details::ArrayFiller<TBase, TTMessages>::ArrayCreate()) {} MessageFactory() {
constexpr std::size_t messageCount = std::tuple_size_v<TTMessages>;
m_Factory.resize(messageCount);
TupleForEach([this](const auto& message){
std::size_t messageID = static_cast<std::size_t>(message.GetId());
using MessageType = std::remove_const_t<std::remove_reference_t<decltype(message)>>;
m_Factory.emplace(m_Factory.begin() + messageID, []() -> std::unique_ptr<TBase> { return std::make_unique<MessageType>(); });
}, TTMessages{});
}
std::unique_ptr<TBase> CreateMessage(IdType id) const { std::unique_ptr<TBase> CreateMessage(IdType id) const {
if (id >= m_Factory.size()) std::size_t idSize = static_cast<std::size_t>(id);
if (idSize >= m_Factory.size())
return nullptr; return nullptr;
return m_Factory.at(id)(); return m_Factory.at(idSize)();
} }
private: private:
details::ArrayType<TBase> m_Factory; std::vector<std::function<std::unique_ptr<TBase>(void)>> m_Factory;
}; };

View File

@@ -1,39 +0,0 @@
#pragma once
namespace sp {
namespace details {
template <typename TBase>
using ArrayType = std::vector<std::function<std::unique_ptr<TBase>(void)>>;
template <typename TBase, typename... TMessages>
struct ArrayFiller {};
template <typename TBase, typename... TMessages>
struct ArrayFiller<TBase, std::tuple<TMessages...>> {
static ArrayType<TBase> ArrayCreate() {
ArrayType<TBase> array;
array.reserve(sizeof...(TMessages));
ArrayFiller<TBase, TMessages...>::ArrayAppend(array);
return array;
}
};
template <typename TBase, typename TMessage, typename... TMessages>
struct ArrayFiller<TBase, TMessage, TMessages...> {
static void ArrayAppend(details::ArrayType<TBase>& array) {
ArrayFiller<TBase, TMessage>::ArrayAppend(array);
ArrayFiller<TBase, TMessages...>::ArrayAppend(array);
}
};
template <typename TBase, typename TMessage>
struct ArrayFiller<TBase, TMessage> {
static void ArrayAppend(details::ArrayType<TBase>& array) {
array.emplace_back([]() -> std::unique_ptr<TBase> { return std::make_unique<TMessage>(); });
}
};
} // namespace details
} // namespace sp

View File

@@ -1,36 +0,0 @@
#pragma once
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::RegisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) {
assert(a_Handler);
auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler);
if (found == m_Handlers[a_MessageType].end())
m_Handlers[a_MessageType].push_back(a_Handler);
}
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::UnregisterHandler(MessageIdType a_MessageType, MessageHandler* a_Handler) {
auto found = std::find(m_Handlers[a_MessageType].begin(), m_Handlers[a_MessageType].end(), a_Handler);
if (found != m_Handlers[a_MessageType].end())
m_Handlers[a_MessageType].erase(found);
}
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::UnregisterHandler(MessageHandler* a_Handler) {
for (auto& pair : m_Handlers) {
if (pair.second.empty())
continue;
MessageIdType type = pair.first;
pair.second.erase(std::remove(pair.second.begin(), pair.second.end(), a_Handler), pair.second.end());
}
}
template <typename MessageIdType, typename MessageBase, typename MessageHandler>
void MessageDispatcher<MessageIdType, MessageBase, MessageHandler>::Dispatch(const MessageBase& a_Message) {
MessageIdType type = a_Message.GetId();
for (auto& handler : m_Handlers[type]) {
a_Message.Dispatch(*handler);
}
}

View File

@@ -1,49 +0,0 @@
#pragma once
namespace sp {
namespace details {
// TBase is interface class
// TOptions... are the implementation options
template <typename TBase, typename... TOptions>
struct MessageImplBuilder {
// ParsedOptions class is supposed to be defined in comms::Message class
using InterfaceOptions = typename TBase::ParsedOptions;
// Parse implementation options
using ImplOptions = MessageImplParsedOptions<TOptions...>;
// Provide GetIdImpl() if possible
static const bool HasStaticNumIdImpl = InterfaceOptions::HasMsgIdType && ImplOptions::HasStaticNumIdImpl;
using Base1 = typename MessageImplProcessStaticNumId<TBase, ImplOptions, HasStaticNumIdImpl>::Type;
// Provide DispatchImpl() if possible
static const bool HasDispatchImpl = InterfaceOptions::HasHandler && ImplOptions::HasDispatchImpl;
using Base2 = typename MessageImplProcessDispatch<Base1, ImplOptions, HasDispatchImpl>::Type;
// Provide access to fields if possible
using Base3 = typename MessageImplProcessFields<Base2, ImplOptions, ImplOptions::HasFieldsImpl>::Type;
// Provide ReadImpl() if possible
static const bool HasReadImpl = InterfaceOptions::HasReadOperations && ImplOptions::HasFieldsImpl;
using Base4 = typename MessageImplProcessReadFields<Base3, HasReadImpl>::Type;
// Provide WriteImpl() if possible
static const bool HasWriteImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl;
using Base5 = typename MessageImplProcessWriteFields<Base4, HasWriteImpl>::Type;
// Provide ValidImpl() if possible
static const bool HasValidImpl = InterfaceOptions::HasValid && ImplOptions::HasFieldsImpl;
using Base6 = typename MessageImplProcessValidFields<Base5, HasValidImpl>::Type;
// Provide ToStringImpl() if possible
static const bool HasToStringImpl = InterfaceOptions::HasToString;
using Base7 = typename MessageImplProcessToString<Base6, ImplOptions, HasToStringImpl>::Type;
// The last BaseN must be taken as final type.
using Type = Base7;
};
} // namespace details
} // namespace sp

View File

@@ -1,66 +0,0 @@
#pragma once
namespace sp {
namespace option {
// Provide static numeric ID, to facilitate implementation of GetIdImpl()
template <std::uintmax_t TId>
struct StaticNumIdImpl {};
// Facilitate implementation of DispatchImpl()
template <typename TActual>
struct DispatchImpl {};
// Provide fields of the message, facilitate implementation of
// ReadImpl(), WriteImpl(), ValidImpl(), etc...
template <typename TFields>
struct FieldsImpl {};
// Print fields of the message, facilitate implementation of
// ToStringImpl()
template <typename TActual>
struct ToStringImpl {};
} // namespace option
namespace details {
template <typename... TOptions>
class MessageImplParsedOptions;
template <>
struct MessageImplParsedOptions<> {
static const bool HasStaticNumIdImpl = false;
static const bool HasDispatchImpl = false;
static const bool HasFieldsImpl = false;
};
template <std::uintmax_t TId, typename... TOptions>
struct MessageImplParsedOptions<option::StaticNumIdImpl<TId>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasStaticNumIdImpl = true;
static const std::uintmax_t MsgId = TId;
};
template <typename TActual, typename... TOptions>
struct MessageImplParsedOptions<option::DispatchImpl<TActual>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasDispatchImpl = true;
using ActualMessage = TActual;
};
template <typename TFields, typename... TOptions>
struct MessageImplParsedOptions<option::FieldsImpl<TFields>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasFieldsImpl = true;
using Fields = TFields;
};
} // namespace details
} // namespace sp

View File

@@ -1,107 +0,0 @@
#pragma once
namespace sp {
namespace details {
// ToString impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessToString;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessToString<TBase, ParsedImplOptions, true> {
using Type = MessageImplToStringBase<TBase, typename ParsedImplOptions::ActualMessage>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessToString<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// id impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessStaticNumId;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessStaticNumId<TBase, ParsedImplOptions, true> {
using Type = MessageImplStaticNumIdBase<TBase, ParsedImplOptions::MsgId>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessStaticNumId<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// dispatch impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessDispatch;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessDispatch<TBase, ParsedImplOptions, true> {
using Type = MessageImplDispatchBase<TBase, typename ParsedImplOptions::ActualMessage>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessDispatch<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// fields impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessFields;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessFields<TBase, ParsedImplOptions, true> {
using Type = MessageImplFieldsBase<TBase, typename ParsedImplOptions::Fields>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessFields<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// read impl
template <typename TBase, bool TImplement>
struct MessageImplProcessReadFields;
template <typename TBase>
struct MessageImplProcessReadFields<TBase, true> {
using Type = MessageImplFieldsReadBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessReadFields<TBase, false> {
using Type = TBase;
};
// write impl
template <typename TBase, bool TImplement>
struct MessageImplProcessWriteFields;
template <typename TBase>
struct MessageImplProcessWriteFields<TBase, true> {
using Type = MessageImplFieldsWriteBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessWriteFields<TBase, false> {
using Type = TBase;
};
// valid impl
template <typename TBase, bool TImplement>
struct MessageImplProcessValidFields;
template <typename TBase>
struct MessageImplProcessValidFields<TBase, true> {
using Type = MessageImplFieldsValidBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessValidFields<TBase, false> {
using Type = TBase;
};
} // namespace details
} // namespace sp

View File

@@ -1,44 +0,0 @@
#pragma once
#include <sp/protocol/message/MessageInterfaces.h>
namespace sp {
namespace details {
class EmptyBase {};
template <typename... TOptions>
struct MessageInterfaceBuilder {
// Parse the options
using ParsedOptions = MessageInterfaceParsedOptions<TOptions...>;
// Add ID retrieval functionality if ID type was provided
using Base1 = typename MessageInterfaceProcessMsgId<EmptyBase, ParsedOptions, ParsedOptions::HasMsgIdType>::Type;
// Add ReadData() and WriteData(), that use the right endian
using Base2 = typename MessageInterfaceProcessEndian<Base1, ParsedOptions::HasLittleEndian>::Type;
// Add read functionality if Read type was provided
using Base3 = typename MessageInterfaceProcessRead<Base2, ParsedOptions::HasReadOperations>::Type;
// Add write functionality if Write type was provided
using Base4 = typename MessageInterfaceProcessWrite<Base3, ParsedOptions::HasWriteOperations>::Type;
// add dispatch functionality if Handler type was provided
using Base5 = typename MessageInterfaceProcessHandler<Base4, ParsedOptions, ParsedOptions::HasHandler>::Type;
// add valid functionality if Valid tpe was provided
using Base6 = typename MessageInterfaceProcessValid<Base5, ParsedOptions::HasValid>::Type;
// add write id functionality if write id and write was provided
using Base7 = typename MessageInterfaceProcessWriteId<Base6, ParsedOptions::HasWriteId && ParsedOptions::HasWriteOperations>::Type;
// add ToString() if HasToString was provided
using Base8 = typename MessageInterfaceProcessToString<Base7, ParsedOptions::HasToString>::Type;
// The last Base8 must be taken as final type.
using Type = Base8;
};
} // namespace details
} // namespace sp

View File

@@ -1,119 +0,0 @@
#pragma once
namespace sp {
namespace details {
// Build message Id
template <typename TBase, typename TParsedOptions, bool THasMsgIdType>
struct MessageInterfaceProcessMsgId;
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessMsgId<TBase, TParsedOptions, true> {
using Type = MessageInterfaceIdTypeBase<TBase, typename TParsedOptions::MsgIdType>;
};
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessMsgId<TBase, TParsedOptions, false> {
using Type = TBase;
};
// Build endianess
template <typename TBase, bool THasLittleEndian>
struct MessageInterfaceProcessEndian;
template <typename TBase>
struct MessageInterfaceProcessEndian<TBase, true> {
using Type = MessageInterfaceLittleEndian<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessEndian<TBase, false> {
using Type = MessageInterfaceBigEndian<TBase>;
};
// Build read
template <typename TBase, bool THasRead>
struct MessageInterfaceProcessRead;
template <typename TBase>
struct MessageInterfaceProcessRead<TBase, true> {
using Type = MessageInterfaceReadBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessRead<TBase, false> {
using Type = TBase;
};
// Build write
template <typename TBase, bool THasWrite>
struct MessageInterfaceProcessWrite;
template <typename TBase>
struct MessageInterfaceProcessWrite<TBase, true> {
using Type = MessageInterfaceWriteBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessWrite<TBase, false> {
using Type = TBase;
};
// Build handler
template <typename TBase, typename TParsedOptions, bool THasHandler>
struct MessageInterfaceProcessHandler;
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessHandler<TBase, TParsedOptions, true> {
using Type = MessageInterfaceHandlerBase<TBase, typename TParsedOptions::HandlerType>;
};
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessHandler<TBase, TParsedOptions, false> {
using Type = TBase;
};
// Build valid
template <typename TBase, bool THasValid>
struct MessageInterfaceProcessValid;
template <typename TBase>
struct MessageInterfaceProcessValid<TBase, true> {
using Type = MessageInterfaceValidityBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessValid<TBase, false> {
using Type = TBase;
};
// Build id writing
template <typename TBase, bool THasValid>
struct MessageInterfaceProcessWriteId;
template <typename TBase>
struct MessageInterfaceProcessWriteId<TBase, true> {
using Type = MessageInterfaceWriteIdBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessWriteId<TBase, false> {
using Type = TBase;
};
// Build to string
template <typename TBase, bool THasToString>
struct MessageInterfaceProcessToString;
template <typename TBase>
struct MessageInterfaceProcessToString<TBase, true> {
using Type = MessageInterfaceToStringBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessToString<TBase, false> {
using Type = TBase;
};
} // namespace details
} // namespace sp

View File

@@ -1,7 +0,0 @@
#pragma once
#include <sp/protocol/message/MessageOptions.h>
#include <sp/protocol/message/MessageInterfacesOptions.h>
#include <sp/protocol/message/MessageInterfacesImpl.h>
#include <sp/protocol/message/MessageInterfaceProcess.h>

View File

@@ -1,145 +0,0 @@
#pragma once
#include <sp/common/ByteSwapping.h>
namespace sp {
namespace details {
// ID retrieval chunk
template <typename TBase, typename TId>
class MessageInterfaceIdTypeBase : public TBase {
public:
using MsgIdType = TId;
MsgIdType GetId() const {
return GetIdImpl();
}
protected:
virtual MsgIdType GetIdImpl() const = 0;
};
// Big endian serialisation chunk
template <typename TBase>
class MessageInterfaceBigEndian : public TBase {
protected:
template <typename T>
void ReadData(T& value, DataBuffer& buffer) {
buffer >> value;
FromNetwork(value);
}
template <typename T>
void WriteData(T value, DataBuffer& buffer) const {
ToNetwork(value);
buffer << value;
}
};
// Little endian serialisation chunk
template <typename TBase>
class MessageInterfaceLittleEndian : public TBase {
protected:
template <typename T>
void ReadData(T& value, DataBuffer& buffer) {
buffer >> value;
TrySwapBytes(value);
FromNetwork(value);
}
template <typename T>
void WriteData(const T& value, DataBuffer& buffer) {
ToNetwork(value);
TrySwapBytes(value);
buffer << value;
}
};
// Read functionality chunk
template <typename TBase>
class MessageInterfaceReadBase : public TBase {
public:
void Read(DataBuffer& buffer) {
return ReadImpl(buffer);
}
protected:
virtual void ReadImpl(DataBuffer& buffer) = 0;
};
// Write functionality chunk
template <typename TBase>
class MessageInterfaceWriteBase : public TBase {
public:
void Write(DataBuffer& buffer) const {
WriteImpl(buffer);
}
// helper
DataBuffer Write() const {
DataBuffer buffer;
this->Write(buffer);
return buffer;
}
protected:
virtual void WriteImpl(DataBuffer& buffer) const = 0;
};
// Handler functionality chunk
template <typename TBase, typename THandler>
class MessageInterfaceHandlerBase : public TBase {
public:
using HandlerType = typename THandler::HandlerT;
void Dispatch(HandlerType& handler) const {
DispatchImpl(handler);
}
protected:
virtual void DispatchImpl(HandlerType& handler) const = 0;
};
// Validity functionality chunk
template <typename TBase>
class MessageInterfaceValidityBase : public TBase {
public:
bool Valid() const {
return ValidImpl();
}
protected:
virtual bool ValidImpl() const = 0;
};
// Writing id functionality chunk
template <typename TBase>
class MessageInterfaceWriteIdBase : public TBase {
public:
void Write(DataBuffer& buffer) const {
buffer << VarInt{this->GetId()};
this->WriteImpl(buffer);
}
// helper
DataBuffer Write() const {
DataBuffer buffer;
this->Write(buffer);
return buffer;
}
};
// Debug print functionality chunk
template <typename TBase>
class MessageInterfaceToStringBase : public TBase {
public:
friend std::ostream& operator<<(std::ostream& a_Stream, const MessageInterfaceToStringBase& a_Message) {
return a_Message.OpOutImpl(a_Stream);
}
protected:
virtual std::ostream& OpOutImpl(std::ostream& a_Stream) const = 0;
};
} // namespace details
} // namespace sp

View File

@@ -1,69 +0,0 @@
#pragma once
namespace sp {
namespace details {
template <typename... TOptions>
struct MessageInterfaceParsedOptions {};
template <>
struct MessageInterfaceParsedOptions<> {
static const bool HasMsgIdType = false;
static const bool HasLittleEndian = false;
static const bool HasReadOperations = false;
static const bool HasWriteOperations = false;
static const bool HasWriteId = false;
static const bool HasHandler = false;
static const bool HasValid = false;
static const bool HasToString = false;
};
template <typename T, typename... TOptions>
struct MessageInterfaceParsedOptions<option::MsgIdType<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasMsgIdType = true;
using MsgIdType = T;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::LittleEndian, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasLittleEndian = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::ReadOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasReadOperations = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::WriteOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasWriteOperations = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::WriteId, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasWriteId = true;
};
template <typename T, typename... TOptions>
struct MessageInterfaceParsedOptions<option::Handler<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasHandler = true;
using HandlerType = option::Handler<T>;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::ValidCheckInterface, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasValid = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<option::DebugPrint, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasToString = true;
};
} // namespace details
} // namespace sp

View File

@@ -1,37 +0,0 @@
#pragma once
#include <sp/common/DataBuffer.h>
namespace sp {
namespace option {
// Define type used to store message ID
template <typename T>
struct MsgIdType {};
// Enable reading
struct ReadOperations {};
// Enable writing
struct WriteOperations {};
// Enable id writing
struct WriteId {};
// Use little endian for serialisation (instead of default big)
struct LittleEndian {};
// Include validity check in public interface
struct ValidCheckInterface {};
// Add a ToString() method containing fields
struct DebugPrint {};
// Define handler class
template <typename T>
struct Handler {
using HandlerT = T;
};
} // namespace option
} // namespace sp

View File

@@ -1,79 +0,0 @@
#pragma once
#include <sp/common/Reflection.h>
#include <sp/common/Templates.h>
#include <sp/protocol/MessagePrinter.h>
namespace sp {
namespace details {
template <typename... TOptions>
struct IdPrinter {};
template <>
struct IdPrinter<> {
static std::string PrintMessageId() {
return "";
}
};
template <typename TOption, typename... TOptions>
struct IdPrinter<TOption, TOptions...> {
static std::string PrintMessageId() {
return IdPrinter<TOptions...>::PrintMessageId();
}
};
template <typename... TOptions, std::uintmax_t TId>
struct IdPrinter<option::StaticNumIdImpl<TId>, TOptions...> {
static std::string PrintMessageId() {
return "(Id: " + std::to_string(TId) + ")";
}
};
} // namespace details
template <typename... TFields>
std::ostream& operator<<(std::ostream& a_Stream, const std::tuple<TFields...>& a_Fields);
template <typename TField, unsigned int IAlignment>
struct FieldPrinter {
static std::ostream& PrintField(std::ostream& a_Stream, const sp::Field<TField, IAlignment>& a_Field) {
return a_Stream << sp::Reflector<TField>::GetClassName() << "=" << PrintableField<TField>(a_Field.GetValue());
}
};
template <unsigned int IAlignment, typename TContainer, typename... TFields>
struct FieldPrinter<sp::BitField<TContainer, TFields...>, IAlignment> {
static std::ostream& PrintField(
std::ostream& a_Stream, const sp::Field<sp::BitField<TContainer, TFields...>, IAlignment>& a_Field) {
a_Stream << "BitField<" << sp::Reflector<TContainer>::GetClassName() << ">[";
a_Stream << a_Field.GetValue().GetFields() << "]";
return a_Stream;
}
};
template <typename... TFields>
std::ostream& operator<<(std::ostream& a_Stream, const std::tuple<TFields...>& a_Fields) {
bool first = true;
TupleForEach(
[&a_Stream, &first](const auto& a_Field) {
if (!first)
a_Stream << ", ";
using TField = typename std::decay<decltype(a_Field)>::type;
constexpr std::size_t alignment = TField::AlignmentValue;
FieldPrinter<typename TField::StorageType, alignment>::PrintField(a_Stream, a_Field);
first = false;
},
a_Fields);
return a_Stream;
}
template <typename TBase, typename... TOptions>
std::ostream& operator<<(std::ostream& a_Stream, const sp::MessageBase<TBase, TOptions...>& a_Message) {
a_Stream << sp::GetBasicClassName(a_Message) << sp::details::IdPrinter<TOptions...>::PrintMessageId() << "["
<< a_Message.GetFields() << "]";
return a_Stream;
}
} // namespace sp

View File

@@ -1,178 +0,0 @@
#pragma once
namespace sp {
template <typename TBase, typename... TOptions>
class MessageBase;
namespace details {
template <typename TBase, typename... TOptions>
std::string PrintMessage(const MessageBase<TBase, TOptions...>& a_Message);
// ID information chunk
template <typename TBase, typename TActual>
class MessageImplToStringBase : public TBase {
protected:
virtual std::ostream& OpOutImpl(std::ostream& a_Stream) const override{
return a_Stream << static_cast<const TActual&>(*this);
}
};
// ID information chunk
template <typename TBase, std::intmax_t TId>
class MessageImplStaticNumIdBase : public TBase {
public:
// Reuse the message ID type defined in the interface
using MsgIdType = typename TBase::MsgIdType;
protected:
virtual MsgIdType GetIdImpl() const override {
return static_cast<MsgIdType>(TId);
}
};
// Dispatch implementation chunk
template <typename TBase, typename TActual>
class MessageImplDispatchBase : public TBase {
public:
// Reuse the Handler type defined in the interface class
using Handler = typename TBase::HandlerType;
protected:
virtual void DispatchImpl(Handler& handler) const override {
handler.Handle(static_cast<const TActual&>(*this));
}
};
template <typename TBase, typename TFields>
class MessageImplFieldsBase : public TBase {
public:
using AllFields = typename details::FieldsBuilder<TFields>::Type;
template <typename... Args>
void Construct(Args... args) {
m_Fields = std::make_tuple(args...);
}
AllFields& GetFields() {
return m_Fields;
}
const AllFields& GetFields() const {
return m_Fields;
}
template <std::size_t FIndex>
auto& GetField() {
return std::get<FIndex>(GetFields()).GetValue();
}
template <std::size_t FIndex>
const auto& GetField() const {
return std::get<FIndex>(GetFields()).GetValue();
}
// allow use of enums
template <typename E, E FIndex>
const auto& GetField() const {
return std::get<static_cast<std::size_t>(FIndex)>(this->GetFields()).GetValue();
}
private:
AllFields m_Fields;
};
template <typename TBase>
class MessageImplFieldsReadBase : public TBase {
private:
// normal reading
template <typename TField>
void ReadField(Field<TField, 0>& field, DataBuffer& buffer) {
this->ReadData(field.GetValue(), buffer);
}
// reading field in bitfield
template <typename TFieldType, typename TField, std::size_t IAlignment>
void ReadField(Field<TField, IAlignment>& field, TFieldType data, std::size_t offset) {
static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8;
// we suppose that the first element is at the highest bits
field.GetValue() = (data >> TotalBitCount - IAlignment - offset) & ((1 << IAlignment) - 1);
}
// reading bitfield
template <typename TContainer, typename TFirst, typename... TFields>
void ReadField(Field<BitField<TContainer, TFirst, TFields...>, 0>& field, DataBuffer& buffer) {
TContainer data;
this->ReadData(data, buffer);
std::size_t offset = 0;
TupleForEach(
[data, this, &offset](auto& field) {
this->ReadField(field, data, offset);
offset += field.GetAlignment();
},
field.GetValue().GetFields());
}
void ReadImpl(DataBuffer& buffer) override {
auto& allFields = this->GetFields();
TupleForEach([&buffer, this](auto& field) { this->ReadField(field, buffer); }, allFields);
}
};
template <typename TBase>
class MessageImplFieldsWriteBase : public TBase {
private:
// normal writing
template <typename TField>
void WriteField(const Field<TField, 0>& field, DataBuffer& buffer) const {
this->WriteData(field.GetValue(), buffer);
}
// writing field in bitfield
template <typename TFieldType, typename TField, std::size_t IAlignment>
void WriteField(const Field<TField, IAlignment>& field, TFieldType& data, std::size_t offset) const {
static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8;
// we suppose that the first element is at the highest bits
data |= (field.GetValue() & ((1 << IAlignment) - 1)) << TotalBitCount - IAlignment - offset;
}
// writing bitfield
template <typename TContainer, typename TFirst, typename... TFields>
void WriteField(const Field<BitField<TContainer, TFirst, TFields...>, 0>& field, DataBuffer& buffer) const {
TContainer data = 0;
std::size_t offset = 0;
TupleForEach(
[&data, this, &offset](auto& field) {
this->WriteField(field, data, offset);
offset += field.GetAlignment();
},
field.GetValue().GetFields());
this->WriteData(data, buffer);
}
void WriteImpl(DataBuffer& buffer) const override {
auto& allFields = this->GetFields();
TupleForEach([&buffer, this](const auto& field) { this->WriteField(field, buffer); }, allFields);
}
};
template <typename TBase>
class MessageImplFieldsValidBase : public TBase {
protected:
bool ValidImpl() const override {
// Access fields via interface provided in previous chunk
// auto& allFields = TBase::GetFields();
//... // validate all the fields
return true;
}
};
} // namespace details
} // namespace sp

View File

@@ -1,56 +0,0 @@
// infix_iterator.h
//
// Lifted from Jerry Coffin's 's prefix_ostream_iterator
#pragma once
#include <ostream>
#include <sp/protocol/Field.h>
namespace sp {
template <class T, class charT = char, class traits = std::char_traits<charT>>
class OstreamFieldIterator {
private:
std::basic_ostream<charT, traits>* m_Os;
std::string m_Delimiter;
bool m_FirstElem;
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
using char_type = charT;
using traits_type = traits;
using ostream_type = std::basic_ostream<charT, traits>;
OstreamFieldIterator(ostream_type& a_Stream) : m_Os(&a_Stream), m_Delimiter(0), m_FirstElem(true) {}
OstreamFieldIterator(ostream_type& a_Stream, std::string&& a_Delimiter) :
m_Os(&a_Stream), m_Delimiter(std::move(a_Delimiter)), m_FirstElem(true) {}
auto& operator=(const T& item) {
// Here's the only real change from ostream_iterator:
// Normally, the '*m_Os << item;' would come before the 'if'.
if (!m_FirstElem && !m_Delimiter.empty())
*m_Os << m_Delimiter;
*m_Os << sp::PrintableField<T>(item);
m_FirstElem = false;
return *this;
}
auto& operator*() {
return *this;
}
auto& operator++() {
return *this;
}
auto& operator++(int) {
return *this;
}
};
} // namespace sp

View File

@@ -49,8 +49,4 @@ DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
return in; return in;
} }
std::ostream& operator<<(std::ostream& a_Stream, const PrintableField<VarInt>& a_VarInt) {
return a_Stream << a_VarInt.GetValue().GetValue();
}
} // namespace sp } // namespace sp

View File

@@ -1,42 +0,0 @@
#include <iostream>
#include <examples/PacketExample.h>
#include <sp/io/File.h>
class CustomPacketHandler : public sp::PacketHandler {
void Handle(const KeepAlivePacket& packet) {
std::cout << "KeepAlive handled ! " << packet.GetKeepAliveId() << "\n";
}
void Handle(const DisconnectPacket& packet) {
std::cout << "Disconnect handled ! " << packet.GetReason() << "\n";
}
void Handle(const UpgradeTowerPacket& packet) {
std::cout << "UpgradeTower handled !\n";
}
};
using FileStream = sp::io::Stream<sp::io::FileTag, sp::PacketDispatcher, sp::PacketFactory, sp::option::ZlibCompress>;
int main() {
auto handler = std::make_shared<CustomPacketHandler>();
FileStream stream(sp::io::File{"test.txt", sp::io::FileTag::In | sp::io::FileTag::Out}, {});
stream.GetDispatcher().RegisterHandler(PacketId::Disconnect, handler.get());
stream.GetDispatcher().RegisterHandler(PacketId::KeepAlive, handler.get());
stream.SendMessage(KeepAlivePacket{96});
stream.SendMessage(KeepAlivePacket{69});
stream.SendMessage(DisconnectPacket{
"This is in the "
"fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile !"});
stream.GetOption<sp::option::ZlibCompress>().m_Enabled = false;
stream.SendMessage(DisconnectPacket{
"This is in the "
"fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile !"});
stream.RecieveMessages();
return 0;
}

View File

@@ -1,40 +0,0 @@
#include <iostream>
#include <examples/PacketExample.h>
#include <sp/io/Memory.h>
using DataBufferStream = sp::io::Stream<sp::io::MemoryTag, sp::PacketDispatcher, sp::PacketFactory>;
class CustomPacketHandler : public sp::PacketHandler {
void Handle(const KeepAlivePacket& packet) {
std::cout << "KeepAlive handled ! " << packet.GetKeepAliveId() << "\n";
}
void Handle(const DisconnectPacket& packet) {
std::cout << "Disconnect handled ! " << packet.GetReason() << "\n";
}
void Handle(const UpgradeTowerPacket& packet) {
std::cout << "UpgradeTower handled !\n";
}
};
int main() {
auto handler = std::make_shared<CustomPacketHandler>();
DataBufferStream stream;
stream.GetDispatcher().RegisterHandler(PacketId::Disconnect, handler.get());
// this should not be dispatched
stream.SendMessage(KeepAlivePacket{96});
stream.RecieveMessages();
stream.GetDispatcher().RegisterHandler(PacketId::KeepAlive, handler.get());
stream.SendMessage(KeepAlivePacket{69});
stream.RecieveMessages();
stream.SendMessage(DisconnectPacket{"A valid reason"});
stream.RecieveMessages();
return 0;
}

49
test/test_message.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include <sp/protocol/ConcreteMessage.h>
#include <sp/protocol/GenericHandler.h>
#include <sp/protocol/MessageDispatcher.h>
#include <sp/protocol/MessageFactory.h>
#include <cstdint>
#include <iostream>
enum class PacketID { KeepAlive = 0};
class PacketHandler;
using PacketBase = sp::MessageBase<PacketID, PacketHandler>;
template <typename TData, PacketID ID>
using Message = sp::ConcreteMessage<TData, PacketID, ID, PacketHandler>;
struct KeepAlivePacket {
std::uint64_t m_KeepAlive;
};
using KeepAliveMessage = Message<KeepAlivePacket, PacketID::KeepAlive>;
using AllMessages = std::tuple<KeepAliveMessage>;
class PacketHandler : public sp::GenericHandler<AllMessages> {};
class MyHandler : public PacketHandler {
public:
virtual void Handle(const KeepAliveMessage& msg) {
std::cout << "yo !" << "\n";
}
};
using PacketDispatcher = sp::MessageDispatcher<PacketBase>;
using PacketFactory = sp::MessageFactory<PacketBase, AllMessages>;
int main() {
KeepAliveMessage m{5U};
MyHandler h;
PacketDispatcher d;
d.RegisterHandler(PacketID::KeepAlive, &h);
d.Dispatch(m);
PacketFactory f;
auto message = f.CreateMessage(PacketID::KeepAlive);
d.Dispatch(*message);
return 0;
}

View File

@@ -1,61 +0,0 @@
#include <iostream>
#include <examples/PacketExample.h>
#include <memory>
#include <sp/default/DefaultPacketDispatcher.h>
#include <sp/extensions/Extensions.h>
class KeepAliveHandler : public sp::PacketHandler {
void Handle(const KeepAlivePacket& packet) {
std::cout << "KeepAlive handled !!\n";
std::cout << packet << std::endl;
}
void Handle(const DisconnectPacket& packet) {
std::cout << "Disconnect handled !\n";
}
void Handle(const UpgradeTowerPacket& packet) {
std::cout << "UpgradeTower handled !\n";
}
};
int main() {
std::map<std::string, std::vector<int>> yes = {{"woa", {5, 8}}, {"insane", {6, 9}}};
auto upgradeTower = std::make_unique<UpgradeTowerPacket>(std::make_tuple(666, 9), 789, yes);
auto keepAlive = std::make_unique<KeepAlivePacket>(6969);
sp::PacketMessage* msg = upgradeTower.get();
auto handler = std::make_shared<KeepAliveHandler>();
msg->Dispatch(*handler);
keepAlive->Dispatch(*handler);
sp::DataBuffer buffer = msg->Write();
std::uint8_t msgId;
buffer >> msgId;
auto upgradeTower2 = std::make_unique<UpgradeTowerPacket>();
upgradeTower2->Read(buffer);
std::cout << "Test : " << *msg << "\n";
sp::PacketFactory factory;
auto packet = factory.CreateMessage(msgId);
if (packet == nullptr) {
std::cout << "Bad ID !\n";
return 1;
}
std::cout << (unsigned)packet->GetId() << std::endl;
packet->Dispatch(*handler);
sp::PacketDispatcher dispatcher;
dispatcher.RegisterHandler(PacketId::KeepAlive, handler.get());
dispatcher.Dispatch(*packet);
dispatcher.UnregisterHandler(PacketId::KeepAlive, handler.get());
dispatcher.UnregisterHandler(handler.get());
return 0;
}

View File

@@ -1,5 +1,9 @@
add_rules("mode.debug", "mode.release") add_rules("mode.debug", "mode.release")
add_requires("boost_pfr")
set_warnings("all")
set_languages("c++17") set_languages("c++17")
local modules = { local modules = {
@@ -71,6 +75,7 @@ target("SimpleProtocol")
add_files("src/sp/**.cpp") add_files("src/sp/**.cpp")
set_group("Library") set_group("Library")
set_kind("$(kind)") set_kind("$(kind)")
add_packages("boost_pfr", {public = true})
add_headerfiles("include/(sp/**.h)", "include/(sp/**.inl)") add_headerfiles("include/(sp/**.h)", "include/(sp/**.inl)")