diff --git a/include/examples/UpgradeTowerPacket.h b/include/examples/UpgradeTowerPacket.h index 1ae4821..fc566b2 100644 --- a/include/examples/UpgradeTowerPacket.h +++ b/include/examples/UpgradeTowerPacket.h @@ -15,7 +15,8 @@ using UpgradeTowerFields = std::tuple< sp::Field, //<- m_Tower sp::Field //<- m_Upgrade >, - sp::VarInt //<- just for testing + sp::VarInt, //<- just for testing + std::map> >; DeclarePacket(UpgradeTower){ diff --git a/include/sp/common/Templates.h b/include/sp/common/Templates.h index fefd2f2..90cdd8f 100644 --- a/include/sp/common/Templates.h +++ b/include/sp/common/Templates.h @@ -2,6 +2,7 @@ #include #include +#include namespace sp { diff --git a/include/sp/common/VarInt.h b/include/sp/common/VarInt.h index df194f1..af96df8 100644 --- a/include/sp/common/VarInt.h +++ b/include/sp/common/VarInt.h @@ -7,7 +7,8 @@ #include #include -#include +#include +#include namespace sp { @@ -56,11 +57,8 @@ class VarInt { * \param var The variable integer to deserialize */ friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); + + friend std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_VarInt); }; -template<> -inline std::string PrintData(const VarInt& a_VarInt) { - return PrintData(a_VarInt.GetValue()); -} - } // namespace sp diff --git a/include/sp/protocol/Field.h b/include/sp/protocol/Field.h index f593c5f..5fb377f 100644 --- a/include/sp/protocol/Field.h +++ b/include/sp/protocol/Field.h @@ -1,6 +1,5 @@ #pragma once -#include #include namespace sp { @@ -98,6 +97,19 @@ class Field { StorageType m_Value; }; +template +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 diff --git a/include/sp/protocol/MessagePrinter.h b/include/sp/protocol/MessagePrinter.h index 5faa02d..7c3b8c7 100644 --- a/include/sp/protocol/MessagePrinter.h +++ b/include/sp/protocol/MessagePrinter.h @@ -1,55 +1,61 @@ #pragma once #include -#include +#include +#include +#include #include #include namespace sp { -template ::value, bool> = true> -inline std::string PrintData(const T& a_Data); - template ::value, bool> = true> -inline std::string PrintData(T a_Data) { - return std::to_string(a_Data); +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_Field) { + return a_Stream << std::to_string(a_Field.GetValue()); } -template <> -inline std::string PrintData(const std::string& a_Data) { - return "\"" + a_Data + "\""; +inline std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_Field) { + return a_Stream << a_Field.GetValue(); } -template -std::string PrintData(const std::pair& a_Data); +inline std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_Field) { + return a_Stream << "\"" << a_Field.GetValue() << "\""; +} + + + + template -std::string PrintData(const std::map& a_Data); +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data); + +template +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data); template -std::string PrintData(const std::vector& a_Data); +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data); + + + + template -std::string PrintData(const std::pair& a_Data) { - return "{" + PrintData(a_Data.first) + " => " + PrintData(a_Data.second) + "}"; +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data) { + return a_Stream << PrintableField(a_Data.GetValue().first) << " => " << PrintableField(a_Data.GetValue().second); } template -std::string PrintData(const std::map& a_Data) { - std::string result = "{"; - for (const auto& pair : a_Data) { - result += PrintData(pair) + ", "; - } - return result.substr(0, result.size() - 2) + "}"; +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data) { + a_Stream << "{"; + std::copy(a_Data.GetValue().begin(), a_Data.GetValue().end(), OstreamFieldIterator>(std::cout, ", ")); + return a_Stream << "}"; } template -std::string PrintData(const std::vector& a_Data) { - std::string result = "{"; - for (const T& value : a_Data) { - result += PrintData(value) + ", "; - } - return result.substr(0, result.size() - 2) + "}"; +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField>& a_Data) { + a_Stream << "{"; + std::copy(a_Data.GetValue().begin(), a_Data.GetValue().end(), OstreamFieldIterator(std::cout, ", ")); + return a_Stream << "}"; } -} // namespace sp \ No newline at end of file +} // namespace sp diff --git a/include/sp/protocol/message/MessageInterfacesImpl.h b/include/sp/protocol/message/MessageInterfacesImpl.h index 72b813d..d9df12e 100644 --- a/include/sp/protocol/message/MessageInterfacesImpl.h +++ b/include/sp/protocol/message/MessageInterfacesImpl.h @@ -133,12 +133,12 @@ class MessageInterfaceWriteIdBase : public TBase { template class MessageInterfaceToStringBase : public TBase { public: - std::string ToString() const { - return ToStringImpl(); + friend std::ostream& operator<<(std::ostream& a_Stream, const MessageInterfaceToStringBase& a_Message) { + return a_Message.OpOutImpl(a_Stream); } protected: - virtual std::string ToStringImpl() const = 0; + virtual std::ostream& OpOutImpl(std::ostream& a_Stream) const = 0; }; } // namespace details diff --git a/include/sp/protocol/message/MessagePrinterImpl.h b/include/sp/protocol/message/MessagePrinterImpl.h index 10c82a4..1b4cfc4 100644 --- a/include/sp/protocol/message/MessagePrinterImpl.h +++ b/include/sp/protocol/message/MessagePrinterImpl.h @@ -31,41 +31,49 @@ struct IdPrinter, TOptions...> { } }; +} // namespace details + template -std::string PrintFields(const std::tuple& a_Fields); +std::ostream& operator<<(std::ostream& a_Stream, const std::tuple& a_Fields); template struct FieldPrinter { - static std::string PrintField(const sp::Field& a_Field) { - return Reflector::GetClassName() + "=" + PrintData(a_Field.GetValue()); + static std::ostream& PrintField(std::ostream& a_Stream, const sp::Field& a_Field) { + return a_Stream << sp::Reflector::GetClassName() << "=" << PrintableField(a_Field.GetValue()); } }; template -struct FieldPrinter, IAlignment> { - static std::string PrintField(const Field, IAlignment>& a_Field) { - return "BitField<" + Reflector::GetClassName() + ">[" + PrintFields(a_Field.GetValue().GetFields()) + "]"; +struct FieldPrinter, IAlignment> { + static std::ostream& PrintField( + std::ostream& a_Stream, const sp::Field, IAlignment>& a_Field) { + a_Stream << "BitField<" << sp::Reflector::GetClassName() << ">["; + a_Stream << a_Field.GetValue().GetFields() << "]"; + return a_Stream; } }; template -std::string PrintFields(const std::tuple& a_Fields) { - std::string concat; +std::ostream& operator<<(std::ostream& a_Stream, const std::tuple& a_Fields) { + bool first = true; TupleForEach( - [&concat](const auto& a_Field) { + [&a_Stream, &first](const auto& a_Field) { + if (!first) + a_Stream << ", "; using TField = typename std::decay::type; constexpr std::size_t alignment = TField::AlignmentValue; - concat += FieldPrinter::PrintField(a_Field) + ", "; + FieldPrinter::PrintField(a_Stream, a_Field); + first = false; }, a_Fields); - return concat.substr(0, concat.size() - 2); + return a_Stream; } template -std::string PrintMessage(const MessageBase& a_Message) { - return sp::GetBasicClassName(a_Message) + sp::details::IdPrinter::PrintMessageId() + "[" + - sp::details::PrintFields(a_Message.GetFields()) + "]"; +std::ostream& operator<<(std::ostream& a_Stream, const sp::MessageBase& a_Message) { + a_Stream << sp::GetBasicClassName(a_Message) << sp::details::IdPrinter::PrintMessageId() << "[" + << a_Message.GetFields() << "]"; + return a_Stream; } -} // namespace details -} // namespace sp +} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessagesImpl.h b/include/sp/protocol/message/MessagesImpl.h index 5c4c947..3afb4b3 100644 --- a/include/sp/protocol/message/MessagesImpl.h +++ b/include/sp/protocol/message/MessagesImpl.h @@ -14,8 +14,8 @@ std::string PrintMessage(const MessageBase& a_Message); template class MessageImplToStringBase : public TBase { protected: - virtual std::string ToStringImpl() const override { - return PrintMessage(static_cast(*this)); + virtual std::ostream& OpOutImpl(std::ostream& a_Stream) const override{ + return a_Stream << static_cast(*this); } }; diff --git a/include/sp/protocol/message/OstreamFieldIterator.h b/include/sp/protocol/message/OstreamFieldIterator.h new file mode 100644 index 0000000..6d26c42 --- /dev/null +++ b/include/sp/protocol/message/OstreamFieldIterator.h @@ -0,0 +1,56 @@ +// infix_iterator.h +// +// Lifted from Jerry Coffin's 's prefix_ostream_iterator +#pragma once + +#include +#include + +namespace sp { + +template > +class OstreamFieldIterator { + private: + std::basic_ostream* 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; + + 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(item); + m_FirstElem = false; + return *this; + } + + auto& operator*() { + return *this; + } + + auto& operator++() { + return *this; + } + + auto& operator++(int) { + return *this; + } +}; + +} // namespace sp diff --git a/src/sp/common/VarInt.cpp b/src/sp/common/VarInt.cpp index a02bf6f..a2e0913 100644 --- a/src/sp/common/VarInt.cpp +++ b/src/sp/common/VarInt.cpp @@ -49,4 +49,8 @@ DataBuffer& operator>>(DataBuffer& in, VarInt& var) { return in; } +std::ostream& operator<<(std::ostream& a_Stream, const PrintableField& a_VarInt) { + return a_Stream << a_VarInt.GetValue().GetValue(); +} + } // namespace sp diff --git a/test/test_packets.cpp b/test/test_packets.cpp index 59302b6..47f0fd4 100644 --- a/test/test_packets.cpp +++ b/test/test_packets.cpp @@ -9,7 +9,7 @@ class KeepAliveHandler : public sp::PacketHandler { void Handle(const KeepAlivePacket& packet) { std::cout << "KeepAlive handled !!\n"; - std::cout << packet.ToString() << std::endl; + std::cout << packet << std::endl; } void Handle(const DisconnectPacket& packet) { @@ -22,7 +22,8 @@ class KeepAliveHandler : public sp::PacketHandler { }; int main() { - auto upgradeTower = std::make_unique(std::make_tuple(666, 9), 789); + std::map> yes = {{"woa", {5, 8}}, {"insane", {6, 9}}}; + auto upgradeTower = std::make_unique(std::make_tuple(666, 9), 789, yes); auto keepAlive = std::make_unique(6969); sp::PacketMessage* msg = upgradeTower.get(); @@ -39,7 +40,7 @@ int main() { auto upgradeTower2 = std::make_unique(); upgradeTower2->Read(buffer); - std::cout << "Test : " << msg->ToString() << "\n"; + std::cout << "Test : " << *msg << "\n"; sp::PacketFactory factory; auto packet = factory.CreateMessage(msgId);