Ice 3.8
C++ API Reference
Loading...
Searching...
No Matches
StreamHelpers.h
1// Copyright (c) ZeroC, Inc.
2
3#ifndef ICE_STREAM_HELPERS_H
4#define ICE_STREAM_HELPERS_H
5
6#include "InputStream.h"
7#include "OutputStream.h"
8#include "StringConverter.h"
9
10#include <iterator>
11#include <ostream>
12
13#if defined(_MSC_VER)
14// With MSVC, __has_include(<span>) evaluates to 1 in c++17 mode and the <span> header then issues a warning that can't
15// be disabled. This is a permissible but impractical behavior. See https://github.com/microsoft/STL/issues/4010.
16# if _MSVC_LANG >= 202002L
17# include <span>
18# endif
19#elif __has_include(<span>)
20# include <span>
21#endif
22
23namespace Ice
24{
25 // StreamHelper templates used by streams to read, write and print data.
26
27#ifdef ICE_DOXYGEN
28 /// Prints a value to a stream.
29 /// @tparam T The type of the value. It's either built-in Slice type or a type generated by the Slice compiler.
30 /// @param stream The stream.
31 /// @param v The value to print.
32 /// @remarks Depending on the type, @p v is passed by value or by const reference.
33 template<typename T> void print(std::ostream& stream, T v);
34
35 // We only provide this declaration for Doxygen. This template is specialized for all types that we support, and
36 // we want the compilation (not linking) to fail if a specialization is missing.
37
38 /// A helper template class for writing (marshaling) and reading (unmarshaling) values to and from a stream of
39 /// Slice-encoded bytes. This helper also prints Slice-defined types.
40 /// @tparam T The type of the value.
41 /// @tparam st The stream helper category.
42 /// @headerfile Ice/Ice.h
43 template<typename T, StreamHelperCategory st> struct StreamHelper
44 {
45 /// Writes a value to the stream.
46 /// @param stream The stream.
47 /// @param v The value to write.
48 /// @remark Each specialization chooses whether @p v is passed by value or by const reference.
49 static void write(OutputStream* stream, T v);
50
51 /// Reads a value from the stream.
52 /// @param stream The stream.
53 /// @param[out] v A reference to the value to read into.
54 static void read(InputStream* stream, T& v);
55
56 /// Prints a value to the stream.
57 /// @param stream The stream.
58 /// @param v The value to print.
59 /// @remark Each specialization chooses whether @p v is passed by value or by const reference.
60 static void print(std::ostream& stream, T v);
61 };
62#endif
63
64 /// @cond INTERNAL
65
66 /// Prints a value with a pass-by-value type (such as `bool`, `int32`, or an enumeration) to a stream.
67 /// @tparam T The type of the value.
68 /// @param stream The stream.
69 /// @param v The value to print.
70 template<
71 typename T,
72 std::enable_if_t<
75 bool> = true>
76 inline void print(std::ostream& stream, T v)
77 {
79 }
80
81 /// Prints an optional value with a pass-by-value type (such as `bool`, `int32`, or an enumeration) to a stream.
82 /// @tparam T The type of the value.
83 /// @param stream The stream.
84 /// @param v The value to print. nullopt is printed as "nullopt".
85 template<
86 typename T,
87 std::enable_if_t<
90 bool> = true>
91 inline void print(std::ostream& stream, std::optional<T> v)
92 {
93 if (v)
94 {
95 Ice::print(stream, *v);
96 }
97 else
98 {
99 stream << "nullopt";
100 }
101 }
102
103 /// Prints a value with a pass-by-const-reference type to the stream.
104 /// @tparam T The type of the value.
105 /// @param stream The stream.
106 /// @param v The value to print.
107 template<
108 typename T,
109 std::enable_if_t<
112 bool> = true>
113 inline void print(std::ostream& stream, const T& v)
114 {
116 }
117
118 /// Prints an optional value with a pass-by-const-reference type to the stream.
119 /// @tparam T The type of the value.
120 /// @param stream The stream.
121 /// @param v The value to print. nullopt is printed as "nullopt".
122 template<
123 typename T,
124 std::enable_if_t<
128 bool> = true>
129 inline void print(std::ostream& stream, const std::optional<T>& v)
130 {
131 if (v)
132 {
133 Ice::print(stream, *v);
134 }
135 else
136 {
137 stream << "nullopt";
138 }
139 }
140
141 template<typename T> struct StreamHelper<T, StreamHelperCategoryBuiltinValue>
142 {
143 static void write(OutputStream* stream, T v) { stream->write(v); }
144
145 static void read(InputStream* stream, T& v) { stream->read(v); }
146
147 static void print(std::ostream& stream, T v) { stream << v; }
148 };
149
150 template<> struct StreamHelper<bool, StreamHelperCategoryBuiltinValue>
151 {
152 static void print(std::ostream& stream, bool v) { stream << (v ? "true" : "false"); }
153 };
154
155 template<> struct StreamHelper<std::uint8_t, StreamHelperCategoryBuiltinValue>
156 {
157 static void print(std::ostream& stream, std::uint8_t v) { stream << static_cast<int>(v); }
158 };
159
160 template<> struct StreamHelper<std::byte, StreamHelperCategoryBuiltinValue>
161 {
162 static void print(std::ostream& stream, std::byte v) { stream << static_cast<int>(v); }
163 };
164
165 template<> struct StreamHelper<std::string_view, StreamHelperCategoryBuiltinValue>
166 {
167 static void write(OutputStream* stream, std::string_view v) { stream->write(v); }
168
169 // No read: we marshal string views but unmarshal strings.
170 // No print: we only print fields.
171 };
172
173 template<> struct StreamHelper<std::wstring_view, StreamHelperCategoryBuiltinValue>
174 {
175 static void write(OutputStream* stream, std::wstring_view v) { stream->write(v); }
176
177 // No read: we marshal wstring views but don't unmarshal wstrings.
178 // No print: we only print fields.
179 };
180
181 /// Helper for built-in types that are not passed by value.
182 template<typename T> struct StreamHelper<T, StreamHelperCategoryBuiltin>
183 {
184 static void write(OutputStream* stream, const T& v) { stream->write(v); }
185
186 static void read(InputStream* stream, T& v) { stream->read(v); }
187
188 static void print(std::ostream& stream, const T& v) { stream << v; }
189 };
190
191 template<> struct StreamHelper<std::wstring, StreamHelperCategoryBuiltin>
192 {
193 static void print(std::ostream& stream, const std::wstring& v) { stream << Ice::wstringToString(v); }
194 };
195
196 template<> struct StreamHelper<std::vector<bool>, StreamHelperCategoryBuiltin>
197 {
198 static void print(std::ostream& stream, const std::vector<bool>& v)
199 {
200 stream << '[';
201 bool firstElement = true;
202 for (bool element : v)
203 {
204 if (!firstElement)
205 {
206 stream << ", ";
207 }
208 firstElement = false;
209 Ice::print(stream, element);
210 }
211 stream << ']';
212 }
213 };
214
215 //
216 // "helpers" for the StreamHelper<T, StreamHelperCategoryStruct>. slice2cpp generates specializations as needed.
217 //
218
219 /// Reader used/generated for structs. Always specialized.
220 template<typename T> struct StreamReader;
221
222 template<typename T> struct StreamHelper<T, StreamHelperCategoryStruct>
223 {
224 static void write(OutputStream* stream, const T& v) { stream->writeAll(v.ice_tuple()); }
225
226 static void read(InputStream* stream, T& v) { StreamReader<T>::read(stream, v); }
227
228 static void print(std::ostream& stream, const T& v) { stream << v; }
229 };
230
231 template<typename T> struct StreamHelper<T, StreamHelperCategoryEnum>
232 {
233 static void write(OutputStream* stream, T v)
234 {
235 if (static_cast<std::int32_t>(v) < StreamableTraits<T>::minValue ||
236 static_cast<std::int32_t>(v) > StreamableTraits<T>::maxValue)
237 {
238 IceInternal::Ex::throwMarshalException(__FILE__, __LINE__, "enumerator out of range");
239 }
240 stream->writeEnum(static_cast<std::int32_t>(v), StreamableTraits<T>::maxValue);
241 }
242
243 static void read(InputStream* stream, T& v)
244 {
245 std::int32_t value = stream->readEnum(StreamableTraits<T>::maxValue);
246 if (value < StreamableTraits<T>::minValue || value > StreamableTraits<T>::maxValue)
247 {
248 IceInternal::Ex::throwMarshalException(__FILE__, __LINE__, "enumerator out of range");
249 }
250 v = static_cast<T>(value); // NOLINT
251 }
252
253 static void print(std::ostream& stream, T v) { stream << v; }
254 };
255
256 template<typename T> struct StreamHelper<T, StreamHelperCategorySequence>
257 {
258 static void write(OutputStream* stream, const T& v)
259 {
260 stream->writeSize(static_cast<std::int32_t>(v.size()));
261 for (const auto& element : v)
262 {
263 stream->write(element);
264 }
265 }
266
267 static void read(InputStream* stream, T& v)
268 {
269 std::int32_t sz = stream->readAndCheckSeqSize(StreamableTraits<typename T::value_type>::minWireSize);
270 T(static_cast<size_t>(sz)).swap(v);
271 for (auto& element : v)
272 {
273 stream->read(element);
274 }
275 }
276
277 static void print(std::ostream& stream, const T& v)
278 {
279 stream << '[';
280 bool firstElement = true;
281 for (const auto& element : v)
282 {
283 if (!firstElement)
284 {
285 stream << ", ";
286 }
287 firstElement = false;
288 Ice::print(stream, element);
289 }
290 stream << ']';
291 }
292 };
293
294 /// Helper the array custom sequence mapping.
295 template<typename T> struct StreamHelper<std::pair<const T*, const T*>, StreamHelperCategorySequence>
296 {
297 static void write(OutputStream* stream, std::pair<const T*, const T*> v) { stream->write(v.first, v.second); }
298
299 static void read(InputStream* stream, std::pair<const T*, const T*>& v) { stream->read(v); }
300
301 // No print: we only print fields.
302 };
303
304#ifdef __cpp_lib_span
305 /// Helper for span (C++20 or later).
306 template<typename T> struct StreamHelper<std::span<T>, StreamHelperCategorySequence>
307 {
308 static void write(OutputStream* stream, const std::span<T>& v) { stream->write(v.data(), v.data() + v.size()); }
309
310 // No read. span are only for view types.
311 // No print: we only print fields.
312 };
313#endif
314
315 template<typename T> struct StreamHelper<T, StreamHelperCategoryDictionary>
316 {
317 static void write(OutputStream* stream, const T& v)
318 {
319 stream->writeSize(static_cast<std::int32_t>(v.size()));
320 for (const auto& entry : v)
321 {
322 stream->write(entry.first);
323 stream->write(entry.second);
324 }
325 }
326
327 static void read(InputStream* stream, T& v)
328 {
329 std::int32_t sz = stream->readSize();
330 v.clear();
331 while (sz--)
332 {
333 typename T::value_type p;
334 stream->read(const_cast<typename T::key_type&>(p.first));
335 auto i = v.insert(v.end(), p);
336 stream->read(i->second);
337 }
338 }
339
340 static void print(std::ostream& stream, const T& v)
341 {
342 stream << '[';
343 bool firstEntry = true;
344 for (const auto& entry : v)
345 {
346 if (!firstEntry)
347 {
348 stream << ", ";
349 }
350 firstEntry = false;
351 stream << '{';
352 Ice::print(stream, entry.first);
353 stream << " : ";
354 Ice::print(stream, entry.second);
355 stream << '}';
356 }
357 stream << ']';
358 }
359 };
360
361 template<typename T> struct StreamHelper<T, StreamHelperCategoryUserException>
362 {
363 static void write(OutputStream* stream, const T& v) { stream->writeException(v); }
364
365 // no read: we don't use this helper for unmarshaling.
366
367 // We provide print for consistency even though user exceptions cannot appear in fields.
368 static void print(std::ostream& stream, const T& v) { stream << v; }
369 };
370
371 template<typename T> struct StreamHelper<T, StreamHelperCategoryProxy>
372 {
373 static void write(OutputStream* stream, const T& v) { stream->write(v); }
374
375 static void read(InputStream* stream, T& v) { stream->read(v); }
376
377 static void print(std::ostream& stream, const T& v) { stream << v; }
378 };
379
380 template<typename T> struct StreamHelper<T, StreamHelperCategoryClass>
381 {
382 static void write(OutputStream* stream, const T& v) { stream->write(v); }
383
384 static void read(InputStream* stream, T& v) { stream->read(v); }
385
386 static void print(std::ostream& stream, const T& v) { stream << v; }
387 };
388
389 //
390 // Helpers to read/write optional fields and arguments.
391 //
392
393 /// Extracts / computes the optionalFormat.
394 // This is used _only_ for the base StreamOptionalHelper below.
395 // /!\ Do not use in StreamOptionalHelper specializations, and do not provide specialization not handled by the
396 // base StreamOptionalHelper.
397 template<StreamHelperCategory st, int minWireSize, bool fixedLength> struct GetOptionalFormat;
398
399 /// Specialization for 1-byte built-in fixed-length types.
400 template<> struct GetOptionalFormat<StreamHelperCategoryBuiltinValue, 1, true>
401 {
402 static constexpr OptionalFormat value = OptionalFormat::F1;
403 };
404
405 /// Specialization for 2-byte built-in fixed-length types.
406 template<> struct GetOptionalFormat<StreamHelperCategoryBuiltinValue, 2, true>
407 {
408 static constexpr OptionalFormat value = OptionalFormat::F2;
409 };
410
411 /// Specialization for 4-byte built-in fixed-length types.
412 template<> struct GetOptionalFormat<StreamHelperCategoryBuiltinValue, 4, true>
413 {
414 static constexpr OptionalFormat value = OptionalFormat::F4;
415 };
416
417 /// Specialization for 8-byte built-in fixed-length types.
418 template<> struct GetOptionalFormat<StreamHelperCategoryBuiltinValue, 8, true>
419 {
420 static constexpr OptionalFormat value = OptionalFormat::F8;
421 };
422
423 /// Specialization for built-in variable-length types.
424 template<> struct GetOptionalFormat<StreamHelperCategoryBuiltinValue, 1, false>
425 {
426 static constexpr OptionalFormat value = OptionalFormat::VSize;
427 };
428
429 /// Specialization for built-in variable-length types.
430 template<> struct GetOptionalFormat<StreamHelperCategoryBuiltin, 1, false>
431 {
432 static constexpr OptionalFormat value = OptionalFormat::VSize;
433 };
434
435 /// Specialization for enum types.
436 template<int minWireSize> struct GetOptionalFormat<StreamHelperCategoryEnum, minWireSize, false>
437 {
438 static constexpr OptionalFormat value = OptionalFormat::Size;
439 };
440
441 /// Base helper template for reading and writing optional values. The default implementations reads/writes the
442 /// value as-is.
443 template<typename T, StreamHelperCategory st, bool fixedLength> struct StreamOptionalHelper
444 {
445 using Traits = StreamableTraits<T>;
446
447 // If this optionalFormat fails to compile, you must either define your specialization
448 // for GetOptionalFormat (in which case the optional data will be marshaled/unmarshaled
449 // with straight calls to write/read on the stream), or define your own
450 // StreamOptionalHelper specialization (which gives you more control over marshaling)
451 //
452 static constexpr OptionalFormat optionalFormat = GetOptionalFormat<st, Traits::minWireSize, fixedLength>::value;
453
454 static void write(OutputStream* stream, const T& v) { stream->write(v); }
455
456 static void read(InputStream* stream, T& v) { stream->read(v); }
457 };
458
459 /// Helper to write fixed-size structs.
460 template<typename T> struct StreamOptionalHelper<T, StreamHelperCategoryStruct, true>
461 {
462 static constexpr OptionalFormat optionalFormat = OptionalFormat::VSize;
463
464 static void write(OutputStream* stream, const T& v)
465 {
466 stream->writeSize(StreamableTraits<T>::minWireSize);
467 stream->write(v);
468 }
469
470 static void read(InputStream* stream, T& v)
471 {
472 stream->skipSize();
473 stream->read(v);
474 }
475 };
476
477 /// Helper to write variable-size structs.
478 template<typename T> struct StreamOptionalHelper<T, StreamHelperCategoryStruct, false>
479 {
480 static constexpr OptionalFormat optionalFormat = OptionalFormat::FSize;
481
482 static void write(OutputStream* stream, const T& v)
483 {
484 OutputStream::size_type pos = stream->startSize();
485 stream->write(v);
486 stream->endSize(pos);
487 }
488
489 static void read(InputStream* stream, T& v)
490 {
491 stream->skip(4);
492 stream->read(v);
493 }
494 };
495
496 // InputStream and OutputStream have special logic for optional proxies that does not rely on the
497 // StreamOptional helpers.
498
499 /// Helper to read/write optional sequences or dictionaries.
500 template<typename T, bool fixedLength, int sz> struct StreamOptionalContainerHelper;
501
502 /// Encodes containers of variable-size elements with the FSize optional type, since we can't easily figure out
503 /// total number of bytes we're about to write.
504 // This is the same encoding as variable size structs so we just re-use its implementation.
505 template<typename T, int sz> struct StreamOptionalContainerHelper<T, false, sz>
506 {
507 static constexpr OptionalFormat optionalFormat = OptionalFormat::FSize;
508
509 static void write(OutputStream* stream, const T& v, std::int32_t)
510 {
511 StreamOptionalHelper<T, StreamHelperCategoryStruct, false>::write(stream, v);
512 }
513
514 static void read(InputStream* stream, T& v)
515 {
516 StreamOptionalHelper<T, StreamHelperCategoryStruct, false>::read(stream, v);
517 }
518 };
519
520 /// Encodes containers of fixed-size elements with the VSize optional type since we can figure out the total number
521 /// of bytes we're about to write.
522 template<typename T, int sz> struct StreamOptionalContainerHelper<T, true, sz>
523 {
524 static constexpr OptionalFormat optionalFormat = OptionalFormat::VSize;
525
526 static void write(OutputStream* stream, const T& v, std::int32_t n)
527 {
528 // The total number of bytes is the number of elements * the size of an element and the size-encoded number
529 // of elements (1 or 5 depending on the number of elements).
530 stream->writeSize(sz * n + (n < 255 ? 1 : 5));
531 stream->write(v);
532 }
533
534 static void read(InputStream* stream, T& v)
535 {
536 stream->skipSize();
537 stream->read(v);
538 }
539 };
540
541 // Optimization: containers of 1 byte elements are encoded with the VSize optional type. There's no need to encode
542 // an additional size for those, the number of elements of the container can be used to skip the optional.
543 template<typename T> struct StreamOptionalContainerHelper<T, true, 1>
544 {
545 static constexpr OptionalFormat optionalFormat = OptionalFormat::VSize;
546
547 static void write(OutputStream* stream, const T& v, std::int32_t) { stream->write(v); }
548
549 static void read(InputStream* stream, T& v) { stream->read(v); }
550 };
551
552 // Helper to write sequences, delegates to the optional container helper template partial specializations.
553 template<typename T> struct StreamOptionalHelper<T, StreamHelperCategorySequence, false>
554 {
555 using E = typename T::value_type;
556 static constexpr int size = StreamableTraits<E>::minWireSize;
557 static constexpr bool fixedLength = StreamableTraits<E>::fixedLength;
558
559 // The optional type of a sequence depends on whether or not elements are fixed
560 // or variable size elements and their size.
561 static constexpr OptionalFormat optionalFormat =
562 StreamOptionalContainerHelper<T, fixedLength, size>::optionalFormat;
563
564 static void write(OutputStream* stream, const T& v)
565 {
566 StreamOptionalContainerHelper<T, fixedLength, size>::write(stream, v, static_cast<std::int32_t>(v.size()));
567 }
568
569 static void read(InputStream* stream, T& v)
570 {
571 StreamOptionalContainerHelper<T, fixedLength, size>::read(stream, v);
572 }
573 };
574
575 // Helper to write sequences, delegates to the optional container helper template partial specializations.
576 template<typename T> struct StreamOptionalHelper<std::pair<const T*, const T*>, StreamHelperCategorySequence, false>
577 {
578 using P = std::pair<const T*, const T*>;
579 static constexpr int size = StreamableTraits<T>::minWireSize;
580 static constexpr bool fixedLength = StreamableTraits<T>::fixedLength;
581
582 // The optional type of a sequence depends on whether or not elements are fixed
583 // or variable size elements and their size.
584 static constexpr OptionalFormat optionalFormat =
585 StreamOptionalContainerHelper<P, fixedLength, size>::optionalFormat;
586
587 static void write(OutputStream* stream, const P& v)
588 {
589 auto n = static_cast<std::int32_t>(v.second - v.first);
590 StreamOptionalContainerHelper<P, fixedLength, size>::write(stream, v, n);
591 }
592
593 static void read(InputStream* stream, P& v)
594 {
595 StreamOptionalContainerHelper<P, fixedLength, size>::read(stream, v);
596 }
597 };
598
599 // Helper to write dictionaries, delegates to the optional container helper template partial specializations.
600 template<typename T> struct StreamOptionalHelper<T, StreamHelperCategoryDictionary, false>
601 {
602 using K = typename T::key_type;
603 using V = typename T::mapped_type;
604
606 static constexpr bool fixedLength = StreamableTraits<K>::fixedLength && StreamableTraits<V>::fixedLength;
607
608 // The optional type of a dictionary depends on whether or not elements are fixed
609 // or variable size elements.
610 static constexpr OptionalFormat optionalFormat =
611 StreamOptionalContainerHelper<T, fixedLength, size>::optionalFormat;
612
613 static void write(OutputStream* stream, const T& v)
614 {
615 StreamOptionalContainerHelper<T, fixedLength, size>::write(stream, v, static_cast<std::int32_t>(v.size()));
616 }
617
618 static void read(InputStream* stream, T& v)
619 {
620 StreamOptionalContainerHelper<T, fixedLength, size>::read(stream, v);
621 }
622 };
623
624 // Specializations for ProtocolVersion and EncodingVersion
625 template<> struct StreamableTraits<ProtocolVersion>
626 {
628 static constexpr int minWireSize = 2;
629 static constexpr bool fixedLength = true;
630 };
631
632 template<> struct StreamReader<ProtocolVersion>
633 {
634 static void read(InputStream* istr, ProtocolVersion& v) { istr->readAll(v.major, v.minor); }
635 };
636
637 template<> struct StreamableTraits<EncodingVersion>
638 {
640 static constexpr int minWireSize = 2;
641 static constexpr bool fixedLength = true;
642 };
643
644 template<> struct StreamReader<EncodingVersion>
645 {
646 static void read(InputStream* istr, EncodingVersion& v) { istr->readAll(v.major, v.minor); }
647 };
648
649 /// @endcond
650}
651
652#endif
Represents a byte buffer used for unmarshaling data encoded using the Slice encoding.
Definition InputStream.h:50
std::size_t size_type
The size type for this byte buffer.
Represents a byte buffer used for marshaling data using the Slice encoding.
constexpr StreamHelperCategory StreamHelperCategoryBuiltin
Built-in types usually passed by reference.
void print(std::ostream &stream, T v)
Prints a value to a stream.
OptionalFormat
The optional format, used for marshaling optional fields and arguments.
@ VSize
"Size encoding" using 1 to 5 bytes followed by data, e.g., string, fixed size struct,...
@ FSize
Fixed size using 4 bytes followed by data, e.g., variable-size struct, container.
constexpr StreamHelperCategory StreamHelperCategoryDictionary
Dictionary types.
constexpr StreamHelperCategory StreamHelperCategoryEnum
Generated enum types.
constexpr StreamHelperCategory StreamHelperCategoryProxy
Proxy types.
int StreamHelperCategory
The stream helper category allows to select a StreamHelper specialization for a specific category of ...
constexpr StreamHelperCategory StreamHelperCategoryClass
Generated class types.
constexpr StreamHelperCategory StreamHelperCategoryStruct
Generated struct types.
constexpr StreamHelperCategory StreamHelperCategorySequence
Sequence types.
constexpr StreamHelperCategory StreamHelperCategoryBuiltinValue
Built-in types usually passed by value.
constexpr StreamHelperCategory StreamHelperCategoryUserException
User exception types.
std::string wstringToString(const std::wstring &str, const StringConverterPtr &nc=nullptr, const WstringConverterPtr &wc=nullptr)
Converts the given wide string to a narrow string.
The Ice RPC framework.
Definition SampleEvent.h:59
Represents a version of the Slice encoding.
Definition Version.h:65
Represents a version of the Ice protocol.
Definition Version.h:36
static void write(OutputStream *stream, T v)
Writes a value to the stream.
static void print(std::ostream &stream, T v)
Prints a value to the stream.
static void read(InputStream *stream, T &v)
Reads a value from the stream.
A helper template class for writing (marshaling) and reading (unmarshaling) values to and from a stre...
static constexpr bool fixedLength
Indicates if the type is always encoded on a fixed number of bytes.
static constexpr int minWireSize
The minimum number of bytes needed to marshal this type.
static constexpr StreamHelperCategory helper
The category trait, used for selecting the appropriate StreamHelper.
Provides traits for a type that can be marshaled or unmarshaled to/from a stream of bytes using the S...