Botcraft 1.21.4
Loading...
Searching...
No Matches
AutoSerializedMacros.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @brief Define a condition that can be used later inside an Internal::Conditioned type
4#define DEFINE_CONDITION(Name, ...) private: bool Name() const { return __VA_ARGS__; } static_assert(true, "Forcing ;")
5
6#ifdef PROTOCOLCRAFT_DETAILED_PARSING
7#define _OFFSETS_DECLARATION(Name) \
8 using Name##_offset_type = \
9 Internal::OffsetType<typename Name##_type::storage_type>::type; \
10 Name##_offset_type Name##_start{}; \
11 Name##_offset_type Name##_end{}
12#define _OFFSETS_FIELDS_DATA_DECLARATION(Name) \
13 using field_offset_type = Name##_offset_type; \
14 template <typename Parent> static constexpr field_offset_type \
15 Parent::* field_start_offset_ptr = &Parent::Name##_start; \
16 template <typename Parent> static constexpr field_offset_type \
17 Parent::* field_end_offset_ptr = &Parent::Name##_end
18#else
19#define _OFFSETS_DECLARATION(Name)
20#define _OFFSETS_FIELDS_DATA_DECLARATION(Name)
21#endif
22
23/// @brief Define a field that will be automatically integrated in Read/Write and Json serialization
24/// We start by retrieving the SerializedType of the given type and create the actual class member.
25/// Then we find the index of this field by counting how many FieldsData<size_t, T> template class
26/// have already been defined. Last part defines a templated FieldsData struct with all the
27/// name/type/member pointer info for this field.
28/// For the name, we want to define it in PascalCase but we also need a camel_case litteral
29/// version for json serialization, hence the constexpr string processing
30/// For field_ptr we don't know the name of the current class so to avoid passing it to the macro
31/// we use a template class instead. See GetField in DEFINE_UTILITIES for example of how to use it.
32/// @param name field name
33/// @param ... field type (passed as VA_ARGS because it can contain some ,) /!\ if Name == Type,
34/// use the fully qualified type instead (like ProtocolCraft::Type instead of Type)
35#define SERIALIZED_FIELD_WITHOUT_GETTER_SETTER(Name, ...) \
36 private: \
37 using Name##_type = typename Internal::SerializedType<__VA_ARGS__>; \
38 Name##_type::storage_type Name{}; \
39 _OFFSETS_DECLARATION(Name); \
40 template <size_t, typename> struct FieldsData; \
41 static constexpr size_t Name##_index = \
42 Internal::field_index<0, struct FieldDummy##Name, FieldsData>; \
43 template <typename T> \
44 struct FieldsData<Name##_index, T> { \
45 static constexpr std::array name_array = \
46 Internal::ToSnakeCase<Internal::GetSnakeCaseSize(#Name)>(#Name); \
47 static constexpr std::string_view field_name = \
48 Internal::ToStringView<std::size(name_array)>(name_array); \
49 using field_type = __VA_ARGS__; \
50 using field_storage_type = Name##_type::storage_type; \
51 using field_serialization_type = Name##_type::serialization_type; \
52 template <typename Parent> static constexpr field_storage_type \
53 Parent::*field_ptr = &Parent::Name; \
54 _OFFSETS_FIELDS_DATA_DECLARATION(Name); \
55 }; \
56 static_assert(true, "Forcing ;")
57
58
59// Basically just a Get and a Set function, by value if it's a simple type, and by const ref if not
60// We don't set an intermediate using with Name##_type::storage_type otherwise IDE autocomplete would
61// display the name of the using instead of the real underlying type
62#define GETTER(Name) \
63 public: \
64 std::conditional_t<std::is_arithmetic_v<Name##_type::storage_type> || std::is_enum_v<Name##_type::storage_type>, \
65 Name##_type::storage_type, const Name##_type::storage_type&> Get##Name() const { return Name; } \
66 static_assert(true, "Forcing ;")
67
68#define SETTER(Name) \
69 public: \
70 auto& Set##Name( \
71 std::conditional_t<std::is_arithmetic_v<Name##_type::storage_type> || std::is_enum_v<Name##_type::storage_type>, \
72 Name##_type::storage_type, const Name##_type::storage_type&> Name##_) { Name = Name##_; return *this; } \
73 static_assert(true, "Forcing ;")
74
75#define GETTER_SETTER(Name) GETTER(Name); SETTER(Name)
76
77#define SERIALIZED_FIELD(Name, ...) \
78 SERIALIZED_FIELD_WITHOUT_GETTER_SETTER(Name, __VA_ARGS__); \
79 GETTER_SETTER(Name)
80
81/// @brief Declare ReadImpl virtual function for auto serializable types
82#define DECLARE_READ protected: virtual void ReadImpl(ReadIterator& iter, size_t& length) override
83/// @brief Declare WriteImpl virtual function for auto serializable types
84#define DECLARE_WRITE protected: virtual void WriteImpl(WriteContainer& container) const override
85/// @brief Declare SerializeImpl virtual function for auto serializable types
86#define DECLARE_SERIALIZE protected: virtual Json::Value SerializeImpl() const override
87
88
89#ifdef PROTOCOLCRAFT_DETAILED_PARSING
90#define _DEFINE_OFFSETS_UTILITIES \
91 template <size_t i> auto& GetFieldStartOffset() { \
92 return this->*FieldsData<i, void>::template field_start_offset_ptr<std::remove_reference_t<decltype(*this)>>; } \
93 template <size_t i> auto& GetFieldEndOffset() { \
94 return this->*FieldsData<i, void>::template field_end_offset_ptr<std::remove_reference_t<decltype(*this)>>; } \
95 template <size_t i> const auto& GetFieldStartOffset() const { \
96 return this->*FieldsData<i, void>::template field_start_offset_ptr<std::remove_reference_t<decltype(*this)>>; } \
97 template <size_t i> const auto& GetFieldEndOffset() const { \
98 return this->*FieldsData<i, void>::template field_end_offset_ptr<std::remove_reference_t<decltype(*this)>>; }
99#else
100#define _DEFINE_OFFSETS_UTILITIES
101#endif
102
103/// @brief Define some constexpr utilities used to deal with the auto serializable fields
104/// We use void as the unique identifier type template parameter for FieldsData as we are
105/// sure it'll be different from what was used for the fields
106#define DEFINE_UTILITIES \
107 private: \
108 template <size_t, typename> struct FieldsData; \
109 static constexpr size_t num_fields = Internal::field_index<0, void, FieldsData>; \
110 template <size_t i> static constexpr std::string_view field_name = FieldsData<i, void>::field_name; \
111 template <size_t i> using field_type = typename FieldsData<i, void>::field_type; \
112 template <size_t i> using field_storage_type = typename FieldsData<i, void>::field_storage_type; \
113 template <size_t i> using field_serialization_type = \
114 typename FieldsData<i, void>::field_serialization_type; \
115 template <size_t i> auto& GetField() { \
116 return this->*FieldsData<i, void>::template field_ptr<std::remove_reference_t<decltype(*this)>>; } \
117 template <size_t i> const auto& GetField() const { \
118 return this->*FieldsData<i, void>::template field_ptr<std::remove_reference_t<decltype(*this)>>; } \
119 _DEFINE_OFFSETS_UTILITIES; \
120 static_assert(true, "Forcing ;")
121
122/// @brief Define auto serializable utilities and declare ReadImpl, WriteImpl and SerializeImpl virtual functions
123#define DECLARE_READ_WRITE_SERIALIZE DEFINE_UTILITIES; DECLARE_READ; DECLARE_WRITE; DECLARE_SERIALIZE
124
125#ifdef PROTOCOLCRAFT_DETAILED_PARSING
126#define _OFFSETS_POINTERS , &this->GetFieldStartOffset<i>(), &this->GetFieldEndOffset<i>()
127#else
128#define _OFFSETS_POINTERS
129#endif
130/// @brief Define ReadImpl virtual function that loops through all auto serializable fields
131#define DEFINE_READ(ClassName) \
132 void ClassName::ReadImpl(ReadIterator& iter, size_t& length) { \
133 Internal::loop<num_fields>([&](auto i) { \
134 try { \
135 auto& field = this->GetField<i>(); \
136 if constexpr (Internal::IsCustomType<field_type<i>>) { \
137 field = field_type<i>::Read( \
138 this, iter, length _OFFSETS_POINTERS); \
139 } \
140 else if constexpr (Internal::IsConditioned<field_type<i>>) { \
141 if (field_type<i>::Evaluate(this)) { \
142 if constexpr (field_type<i>::stored_as_optional) { \
143 field = ReadData< \
144 typename field_storage_type<i>::value_type, \
145 field_serialization_type<i>>( \
146 iter, length _OFFSETS_POINTERS); \
147 } \
148 else { \
149 field = ReadData< \
150 field_storage_type<i>, \
151 field_serialization_type<i>>( \
152 iter, length _OFFSETS_POINTERS); \
153 } \
154 } \
155 else if constexpr (field_type<i>::stored_as_optional) { \
156 field = std::nullopt; \
157 } \
158 } \
159 else { \
160 field = ReadData< \
161 field_storage_type<i>, \
162 field_serialization_type<i>>( \
163 iter, length _OFFSETS_POINTERS); \
164 } \
165 } \
166 catch (const std::exception& ex) { \
167 throw std::runtime_error(std::string("While reading ") + \
168 std::string(field_name<i>) + " (" + std::to_string(i) + \
169 "th field) in " + #ClassName + "\n" + ex.what()); \
170 } \
171 }); \
172 } static_assert(true, "Forcing ;")
173
174/// @brief Define WriteImpl virtual function that loops through all auto serializable fields
175#define DEFINE_WRITE(ClassName) \
176 void ClassName::WriteImpl(WriteContainer& container) const { \
177 Internal::loop<num_fields>([&](auto i) { \
178 const auto& field = this->GetField<i>(); \
179 if constexpr (Internal::IsCustomType<field_type<i>>) { \
180 field_type<i>::Write(this, field, container); \
181 } \
182 else if constexpr (Internal::IsConditioned<field_type<i>>) { \
183 if (field_type<i>::Evaluate(this)) { \
184 if constexpr (field_type<i>::stored_as_optional) { \
185 WriteData< \
186 typename field_storage_type<i>::value_type, \
187 field_serialization_type<i>>(field.value(), container); \
188 } \
189 else { \
190 WriteData< \
191 field_storage_type<i>, \
192 field_serialization_type<i>>(field, container); \
193 } \
194 } \
195 } \
196 else { \
197 WriteData< \
198 field_storage_type<i>, \
199 field_serialization_type<i>>(field, container); \
200 } \
201 }); \
202 } static_assert(true, "Forcing ;")
203
204// Define SerializeImpl virtual function for auto serializable types
205#define DEFINE_SERIALIZE(ClassName) \
206 Json::Value ClassName::SerializeImpl() const { \
207 Json::Value output; \
208 Internal::loop<num_fields>([&](auto i) { \
209 const auto& field = this->GetField<i>(); \
210 std::optional<Json::Value> serialized = std::nullopt; \
211 if constexpr (Internal::IsCustomType<field_type<i>>) { \
212 serialized = field_type<i>::Serialize( \
213 this, field _OFFSETS_POINTERS); \
214 } \
215 else if constexpr (Internal::IsConditioned<field_type<i>>) { \
216 if (field_type<i>::Evaluate(this)) { \
217 serialized = SerializeType<field_storage_type<i>>( \
218 field _OFFSETS_POINTERS); \
219 } \
220 } \
221 else { \
222 serialized = SerializeType<field_storage_type<i>>( \
223 field _OFFSETS_POINTERS); \
224 } \
225 if (serialized.has_value()) { \
226 output[std::string(field_name<i>)] = serialized.value(); \
227 } \
228 }); \
229 return output; \
230 } static_assert(true, "Forcing ;")
231
232// Define a NetworkType with auto serializable fields
233#define DEFINE_NETWORK_TYPE(ClassName) \
234 DEFINE_READ(ClassName); \
235 DEFINE_WRITE(ClassName); \
236 DEFINE_SERIALIZE(ClassName)
237
238// Define a Message with auto serializable fields
239#define DEFINE_MESSAGE_CLASS(ClassName) \
240 DEFINE_READ(ClassName); \
241 DEFINE_WRITE(ClassName); \
242 DEFINE_SERIALIZE(ClassName); \
243 template class BaseMessage<ClassName>