better example
This commit is contained in:
65
include/sp/protocol/Field.h
Normal file
65
include/sp/protocol/Field.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/common/DataBuffer.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
template <typename ValueType>
|
||||
class Field {
|
||||
public:
|
||||
// Provide an access to the stored value
|
||||
ValueType& GetValue() {
|
||||
return m_Value;
|
||||
}
|
||||
const ValueType& GetValue() const {
|
||||
return m_Value;
|
||||
}
|
||||
|
||||
// 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
|
||||
141
include/sp/protocol/GenericHandler.h
Normal file
141
include/sp/protocol/GenericHandler.h
Normal file
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace sp
|
||||
{
|
||||
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
|
||||
|
||||
// TCommon is common interface class for all the messages
|
||||
// TAll is all the message types, that need to be handled, bundled in std::tuple
|
||||
template <typename TCommon, typename TAll>
|
||||
class GenericHandler;
|
||||
|
||||
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
|
||||
template <typename TCommon,
|
||||
typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16, typename T17, typename T18, typename T19, typename T20,
|
||||
typename... TRest>
|
||||
class 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...> >
|
||||
{
|
||||
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
|
||||
public:
|
||||
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T5& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T6& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T7& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T8& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T9& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T10& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T11& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T12& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T13& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T14& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T15& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T16& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T17& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T18& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T19& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T20& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
};
|
||||
|
||||
// 10 by 10
|
||||
template <typename TCommon,
|
||||
typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename... TRest>
|
||||
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
|
||||
{
|
||||
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
|
||||
public:
|
||||
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T5& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T6& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T7& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T8& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T9& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T10& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
};
|
||||
|
||||
// 5 by 5
|
||||
template <typename TCommon,
|
||||
typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename... TRest>
|
||||
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
|
||||
{
|
||||
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
|
||||
public:
|
||||
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T5& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
};
|
||||
|
||||
// Deal with rest with 4 types
|
||||
template <typename TCommon, typename T1, typename T2, typename T3, typename T4>
|
||||
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4> >
|
||||
{
|
||||
public:
|
||||
virtual ~GenericHandler() {}
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(TCommon&) { } //Nothing to do
|
||||
};
|
||||
|
||||
// Deal with rest with 3 types
|
||||
template <typename TCommon, typename T1, typename T2, typename T3>
|
||||
class GenericHandler<TCommon, std::tuple<T1, T2, T3> >
|
||||
{
|
||||
public:
|
||||
virtual ~GenericHandler() {}
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(TCommon&) { } //Nothing to do
|
||||
};
|
||||
|
||||
// Deal with rest with 2 types
|
||||
template <typename TCommon, typename T1, typename T2>
|
||||
class GenericHandler<TCommon, std::tuple<T1, T2> >
|
||||
{
|
||||
public:
|
||||
virtual ~GenericHandler() {}
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(TCommon&) { } //Nothing to do
|
||||
};
|
||||
|
||||
// Deal with rest with 1 type
|
||||
template <typename TCommon, typename T1>
|
||||
class GenericHandler<TCommon, std::tuple<T1> >
|
||||
{
|
||||
public:
|
||||
virtual ~GenericHandler() {}
|
||||
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
|
||||
virtual void Handle(TCommon&) { } //Nothing to do
|
||||
};
|
||||
|
||||
// Deal with rest with 0 type
|
||||
template <typename TCommon>
|
||||
class GenericHandler<TCommon, std::tuple<> >
|
||||
{
|
||||
public:
|
||||
virtual ~GenericHandler() {}
|
||||
virtual void Handle(TCommon&) { } //Nothing to do
|
||||
};
|
||||
|
||||
} // sp
|
||||
13
include/sp/protocol/Message.h
Normal file
13
include/sp/protocol/Message.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/protocol/MessageInterfaceBuilder.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
template <typename... TOptions>
|
||||
class Message : public option::MessageInterfaceBuilder<TOptions...>::Type {
|
||||
public:
|
||||
using ParsedOptions = typename option::MessageInterfaceBuilder<TOptions...>::ParsedOptions;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
278
include/sp/protocol/MessageBase.h
Normal file
278
include/sp/protocol/MessageBase.h
Normal file
@@ -0,0 +1,278 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/protocol/Message.h>
|
||||
#include <sp/protocol/Templates.h>
|
||||
|
||||
namespace sp {
|
||||
namespace option {
|
||||
|
||||
// Provide static numeric ID, to facilitate implementation of GetIdImpl()
|
||||
template <std::intmax_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 {};
|
||||
|
||||
} // 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;
|
||||
}
|
||||
|
||||
template<std::size_t FIndex>
|
||||
auto& GetField() {
|
||||
return std::get<FIndex>(GetFields()).GetValue();
|
||||
}
|
||||
|
||||
private:
|
||||
TFields m_Fields;
|
||||
};
|
||||
|
||||
template <typename TBase>
|
||||
class MessageImplFieldsReadBase : public TBase {
|
||||
protected:
|
||||
void ReadImpl(DataBuffer& buffer) override {
|
||||
//TODO: add endianess
|
||||
auto& allFields = TBase::GetFields();
|
||||
tupleForEach(allFields, FieldReader{buffer});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TBase>
|
||||
class MessageImplFieldsWriteBase : public TBase {
|
||||
protected:
|
||||
void WriteImpl(DataBuffer& buffer) override {
|
||||
//TODO: add endianess
|
||||
auto& allFields = TBase::GetFields();
|
||||
tupleForEach(allFields, FieldWriter{buffer});
|
||||
}
|
||||
};
|
||||
|
||||
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::HasValid && 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
|
||||
38
include/sp/protocol/MessageInterfaceBuilder.h
Normal file
38
include/sp/protocol/MessageInterfaceBuilder.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/protocol/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/protocol/MessageInterfaces.h
Normal file
243
include/sp/protocol/MessageInterfaces.h
Normal file
@@ -0,0 +1,243 @@
|
||||
#pragma once
|
||||
|
||||
#include <sp/protocol/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/protocol/Options.h
Normal file
31
include/sp/protocol/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
|
||||
86
include/sp/protocol/Templates.h
Normal file
86
include/sp/protocol/Templates.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace sp {
|
||||
|
||||
/// @brief Default case, see field_index specialization for implementation details
|
||||
template <size_t N, typename T, template <size_t, typename> typename U, typename = void>
|
||||
static constexpr size_t field_index = 0;
|
||||
|
||||
/// @brief A templated size_t that counts the number of existing classes U with a "field_name" member
|
||||
/// @tparam N Current counter for recursion (user should always call it with 0)
|
||||
/// @tparam T Can be any type, must be different for each field we want to be counted later (used because we can't have a empty
|
||||
/// template<> specialization nested in a class)
|
||||
/// @tparam U The templated class that will be searched for match
|
||||
template <size_t N, typename T, template <size_t, typename> typename U>
|
||||
static constexpr size_t field_index<N, T, U, std::void_t<decltype(U<N, T>::field_name)>> = 1 + field_index<N + 1, T, U>;
|
||||
|
||||
/// @brief Concat multiple tuples in one big tuple
|
||||
/// @tparam ...input_t Multiple std::tuple types to concat
|
||||
template <typename... input_t>
|
||||
using tuple_cat_t = decltype(std::tuple_cat(std::declval<input_t>()...));
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
constexpr bool tuple_contains_type = false;
|
||||
template <typename T, typename... Ts>
|
||||
constexpr bool tuple_contains_type<T, std::tuple<Ts...>> = std::disjunction_v<std::is_same<T, Ts>...>;
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
constexpr int get_tuple_index = 0;
|
||||
template <typename T, typename... Rest>
|
||||
constexpr int get_tuple_index<T, std::tuple<T, Rest...>> = 0;
|
||||
template <typename T, typename First, typename... Rest>
|
||||
constexpr int get_tuple_index<T, std::tuple<First, Rest...>> = 1 + get_tuple_index<T, std::tuple<Rest...>>;
|
||||
|
||||
// Template black magic to loop at compile time
|
||||
template <std::size_t... indices, class LoopBody>
|
||||
void loop_impl(std::index_sequence<indices...>, LoopBody&& loop_body) {
|
||||
(loop_body(std::integral_constant<std::size_t, indices>{}), ...);
|
||||
}
|
||||
|
||||
template <std::size_t N, class LoopBody>
|
||||
void loop(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
|
||||
Reference in New Issue
Block a user