Unravel Engine C++ Reference
Loading...
Searching...
No Matches
serialization.h
Go to the documentation of this file.
1#pragma once
2#include "ser20/access.hpp"
3#include "ser20/ser20.hpp"
4#include "ser20/types/polymorphic.hpp"
5#include "ser20/types/vector.hpp"
6#include <hpp/source_location.hpp>
7#include <hpp/concepts.hpp>
8
9#include <functional>
10#include <string>
11#include <vector>
12#include <stack>
13#include <unordered_map>
14
15#define SERIALIZE_FUNCTION_NAME SER20_SERIALIZE_FUNCTION_NAME
16#define SAVE_FUNCTION_NAME SER20_SAVE_FUNCTION_NAME
17#define LOAD_FUNCTION_NAME SER20_LOAD_FUNCTION_NAME
18#define SAVE_MINIMAL_FUNCTION_NAME SER20_SAVE_MINIMAL_FUNCTION_NAME
19#define LOAD_MINIMAL_FUNCTION_NAME SER20_LOAD_MINIMAL_FUNCTION_NAME
20#define SERIALIZE_REGISTER_TYPE_WITH_NAME(T, Name) SER20_REGISTER_TYPE_WITH_NAME(T, Name)
21namespace serialization
22{
23using namespace ser20;
24using log_callback_t = std::function<void(const std::string&, const hpp::source_location& loc)>;
25
30void log_warning(const std::string& log_msg, const hpp::source_location& loc = hpp::source_location::current());
31void init(const init_data& data = {});
32
33// Path tracking for deserialization
35{
36 std::function<bool(const std::string&)> should_serialize_property_callback;
37 std::vector<std::string> path_segments;
38 bool recording_enabled = false;
39 bool ignore_next_push = false;
40
41 auto push_segment(const std::string& segment) -> bool;
42 void pop_segment();
43 auto get_current_path() const -> std::string;
44 void enable_recording();
45 void disable_recording();
46 auto is_recording() const -> bool;
47 void clear();
48
49 auto should_serialize_property(const std::string& property_path) const -> bool
50 {
52 {
53 return should_serialize_property_callback(property_path);
54 }
55 return true;
56 }
57};
58
59auto get_path_context() -> path_context*;
60void set_path_context(path_context* ctx);
61
62// Convenience function to get current deserialization path
63auto get_current_deserialization_path() -> std::string;
64
65// RAII helper for path segments
67{
68 path_segment_guard() = default;
69 path_segment_guard(const std::string& segment);
71
72 void push_segment(const std::string& segment);
73 void pop_segment();
74 // Non-copyable and non-movable to avoid double-popping
79
80private:
81 bool was_pushed_ = false;
82};
83
85{
86 path_skip_segment_guard(bool ignore_next_push = false);
88
89 // Non-copyable and non-movable to avoid double-popping
94
95private:
96};
97
98
99} // namespace serialization
100
101#define SERIALIZABLE(T) \
102 \
103public: \
104 friend class serialization::access; \
105 template<typename Archive> \
106 friend void SAVE_FUNCTION_NAME(Archive& ar, T const&); \
107 template<typename Archive> \
108 friend void LOAD_FUNCTION_NAME(Archive& ar, T&);
109
110#define SERIALIZE_INLINE(cls) \
111 template<typename Archive> \
112 inline void SERIALIZE_FUNCTION_NAME(Archive& ar, cls& obj)
113
114#define SAVE_INLINE(cls) \
115 template<typename Archive> \
116 inline void SAVE_FUNCTION_NAME(Archive& ar, cls const& obj)
117
118#define LOAD_INLINE(cls) \
119 template<typename Archive> \
120 inline void LOAD_FUNCTION_NAME(Archive& ar, cls& obj)
121
122#define SERIALIZE_EXTERN(cls) \
123 template<typename Archive> \
124 extern void SERIALIZE_FUNCTION_NAME(Archive& ar, cls& obj)
125
126#define SAVE_EXTERN(cls) \
127 template<typename Archive> \
128 extern void SAVE_FUNCTION_NAME(Archive& ar, cls const& obj)
129
130#define LOAD_EXTERN(cls) \
131 template<typename Archive> \
132 extern void LOAD_FUNCTION_NAME(Archive& ar, cls& obj)
133
134#define SERIALIZE(cls) \
135 template<typename Archive> \
136 void SERIALIZE_FUNCTION_NAME(Archive& ar, cls& obj)
137
138#define SAVE(cls) \
139 template<typename Archive> \
140 void SAVE_FUNCTION_NAME(Archive& ar, cls const& obj)
141
142#define LOAD(cls) \
143 template<typename Archive> \
144 void LOAD_FUNCTION_NAME(Archive& ar, cls& obj)
145
146#define SERIALIZE_INSTANTIATE(cls, Archive) template void SERIALIZE_FUNCTION_NAME(Archive& archive, cls& obj)
147
148#define SAVE_INSTANTIATE(cls, Archive) template void SAVE_FUNCTION_NAME(Archive& archive, cls const& obj)
149
150#define LOAD_INSTANTIATE(cls, Archive) template void LOAD_FUNCTION_NAME(Archive& archive, cls& obj)
151
152template<typename Archive>
153constexpr inline auto is_binary_archive() -> bool
154{
155 return false;
156}
157
158// Check if Archive is loading (deserializing)
159template<typename Archive>
160constexpr inline auto is_loading_archive() -> bool
161{
162 return Archive::is_loading::value;
163}
164
165// Specialized handler for sequence containers with per-element override support
166template<typename Archive, typename T>
167inline auto try_serialize_sequence_container(Archive& ar,
168 ser20::NameValuePair<T>&& t,
169 const hpp::source_location& loc = hpp::source_location::current()) -> bool;
170
171template<typename Archive, typename T>
172inline auto try_serialize_direct(Archive& ar,
173 ser20::NameValuePair<T>&& t,
174 const hpp::source_location& loc = hpp::source_location::current()) -> bool
175{
176 try
177 {
178 ar(std::forward<ser20::NameValuePair<T>>(t));
179 }
180 catch(const std::exception& e)
181 {
182 if constexpr(is_binary_archive<Archive>())
183 {
184 serialization::log_warning(e.what(), loc);
185 }
186 return false;
187 }
188 return true;
189}
190
191// template<typename Archive, typename T>
192// inline auto try_serialize_sequence_container(Archive& ar,
193// ser20::NameValuePair<T>&& t,
194// const hpp::source_location& loc) -> bool
195// {
196// using decayed_type = std::decay_t<T>;
197// // static_assert(std::is_same_v<T, std::decay_t<T>>, "T should be decayed type");
198
199// auto path_ctx = serialization::get_path_context();
200
201// // If not loading or no path context, use normal serialization
202// if constexpr(!is_loading_archive<Archive>())
203// {
204// return try_serialize_direct(ar, std::forward<ser20::NameValuePair<T>>(t), loc);
205// }
206// else
207// {
208// if(!path_ctx || !path_ctx->is_recording())
209// {
210// return try_serialize_direct(ar, std::forward<ser20::NameValuePair<T>>(t), loc);
211// }
212
213
214
215// // Deserialize the entire container
216// bool success = try_serialize_direct(ar, std::forward<ser20::NameValuePair<T>>(t), loc);
217
218// // Restore overridden elements (only if they fit in the new container size)
219// // for(auto& [idx, value] : overridden_elements)
220// // {
221// // if(idx < t.value.size())
222// // {
223// // // Use iterator-based approach for containers that support it
224// // auto it = t.value.begin();
225// // std::advance(it, static_cast<typename decayed_type::difference_type>(idx));
226// // *it = std::move(value);
227// // }
228// // else
229// // {
230// // // Element index is out of bounds in the new container - log warning
231// // std::string msg = "Cannot restore overridden element [" + std::to_string(idx) +
232// // "] in '" + t.name + "': index out of bounds (container size: " +
233// // std::to_string(t.value.size()) + ")";
234// // serialization::log_warning(msg, loc);
235// // }
236// // }
237
238// return success;
239// }
240// }
241
242template<typename F>
243inline auto serialize_check(const std::string& name, F&& serialize_callback) -> bool
244{
245 auto path_ctx = serialization::get_path_context();
246 if(path_ctx)
247 {
249 auto path = path_ctx->get_current_path();
250 if(!path_ctx->should_serialize_property(path))
251 {
252 return false;
253 }
254 return serialize_callback();
255 }
256 return serialize_callback();
257}
258
259
260template<typename Archive, typename T>
261inline auto try_serialize(Archive& ar,
262 ser20::NameValuePair<T>&& t,
263 const hpp::source_location& loc = hpp::source_location::current()) -> bool
264{
265 bool result = serialize_check(t.name, [&]() -> bool
266 {
267 return try_serialize_direct(ar, std::forward<ser20::NameValuePair<T>>(t), loc);
268 });
269
270 return result;
271}
272
273
274template<typename Archive, typename T>
275inline auto try_save(Archive& ar,
276 ser20::NameValuePair<T>&& t,
277 const hpp::source_location& loc = hpp::source_location::current()) -> bool
278{
279 return try_serialize(ar, std::forward<ser20::NameValuePair<T>>(t), loc);
280}
281
282template<typename Archive, typename T>
283inline auto try_load(Archive& ar,
284 ser20::NameValuePair<T>&& t,
285 const hpp::source_location& loc = hpp::source_location::current()) -> bool
286{
287 return try_serialize(ar, std::forward<ser20::NameValuePair<T>>(t), loc);
288}
289
290
291template<typename Archive, typename F>
292inline auto try_serialize(Archive& ar,
293 const char* name,
294 F&& serialize_callback,
295 const hpp::source_location& loc = hpp::source_location::current()) -> bool
296{
297 if constexpr(!is_binary_archive<Archive>())
298 {
299 ar.setNextName(name);
300 }
301 return serialize_callback();
302}
303
304template<typename Archive, typename F>
305inline auto try_load(Archive& ar,
306 const char* name,
307 F&& load_callback,
308 const hpp::source_location& loc = hpp::source_location::current()) -> bool
309{
310 return try_serialize(ar, name, std::forward<F>(load_callback), loc);
311}
312
313template<typename Archive, typename F>
314inline auto try_save(Archive& ar,
315 const char* name,
316 F&& save_callback,
317 const hpp::source_location& loc = hpp::source_location::current()) -> bool
318{
319 return try_serialize(ar, name, std::forward<F>(save_callback), loc);
320}
321
std::string name
Definition hub.cpp:33
Definition yaml.hpp:46
void log_warning(const std::string &log_msg, const hpp::source_location &loc)
auto get_current_deserialization_path() -> std::string
std::function< void(const std::string &, const hpp::source_location &loc)> log_callback_t
void set_path_context(path_context *ctx)
void init(const init_data &data)
auto get_path_context() -> path_context *
Hash specialization for batch_key to enable use in std::unordered_map.
auto try_serialize(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
auto try_save(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
auto try_serialize_sequence_container(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
auto try_serialize_direct(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
auto serialize_check(const std::string &name, F &&serialize_callback) -> bool
constexpr auto is_binary_archive() -> bool
constexpr auto is_loading_archive() -> bool
auto try_load(Archive &ar, ser20::NameValuePair< T > &&t, const hpp::source_location &loc=hpp::source_location::current()) -> bool
log_callback_t warning_logger
auto get_current_path() const -> std::string
auto push_segment(const std::string &segment) -> bool
auto should_serialize_property(const std::string &property_path) const -> bool
std::function< bool(const std::string &)> should_serialize_property_callback
auto is_recording() const -> bool
std::vector< std::string > path_segments
path_segment_guard & operator=(const path_segment_guard &)=delete
path_segment_guard(const path_segment_guard &)=delete
path_segment_guard(path_segment_guard &&)=delete
path_segment_guard & operator=(path_segment_guard &&)=delete
void push_segment(const std::string &segment)
path_skip_segment_guard(const path_skip_segment_guard &)=delete
path_skip_segment_guard & operator=(path_skip_segment_guard &&)=delete
path_skip_segment_guard & operator=(const path_skip_segment_guard &)=delete
path_skip_segment_guard(bool ignore_next_push=false)
path_skip_segment_guard(path_skip_segment_guard &&)=delete