VLink  2.0.0
A high-performance communication middleware
serializer.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file serializer.h
26  * @brief Compile-time codec dispatch for VLink message payloads.
27  *
28  * @details
29  * The @c Serializer namespace is the codec router used by every VLink
30  * primitive. Given a C++ type @c T it determines, at compile time, which
31  * encoding family applies (raw bytes, Protobuf, FlatBuffers, FastDDS CDR,
32  * standard-layout POD, etc.) and dispatches @c serialize() / @c deserialize()
33  * to the appropriate code path with zero runtime cost.
34  *
35  * Application code rarely calls these helpers directly; @c Publisher,
36  * @c Subscriber, @c Client, @c Server, @c Setter, and @c Getter call them
37  * internally as part of their @c publish() / @c listen() / @c invoke() /
38  * @c set() / @c get() implementations.
39  *
40  * @par Codec Table -- @c Serializer::Type Enum
41  * | Constant | C++ Type Criterion | Trait check | Notes |
42  * | ------------------- | ----------------------------------- | ----------------------- | --------------------------- |
43  * | @c kBytesType | @c T == @c Bytes | @c is_bytes_type | Pass-through, no codec. |
44  * | @c kDynamicType | Has @c is_vlink_dynamic_data() | @c is_dynamic_type | Dynamic typed data. |
45  * | @c kCdrType | Has @c serialize/deserialize(Cdr&) | @c is_cdr_type | FastDDS CDR fast path. |
46  * | @c kProtoType | Has SerializeToArray/ParseFromArray | @c is_proto_type | Protobuf-like value. |
47  * | @c kProtoPtrType | Pointer with proto serialize/parse | @c is_proto_ptr_type | Caller owns the pointee. |
48  * | @c kFlatTableType | Derives from FB NativeTable | @c is_flat_table_type | FlatBuffers object API. |
49  * | @c kFlatPtrType | Pointer to @c flatbuffers::Table | @c is_flat_ptr_type | Zero-copy FlatBuffers view. |
50  * | @c kFlatBuilderType | Has @c fbb_ member and @c Finish() | @c is_flat_builder_type | FlatBuffers builder. |
51  * | @c kCustomType | Has @c operator>>/<<(Bytes&) | @c is_custom_type | User-supplied codec. |
52  * | @c kStringType | @c T == @c std::string | @c is_string_type | UTF-8 string. |
53  * | @c kCharsType | String-constructible (not @c string)| @c is_chars_type | C string / @c char*. |
54  * | @c kStreamType | Streamable via @c std::stringstream | @c is_stream_type | Reached only as fallback. |
55  * | @c kStandardType | Trivial standard-layout value (POD) | @c is_standard_type | @c sizeof(T) byte copy. |
56  * | @c kStandardPtrType | Pointer to trivial standard-layout | @c is_standard_ptr_type | Zero-copy POD pointer. |
57  *
58  * Most value-like detectors unwrap @c std::shared_ptr<T> before matching
59  * (e.g. Protobuf values, CDR values, FlatBuffers native tables, custom
60  * codecs, strings, stream types, and standard-layout values).
61  *
62  * @par Detection Precedence Flow
63  * @verbatim
64  * get_type_of<T>() probes traits in this fixed order; first match wins:
65  *
66  * Bytes --(no)--> Dynamic --(no)--> CDR --(no)--> Proto
67  * |
68  * v (no)
69  * FlatPtr <--(no)-- FlatTable <--(no)-- ProtoPtr <--(no)--+
70  * |
71  * v (no)
72  * FlatBuilder --(no)--> Custom --(no)--> String --(no)--> Chars
73  * |
74  * v (no)
75  * Stream <--(no)-- StandardPtr <--(no)-- Standard <--(no)----+
76  * |
77  * v (no)
78  * kUnknownType
79  * @endverbatim
80  *
81  * @par Type Detection Example
82  * @code
83  * constexpr auto t = vlink::Serializer::get_type_of<MyProto>(); // -> kProtoType
84  * static_assert(vlink::Serializer::is_supported(t));
85  *
86  * constexpr auto u = vlink::Serializer::get_type_of<int>(); // -> kStandardType (POD)
87  * constexpr auto v = vlink::Serializer::get_type_of<std::string>(); // -> kStringType
88  * constexpr auto w = vlink::Serializer::get_type_of<const char*>(); // -> kCharsType
89  * @endcode
90  *
91  * @par Serialise and Deserialise
92  * @code
93  * MyProto msg;
94  * vlink::Bytes bytes;
95  * vlink::Serializer::serialize(msg, bytes);
96  *
97  * MyProto out;
98  * vlink::Serializer::deserialize(bytes, out);
99  * @endcode
100  *
101  * @par Custom Codec
102  * @code
103  * struct MyCustomMsg {
104  * int x;
105  * void operator>>(vlink::Bytes& out) const { ... } // serialise
106  * void operator<<(const vlink::Bytes& in) { ... } // deserialise
107  * };
108  * // vlink::Serializer::get_type_of<MyCustomMsg>() == vlink::Serializer::kCustomType
109  * @endcode
110  *
111  * @par Transport-aware Fast Path
112  * Some transports (e.g. @c dds://) use pointer passing for CDR types. Use
113  * the explicit overloads to opt into the transport-specific path:
114  * @code
115  * vlink::Serializer::serialize<vlink::Serializer::kCdrType>(msg, bytes, vlink::TransportType::kDds);
116  * @endcode
117  *
118  * @note Most entry points are header-defined templates; a few non-template
119  * overloads are declared @c static or @c inline where appropriate.
120  *
121  * @see base/bytes.h, impl/types.h
122  */
123 
124 #pragma once
125 
126 #include <string>
127 
128 #include "./base/bytes.h"
129 #include "./impl/types.h"
130 
131 namespace vlink {
132 
133 /**
134  * @namespace Serializer
135  * @brief Compile-time codec detection and dispatch for VLink message payloads.
136  *
137  * @details
138  * Header-defined helper namespace. Most entry points are templates so the
139  * full codec chain is resolved at compile time. Application code rarely
140  * uses this namespace directly; the framework invokes it internally inside
141  * @c publish() / @c listen() / @c invoke() / @c set() / @c get().
142  */
143 namespace Serializer { // NOLINT(readability-identifier-naming)
144 
145 /**
146  * @enum Type
147  * @brief Identifies the codec to use for a given C++ message type.
148  *
149  * @details
150  * Resolved at compile time by @c get_type_of<T>() and stored as a
151  * @c constexpr member on every primitive class, so all codec dispatch is
152  * zero-cost at runtime.
153  */
154 enum Type : uint8_t {
155  kUnknownType = 0, ///< Unsupported type; @c is_supported() returns @c false.
156  kBytesType = 1, ///< @c Bytes -- raw byte pass-through.
157  kDynamicType = 2, ///< VLink dynamic typed data.
158  kCustomType = 3, ///< User-defined codec via @c operator>>/<<.
159  kCdrType = 4, ///< FastDDS CDR via @c serialize(Cdr&) / @c deserialize(Cdr&).
160  kProtoType = 5, ///< Protobuf-like value.
161  kProtoPtrType = 6, ///< Protobuf-like raw pointer; caller-owned.
162  kFlatTableType = 7, ///< FlatBuffers NativeTable (object API).
163  kFlatPtrType = 8, ///< Pointer to @c flatbuffers::Table (zero-copy view).
164  kFlatBuilderType = 9, ///< FlatBuffers builder (@c fbb_ + @c Finish()).
165  kStringType = 10, ///< @c std::string -- UTF-8 text.
166  kCharsType = 11, ///< C string / @c char*.
167  kStreamType = 12, ///< Stream-serialisable via @c std::stringstream.
168  kStandardType = 13, ///< Trivial standard-layout struct (POD value).
169  kStandardPtrType = 14, ///< Pointer to trivial standard-layout struct (POD pointer).
170 };
171 
172 /**
173  * @brief Reports whether @p type identifies a usable codec.
174  *
175  * @details
176  * @c kUnknownType is the only unsupported value. This function is invoked
177  * from the @c static_assert in every primitive constructor so unsupported
178  * message types fail at compile time with a clear diagnostic.
179  *
180  * @param type Codec enumerator.
181  * @return @c false only for @c kUnknownType.
182  */
183 [[maybe_unused]] [[nodiscard]] static constexpr bool is_supported(Type type) noexcept;
184 
185 /**
186  * @brief Resolves the codec @c Type for @c T at compile time.
187  *
188  * @details
189  * Evaluates the @c if-constexpr chain documented above and returns the
190  * first matching enumerator. Returns @c kUnknownType if no codec matches.
191  *
192  * @tparam T C++ message type to classify.
193  * @return Resolved @c Type enumerator.
194  */
195 template <typename T>
196 [[nodiscard]] static constexpr Type get_type_of() noexcept;
197 
198 /**
199  * @brief Returns the coarse schema family for @c T with an explicit codec tag.
200  *
201  * @tparam TypeT Explicit VLink codec kind.
202  * @tparam T C++ message type to classify.
203  * @return @c SchemaType::kProtobuf, @c kFlatbuffers, @c kZeroCopy, or @c kRaw.
204  */
205 template <Type TypeT, typename T>
206 [[nodiscard]] static constexpr SchemaType get_schema_type() noexcept;
207 
208 /**
209  * @brief Returns the coarse schema family inferred from @c T alone.
210  *
211  * @tparam T C++ message type to classify.
212  * @return @c SchemaType::kProtobuf, @c kFlatbuffers, @c kZeroCopy, or @c kRaw.
213  */
214 template <typename T>
215 [[nodiscard]] static constexpr SchemaType get_schema_type() noexcept;
216 
217 /**
218  * @brief Returns the serialised type-name string for @c T with explicit codec tag.
219  *
220  * @details
221  * Used by the framework for cross-peer type matching (DDS topic type name,
222  * Protobuf fully-qualified name, FlatBuffers table name, etc.). Returns an
223  * empty string for codecs with no meaningful type name (e.g. @c kBytesType).
224  *
225  * @tparam TypeT Explicit codec kind.
226  * @tparam T C++ message type.
227  * @return Type-name string; empty if not applicable.
228  */
229 template <Type TypeT, typename T>
230 [[nodiscard]] static std::string get_serialized_type() noexcept;
231 
232 /**
233  * @brief Returns the serialised type-name string for @c T (codec auto-detected).
234  *
235  * @tparam T C++ message type.
236  * @return Type-name string; empty if not applicable.
237  */
238 template <typename T>
239 [[nodiscard]] static std::string get_serialized_type() noexcept;
240 
241 /**
242  * @brief Returns a serialised-size hint for @p src with explicit codec tag.
243  *
244  * @details
245  * Used to size loaned buffers ahead of serialisation. The returned value is
246  * an exact byte count only for codecs that can produce one cheaply; it is
247  * @c 0 for codecs that cannot report an upfront size (e.g. @c kBytesType,
248  * @c kStringType, @c kFlatTableType, @c kStandardType).
249  *
250  * @tparam TypeT Codec kind.
251  * @tparam T C++ message type.
252  * @param src Source value to measure.
253  * @return Byte-count hint; @c 0 if unknown.
254  */
255 template <Type TypeT, typename T>
256 [[nodiscard]] static size_t get_serialized_size(const T& src) noexcept;
257 
258 /**
259  * @brief Returns a serialised-size hint for @p src (codec auto-detected).
260  *
261  * @tparam T C++ message type.
262  * @param src Source value to measure.
263  * @return Byte-count hint; @c 0 if unknown.
264  */
265 template <typename T>
266 [[nodiscard]] static size_t get_serialized_size(const T& src) noexcept;
267 
268 /**
269  * @brief Serialises @p src into @p des with explicit codec and transport tags.
270  *
271  * @details
272  * @p transport activates transport-specific fast paths (e.g. CDR pointer
273  * passing on @c kDds). Pass @c TransportType::kUnknown for the default
274  * copy-based path. @p offset prepends that many zero bytes before the
275  * payload (used internally by some transports for framing).
276  *
277  * For @c kFlatBuilderType, serialisation calls the builder's @c Finish()
278  * path so @p src may be mutated. Loaned output buffers shallow-borrow the
279  * builder's internal storage; keep the builder alive while the borrowed
280  * @c Bytes is in use.
281  *
282  * @tparam TypeT Codec kind.
283  * @tparam T C++ message type.
284  * @param src Source value to serialise.
285  * @param des Destination @c Bytes buffer (may be loaned).
286  * @param transport Active transport back-end for fast-path selection.
287  * @param offset Number of header bytes to prepend (default @c 0).
288  * @return @c true on success; @c false on codec failure.
289  */
290 template <Type TypeT, typename T>
291 static bool serialize(const T& src, Bytes& des, TransportType transport = TransportType::kUnknown, uint8_t offset = 0);
292 
293 /**
294  * @brief Serialises @p src into @p des (codec and transport auto-detected).
295  *
296  * @tparam T C++ message type.
297  * @param src Source value.
298  * @param des Destination @c Bytes buffer.
299  * @return @c true on success.
300  */
301 template <typename T>
302 static bool serialize(const T& src, Bytes& des);
303 
304 /**
305  * @brief Deserialises @p src into @p des with explicit codec and transport tags.
306  *
307  * @details
308  * @p transport activates transport-specific fast paths (e.g. @c kDds
309  * dereferences a CDR pointer stored in @p src instead of copying bytes).
310  *
311  * @tparam TypeT Codec kind.
312  * @tparam T C++ message type.
313  * @param src Source @c Bytes buffer.
314  * @param des Destination value to fill.
315  * @param transport Active transport back-end.
316  * @return @c true on success; @c false on parse failure.
317  */
318 template <Type TypeT, typename T>
319 static bool deserialize(const Bytes& src, T& des, TransportType transport = TransportType::kUnknown);
320 
321 /**
322  * @brief Deserialises @p src into @p des (codec and transport auto-detected).
323  *
324  * @tparam T C++ message type.
325  * @param src Source @c Bytes buffer.
326  * @param des Destination value.
327  * @return @c true on success.
328  */
329 template <typename T>
330 static bool deserialize(const Bytes& src, T& des);
331 
332 /**
333  * @brief Converts between two types where at least one side is @c Bytes.
334  *
335  * @details
336  * A compile-time @c static_assert enforces that @c SrcT or @c DesT (or both)
337  * is @c Bytes. The three cases are:
338  * - Both @c Bytes: shallow-copies @p src to @p des.
339  * - @c DesT == @c Bytes: dispatches to @c serialize().
340  * - @c SrcT == @c Bytes: dispatches to @c deserialize().
341  *
342  * @tparam SrcT Source type.
343  * @tparam DesT Destination type.
344  * @param src Source value.
345  * @param des Destination value.
346  * @return @c true on success.
347  */
348 template <typename SrcT, typename DesT>
349 static bool convert(const SrcT& src, DesT& des);
350 
351 /**
352  * @brief Dereferences a value, unwrapping @c std::shared_ptr when present.
353  *
354  * @details
355  * If @c T is @c std::shared_ptr<U>, returns @c *t; otherwise returns @c t.
356  * Internal helper so codec code can treat both value and shared-pointer
357  * inputs uniformly.
358  *
359  * @tparam T Input type (value or @c shared_ptr).
360  * @param t Input value.
361  * @return Reference to the underlying value.
362  */
363 template <typename T>
364 [[nodiscard]] static constexpr auto& deref(const T& t) noexcept;
365 
366 /**
367  * @brief Reports whether @c T is exactly @c Bytes.
368  *
369  * @tparam T Type to test.
370  * @return @c true for @c Bytes.
371  */
372 template <typename T>
373 [[nodiscard]] static constexpr bool is_bytes_type() noexcept;
374 
375 /**
376  * @brief Reports whether @c T is a VLink dynamic data type.
377  *
378  * @details
379  * Dynamic types expose an @c is_vlink_dynamic_data() member.
380  *
381  * @tparam T Type to test.
382  * @return @c true for dynamic data types.
383  */
384 template <typename T>
385 [[nodiscard]] static constexpr bool is_dynamic_type() noexcept;
386 
387 /**
388  * @brief Reports whether @c T is a FastDDS CDR-serialisable type.
389  *
390  * @details
391  * Requires @c fastcdr to be available, plus either both
392  * @c serialize(Cdr&) and @c deserialize(Cdr&) methods, or a type name
393  * carrying the @c VLINK_FASTDDS_IDL_PREFIX prefix.
394  *
395  * @tparam T Type to test.
396  * @return @c true for CDR types when @c fastcdr is available.
397  */
398 template <typename T>
399 [[nodiscard]] static constexpr bool is_cdr_type() noexcept;
400 
401 /**
402  * @brief Reports whether @c T is a Protobuf-like message value type.
403  *
404  * @details
405  * Requires Protobuf to be available and the type to expose
406  * @c SerializeToArray() and @c ParseFromArray() methods.
407  *
408  * @tparam T Type to test.
409  * @return @c true for Protobuf-compatible value types.
410  */
411 template <typename T>
412 [[nodiscard]] static constexpr bool is_proto_type() noexcept;
413 
414 /**
415  * @brief Reports whether @c T is a raw pointer to a Protobuf-like message.
416  *
417  * @details
418  * The pointee is not owned by the serialiser and must be non-null whenever
419  * the codec path dereferences it.
420  *
421  * @tparam T Pointer type to test.
422  * @return @c true for Protobuf-compatible pointer types.
423  */
424 template <typename T>
425 [[nodiscard]] static constexpr bool is_proto_ptr_type() noexcept;
426 
427 /**
428  * @brief Reports whether @c T is a FlatBuffers NativeTable type.
429  *
430  * @details
431  * Requires @c flatbuffers and the type (or its @c shared_ptr element type)
432  * to derive from @c flatbuffers::NativeTable.
433  *
434  * @tparam T Type to test.
435  * @return @c true for FlatBuffers NativeTable types.
436  */
437 template <typename T>
438 [[nodiscard]] static constexpr bool is_flat_table_type() noexcept;
439 
440 /**
441  * @brief Reports whether @c T is a FlatBuffers builder type.
442  *
443  * @details
444  * Requires @c flatbuffers and the type to expose both an @c fbb_ member
445  * and a @c Finish() method.
446  *
447  * @tparam T Type to test.
448  * @return @c true for FlatBuffers builder types.
449  */
450 template <typename T>
451 [[nodiscard]] static constexpr bool is_flat_builder_type() noexcept;
452 
453 /**
454  * @brief Reports whether @c T is a raw pointer to a @c flatbuffers::Table.
455  *
456  * @tparam T Pointer type to test.
457  * @return @c true for FlatBuffers Table pointer types.
458  */
459 template <typename T>
460 [[nodiscard]] static constexpr bool is_flat_ptr_type() noexcept;
461 
462 /**
463  * @brief Reports whether @c T provides a custom @c operator>>/<< codec.
464  *
465  * @details
466  * Checked via @c Traits::Operatorable for @c operator>>(Bytes&) and
467  * @c operator<<(const Bytes&).
468  *
469  * @tparam T Type to test.
470  * @return @c true for custom-codec types.
471  */
472 template <typename T>
473 [[nodiscard]] static constexpr bool is_custom_type() noexcept;
474 
475 /**
476  * @brief Reports whether @c T is @c std::string after unwrapping @c shared_ptr.
477  *
478  * @tparam T Type to test.
479  * @return @c true for @c std::string and @c std::shared_ptr<std::string>.
480  */
481 template <typename T>
482 [[nodiscard]] static constexpr bool is_string_type() noexcept;
483 
484 /**
485  * @brief Reports whether @c std::string is constructible from @c T (but @c T is not @c string).
486  *
487  * @details
488  * Matches @c char*, @c const char*, and string literal types.
489  *
490  * @tparam T Type to test.
491  * @return @c true for C-string-compatible types.
492  */
493 template <typename T>
494 [[nodiscard]] static constexpr bool is_chars_type() noexcept;
495 
496 /**
497  * @brief Reports whether @c T supports bidirectional @c std::stringstream streaming.
498  *
499  * @details
500  * Detected via @c Traits::Operatorable<std::stringstream, T>(); the check
501  * requires both @c ss << t and @c ss >> t to be well-formed. Higher-priority
502  * codecs are checked first in @c get_type_of(), so this function is only
503  * reached for types that fail every earlier trait.
504  *
505  * @tparam T Type to test.
506  * @return @c true for stream-serialisable types.
507  */
508 template <typename T>
509 [[nodiscard]] static constexpr bool is_stream_type() noexcept;
510 
511 /**
512  * @brief Reports whether @c T is a trivial standard-layout value (POD).
513  *
514  * @details
515  * Matches non-pointer types where both @c std::is_trivial_v and
516  * @c std::is_standard_layout_v hold. Such types are byte-copied into and
517  * out of a @c Bytes buffer of @c sizeof(T) bytes.
518  *
519  * @tparam T Type to test.
520  * @return @c true for POD value types.
521  */
522 template <typename T>
523 [[nodiscard]] static constexpr bool is_standard_type() noexcept;
524 
525 /**
526  * @brief Reports whether @c T is a pointer to a trivial standard-layout type.
527  *
528  * @details
529  * Matches @c U* where @c std::is_trivial_v<U> && @c std::is_standard_layout_v<U>.
530  * The pointer is reinterpreted (not copied through) for zero-copy use.
531  *
532  * @tparam T Pointer type to test.
533  * @return @c true for POD-pointer types.
534  */
535 template <typename T>
536 [[nodiscard]] static constexpr bool is_standard_ptr_type() noexcept;
537 
538 } // namespace Serializer
539 
540 } // namespace vlink
541 
542 #include "./internal/serializer-inl.h"
Canonical 128-byte binary payload carrier with inline storage, multi-mode ownership and LZAV compress...
Compile-time codec detection and dispatch for VLink message payloads.
Core enumerations and small value types shared by the entire VLink implementation layer.