dirty messages
This commit is contained in:
61
include/sp/Field.h
Normal file
61
include/sp/Field.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/common/DataBuffer.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
template <typename ValueType>
|
||||||
|
class Field {
|
||||||
|
public:
|
||||||
|
// Provide an access to the stored value
|
||||||
|
ValueType& GetValue();
|
||||||
|
const ValueType& GetValue() const;
|
||||||
|
|
||||||
|
// Read (deserialise) and update internal value
|
||||||
|
void Read(DataBuffer& buffer) {
|
||||||
|
buffer >> m_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write (serialise) internal value
|
||||||
|
void Write(DataBuffer& buffer) const {
|
||||||
|
buffer << m_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field& operator=(const ValueType& value) {
|
||||||
|
m_Value = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ValueType m_Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functor used to read all tuple values
|
||||||
|
class FieldReader {
|
||||||
|
public:
|
||||||
|
FieldReader(DataBuffer& buffer) : m_Buffer(buffer) {}
|
||||||
|
|
||||||
|
template <typename TField>
|
||||||
|
void operator()(TField& field) {
|
||||||
|
field.Read(m_Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataBuffer& m_Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functor used to write all tuple values
|
||||||
|
class FieldWriter {
|
||||||
|
public:
|
||||||
|
FieldWriter(DataBuffer& buffer) : m_Buffer(buffer) {}
|
||||||
|
|
||||||
|
template <typename TField>
|
||||||
|
void operator()(TField& field) {
|
||||||
|
field.Write(m_Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataBuffer& m_Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sp
|
||||||
@@ -1,38 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <sp/MessageInterfaceBuilder.h>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <sp/common/DataBuffer.h>
|
|
||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
class Handler;
|
|
||||||
|
|
||||||
class Message {
|
template <typename... TOptions>
|
||||||
|
class Message : public option::MessageInterfaceBuilder<TOptions...>::Type {
|
||||||
public:
|
public:
|
||||||
virtual ~Message() {}
|
using ParsedOptions = typename option::MessageInterfaceBuilder<TOptions...>::ParsedOptions;
|
||||||
|
|
||||||
void Write(DataBuffer& buffer) const {
|
|
||||||
buffer << GetId();
|
|
||||||
WriteImpl(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Read(const DataBuffer& buffer) {
|
|
||||||
ReadImpl(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dispatch(Handler& handler) {
|
|
||||||
DispatchImpl(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int GetId() const = 0;
|
|
||||||
|
|
||||||
virtual std::string_view GetName() const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void DispatchImpl(Handler& handler) = 0;
|
|
||||||
virtual void WriteImpl(DataBuffer& buffer) const = 0;
|
|
||||||
virtual void ReadImpl(const DataBuffer& buffer) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sp
|
} // namespace sp
|
||||||
@@ -3,16 +3,270 @@
|
|||||||
#include <sp/Message.h>
|
#include <sp/Message.h>
|
||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
|
namespace option {
|
||||||
|
|
||||||
template <typename TDerived>
|
// Provide static numeric ID, to facilitate implementation of GetIdImpl()
|
||||||
class MessageBase : public Message {
|
template <std::intmax_t TId>
|
||||||
protected:
|
struct StaticNumIdImpl {};
|
||||||
virtual void DispatchImpl(Handler& handler) override {
|
|
||||||
handler.Handle(static_cast<TDerived&>(*this));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void WriteImpl(DataBuffer& buffer) const = 0;
|
// Facilitate implementation of DispatchImpl()
|
||||||
virtual void ReadImpl(const DataBuffer& buffer) = 0;
|
template <typename TActual>
|
||||||
|
struct DispatchImpl {};
|
||||||
|
|
||||||
|
// Provide fields of the message, facilitate implementation of
|
||||||
|
// ReadImpl(), WriteImpl(), ValidImpl(), etc...
|
||||||
|
template <typename TFields>
|
||||||
|
struct FieldsImpl {};
|
||||||
|
|
||||||
|
} // namespace option
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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::intmax_t TId, typename... TOptions>
|
||||||
|
struct MessageImplParsedOptions<option::StaticNumIdImpl<TId>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
|
||||||
|
static const bool HasStaticNumIdImpl = true;
|
||||||
|
static const std::intmax_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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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) override {
|
||||||
|
handler.Handle(static_cast<TActual&>(*this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename TBase, typename TFields>
|
||||||
|
class MessageImplFieldsBase : public TBase {
|
||||||
|
public:
|
||||||
|
using AllFields = TFields;
|
||||||
|
|
||||||
|
AllFields& GetFields() {
|
||||||
|
return m_Fields;
|
||||||
|
}
|
||||||
|
const AllFields& GetFields() const {
|
||||||
|
return m_Fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TFields m_Fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TBase>
|
||||||
|
class MessageImplFieldsReadBase : public TBase {
|
||||||
|
protected:
|
||||||
|
void ReadImpl(DataBuffer& buffer) override {
|
||||||
|
// Access fields via interface provided in previous chunk
|
||||||
|
auto& allFields = TBase::GetFields();
|
||||||
|
//... // read all the fields
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TBase>
|
||||||
|
class MessageImplFieldsWriteBase : public TBase {
|
||||||
|
protected:
|
||||||
|
void WriteImpl(DataBuffer& buffer) override {
|
||||||
|
// Access fields via interface provided in previous chunk
|
||||||
|
auto& allFields = TBase::GetFields();
|
||||||
|
//... // write all the fields
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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::HasWriteOperations && ImplOptions::HasFieldsImpl;
|
||||||
|
using Base6 = typename MessageImplProcessValidFields<Base5, HasValidImpl>::Type;
|
||||||
|
|
||||||
|
// The last BaseN must be taken as final type.
|
||||||
|
using Type = Base6;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename TBase, typename... TOptions>
|
||||||
|
class MessageBase : public MessageImplBuilder<TBase, TOptions...>::Type {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace sp
|
} // namespace sp
|
||||||
38
include/sp/MessageInterfaceBuilder.h
Normal file
38
include/sp/MessageInterfaceBuilder.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/MessageInterfaces.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
namespace option {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// The last Base6 must be taken as final type.
|
||||||
|
using Type = Base6;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace option
|
||||||
|
} // namespace sp
|
||||||
243
include/sp/MessageInterfaces.h
Normal file
243
include/sp/MessageInterfaces.h
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/Options.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
namespace option {
|
||||||
|
|
||||||
|
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 HasHandler = false;
|
||||||
|
static const bool HasValid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename... TOptions>
|
||||||
|
struct MessageInterfaceParsedOptions<MsgIdType<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
|
||||||
|
static const bool HasMsgIdType = true;
|
||||||
|
using MsgIdType = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... TOptions>
|
||||||
|
struct MessageInterfaceParsedOptions<LittleEndian, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
|
||||||
|
static const bool HasLittleEndian = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... TOptions>
|
||||||
|
struct MessageInterfaceParsedOptions<ReadOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
|
||||||
|
static const bool HasReadOperations = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... TOptions>
|
||||||
|
struct MessageInterfaceParsedOptions<WriteOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
|
||||||
|
static const bool HasWriteOperations = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename... TOptions>
|
||||||
|
struct MessageInterfaceParsedOptions<Handler<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
|
||||||
|
static const bool HasHandler = true;
|
||||||
|
using HandlerType = Handler<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... TOptions>
|
||||||
|
struct MessageInterfaceParsedOptions<ValidCheckInterface, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
|
||||||
|
static const bool HasValid = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
static T ReadData(DataBuffer& buffer) {
|
||||||
|
// use big endian
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void WriteData(T value, DataBuffer& buffer) {
|
||||||
|
// use big endian
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Little endian serialisation chunk
|
||||||
|
template <typename TBase>
|
||||||
|
class MessageInterfaceLittleEndian : public TBase {
|
||||||
|
protected:
|
||||||
|
template <typename T>
|
||||||
|
static T ReadData(DataBuffer& buffer) {
|
||||||
|
// use little endian
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void WriteData(const T& value, DataBuffer& buffer) {
|
||||||
|
// use little endian
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return WriteImpl(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void WriteImpl(DataBuffer& buffer) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler functionality chunk
|
||||||
|
template <typename TBase, typename THandler>
|
||||||
|
class MessageInterfaceHandlerBase : public TBase {
|
||||||
|
public:
|
||||||
|
using HandlerType = typename THandler::HandlerT;
|
||||||
|
|
||||||
|
void Dispatch(HandlerType& handler) {
|
||||||
|
DispatchImpl(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void DispatchImpl(HandlerType& handler) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validity functionality chunk
|
||||||
|
template <typename TBase>
|
||||||
|
class MessageInterfaceValidityBase : public TBase {
|
||||||
|
public:
|
||||||
|
bool Valid() const {
|
||||||
|
return ValidImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool ValidImpl() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
} // namespace option
|
||||||
|
} // namespace sp
|
||||||
31
include/sp/Options.h
Normal file
31
include/sp/Options.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#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 {};
|
||||||
|
|
||||||
|
// Use little endian for serialisation (instead of default big)
|
||||||
|
struct LittleEndian {};
|
||||||
|
|
||||||
|
// Include validity check in public interface
|
||||||
|
struct ValidCheckInterface {};
|
||||||
|
|
||||||
|
// Define handler class
|
||||||
|
template <typename T>
|
||||||
|
struct Handler {
|
||||||
|
using HandlerT = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace option
|
||||||
|
} // namespace sp
|
||||||
@@ -44,4 +44,43 @@ void loop(LoopBody&& loop_body) {
|
|||||||
loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body));
|
loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
template <std::size_t TRem>
|
||||||
|
class TupleForEachHelper {
|
||||||
|
public:
|
||||||
|
template <typename TTuple, typename TFunc>
|
||||||
|
static void exec(TTuple&& tuple, TFunc&& func) {
|
||||||
|
using Tuple = typename std::decay<TTuple>::type;
|
||||||
|
static const std::size_t TupleSize = std::tuple_size<Tuple>::value;
|
||||||
|
static_assert(TRem <= TupleSize, "Incorrect parameters");
|
||||||
|
|
||||||
|
// Invoke function with current element
|
||||||
|
static const std::size_t Idx = TupleSize - TRem;
|
||||||
|
func(std::get<Idx>(std::forward<TTuple>(tuple)));
|
||||||
|
|
||||||
|
// Compile time recursion - invoke function with the remaining elements
|
||||||
|
TupleForEachHelper<TRem - 1>::exec(std::forward<TTuple>(tuple), std::forward<TFunc>(func));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class TupleForEachHelper<0> {
|
||||||
|
public:
|
||||||
|
// Stop compile time recursion
|
||||||
|
template <typename TTuple, typename TFunc>
|
||||||
|
static void exec(TTuple&& tuple, TFunc&& func) {
|
||||||
|
static_cast<void>(tuple);
|
||||||
|
static_cast<void>(func);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
template <typename TTuple, typename TFunc>
|
||||||
|
void tupleForEach(TTuple&& tuple, TFunc&& func) {
|
||||||
|
using Tuple = typename std::decay<TTuple>::type;
|
||||||
|
static const std::size_t TupleSize = std::tuple_size<Tuple>::value;
|
||||||
|
|
||||||
|
details::TupleForEachHelper<TupleSize>::exec(std::forward<TTuple>(tuple), std::forward<TFunc>(func));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sp
|
} // namespace sp
|
||||||
|
|||||||
151
src/main.cpp
151
src/main.cpp
@@ -1,125 +1,58 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <sp/Field.h>
|
||||||
#include <sp/GenericHandler.h>
|
#include <sp/GenericHandler.h>
|
||||||
#include <sp/MessageBase.h>
|
#include <sp/MessageBase.h>
|
||||||
#include <sp/Templates.h>
|
|
||||||
|
|
||||||
#define PacketClass(className) class className : public sp::MessageBase<className>
|
#include <memory>
|
||||||
|
|
||||||
template <typename T>
|
enum MyMsgId {
|
||||||
class Packet : public sp::MessageBase<T> {
|
MyMsgId_Msg1,
|
||||||
public:
|
MyMsgId_Msg2,
|
||||||
virtual std::string_view GetName() const = 0;
|
|
||||||
void WriteImpl(sp::DataBuffer& buffer) const = 0;
|
|
||||||
void ReadImpl(const sp::DataBuffer& buffer) = 0;
|
|
||||||
int GetId() const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
class MyHandler; // forward declaration of the handler class.
|
||||||
class Command : public sp::MessageBase<T> {
|
|
||||||
|
using MyMessage = sp::Message<sp::option::MsgIdType<MyMsgId>, // add id() operation
|
||||||
|
sp::option::ReadOperations, // add read() operation
|
||||||
|
sp::option::WriteOperations, // add write() operation
|
||||||
|
sp::option::Handler<MyHandler>, // add dispatch() operation
|
||||||
|
sp::option::ValidCheckInterface, // add valid() operation
|
||||||
|
sp::option::LittleEndian // use little endian for serialisation
|
||||||
|
>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using ActualMessage1Fields = std::tuple<sp::Field<std::uint16_t>, sp::Field<std::int8_t>>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename TMessageInterface>
|
||||||
|
class ActualMessage1 : public sp::MessageBase<TMessageInterface,
|
||||||
|
sp::option::StaticNumIdImpl<MyMsgId_Msg1>, // provide idImpl() if needed
|
||||||
|
sp::option::DispatchImpl<ActualMessage1<TMessageInterface>>, // provide dispatchImpl() if needed
|
||||||
|
sp::option::FieldsImpl<ActualMessage1Fields> // provide access to fields and
|
||||||
|
// readImpl(), writeImpl(),
|
||||||
|
// lengthImpl(), validImpl()
|
||||||
|
// functions if needed
|
||||||
|
> {};
|
||||||
|
|
||||||
|
using MyActualMessage1 = ActualMessage1<MyMessage>;
|
||||||
|
|
||||||
|
using AllMessages = std::tuple<MyActualMessage1>;
|
||||||
|
|
||||||
|
class MyHandler : public sp::GenericHandler<MyMessage, AllMessages> {
|
||||||
public:
|
public:
|
||||||
virtual std::string_view GetName() const = 0;
|
void Handle(MyActualMessage1& msg) {
|
||||||
void WriteImpl(sp::DataBuffer& buffer) const = 0;
|
std::cout << "Got message 1 !\n";
|
||||||
void ReadImpl(const sp::DataBuffer& buffer) = 0;
|
|
||||||
int GetId() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define TestPacket(packetName) \
|
|
||||||
class packetName : public Packet<packetName> { \
|
|
||||||
public: \
|
|
||||||
std::string_view GetName() const { \
|
|
||||||
return #packetName; \
|
|
||||||
} \
|
|
||||||
void WriteImpl(sp::DataBuffer& buffer) const {} \
|
|
||||||
void ReadImpl(const sp::DataBuffer& buffer) {} \
|
|
||||||
int GetId() const { \
|
|
||||||
return 0; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TestCommand(commandName) \
|
|
||||||
class commandName : public Command<commandName> { \
|
|
||||||
public: \
|
|
||||||
std::string_view GetName() const { \
|
|
||||||
return #commandName; \
|
|
||||||
} \
|
|
||||||
void WriteImpl(sp::DataBuffer& buffer) const {} \
|
|
||||||
void ReadImpl(const sp::DataBuffer& buffer) {} \
|
|
||||||
int GetId() const { \
|
|
||||||
return 0; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
TestPacket(ChatPacket);
|
|
||||||
TestPacket(PlayerJoinPacket);
|
|
||||||
TestPacket(PlayerLeavePacket);
|
|
||||||
|
|
||||||
TestCommand(SpawnTroopCommand);
|
|
||||||
TestCommand(PlaceTowerCommand);
|
|
||||||
|
|
||||||
class ActualMessage2 : public sp::MessageBase<ActualMessage2> {
|
|
||||||
std::string_view GetName() const {
|
|
||||||
return "mesmes";
|
|
||||||
}
|
|
||||||
void WriteImpl(sp::DataBuffer& buffer) const {}
|
|
||||||
void ReadImpl(const sp::DataBuffer& buffer) {}
|
|
||||||
int GetId() const {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using AllPackets = std::tuple<ChatPacket, PlayerJoinPacket, PlayerLeavePacket>;
|
|
||||||
using AllCommands = std::tuple<SpawnTroopCommand, PlaceTowerCommand>;
|
|
||||||
using AllTests = std::tuple<ActualMessage2>;
|
|
||||||
|
|
||||||
using AllMessages = sp::tuple_cat_t<AllPackets, AllCommands, AllTests>;
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
class Handler : public sp::GenericHandler<sp::Message, AllMessages> {};
|
|
||||||
} // namespace sp
|
|
||||||
|
|
||||||
using PacketHandler = sp::Handler;
|
|
||||||
using CommandHandler = sp::Handler;
|
|
||||||
|
|
||||||
class PacketPrinter : public PacketHandler {
|
|
||||||
public:
|
|
||||||
void Handle(ChatPacket& packet) override {
|
|
||||||
std::cout << packet.GetName() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// void Handle(SpawnTroopCommand& cmd) {
|
|
||||||
// std::cout << "NOOOOOO\n";
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ActualHandler1 : public sp::Handler {
|
|
||||||
public:
|
|
||||||
virtual void Handle(ActualMessage2& msg) override {
|
|
||||||
std::cout << "Handling ActualMessage2" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Handle(sp::Message& msg) override {
|
|
||||||
std::cout << "Common handling function is invoked" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Handle(ChatPacket& msg) override {
|
|
||||||
std::cout << "Chat invoked" << std::endl;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::unique_ptr<sp::Message> msg = std::make_unique<ActualMessage2>();
|
MyMessage::MsgIdType test;
|
||||||
std::unique_ptr<sp::Message> chat = std::make_unique<ChatPacket>();
|
auto yes = std::make_unique<ActualMessage1<MyMessage>>();
|
||||||
SpawnTroopCommand cmd;
|
MyHandler handlerTest;
|
||||||
PacketPrinter packetHandler;
|
yes->Dispatch(handlerTest);
|
||||||
PacketHandler pp;
|
|
||||||
chat->Dispatch(packetHandler);
|
|
||||||
ActualHandler1 handler;
|
|
||||||
msg->Dispatch(handler);
|
|
||||||
chat->Dispatch(handler);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user