Bond
transforms.h
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#pragma once
5
6#include <bond/core/config.h>
7
8#include "bond_fwd.h"
9#include "detail/debug.h"
10#include "detail/double_pass.h"
11#include "detail/marshaled_bonded.h"
12#include "detail/odr.h"
13#include "detail/omit_default.h"
14#include "detail/tags.h"
15#include "exception.h"
16#include "null.h"
17#include "reflection.h"
18
19#include <boost/static_assert.hpp>
20
21namespace bond
22{
23
24// Transforms take input from parser as a series of calls to the Field methods.
25// Arguments for the calls privide id, name and value of the fields. Return
26// value from Field methods must be convertible to bool, with the value of true
27// indicating that the transform has completed and the parser may exit.
28
29
30namespace detail
31{
32 template <typename T, typename Schema, typename Transform>
33 class _Parser;
34
35} // namespace detail
36
37
38//
39// Serializer writes input using provided protocol writer.
40// When the input is comming from parsing a struct, applying this transform is
41// equivalent to serialization using the specfied protocol.
42// Applying this transform to input from parsing serialized data is equivalent
43// to transcoding from one protocol to another.
44//
45template <typename Writer, typename Protocols>
46class Serializer
47 : public SerializingTransform
48{
49public:
50 typedef Writer writer_type;
51
52 BOOST_STATIC_ASSERT(is_writer<Writer>::value);
53
54 Serializer(Writer& output, bool base = false)
55 : _output(output),
56 _base(base)
57 {}
58
59
60 bool NeedPass0() const
61 {
62 return _output.NeedPass0();
63 }
64
65 template <typename Pass0>
66 Serializer<Pass0, Protocols> Rebind(Pass0& pass0) const
67 {
68 return Serializer<Pass0, Protocols>(pass0);
69 }
70
71 void Begin(const Metadata& metadata) const
72 {
73 _output.WriteStructBegin(metadata, _base);
74 }
75
76 void End() const
77 {
78 _output.WriteStructEnd(_base);
79 }
80
81 void UnknownEnd() const
82 {
83 _output.WriteStructEnd(true);
84 }
85
86 template <typename T>
87 bool Base(const T& value) const
88 {
89 // 'true' means that we are writing a base struct
90 Apply<Protocols>(Serializer(_output, true), value);
91 return false;
92 }
93
94 template <typename T>
95 typename boost::enable_if_c<may_omit_fields<Writer>::value && !is_bond_type<T>::value, bool>::type
96 Field(uint16_t id, const Metadata& metadata, const T& value) const
97 {
98 if (detail::omit_field<Writer>(metadata, value))
99 {
100 detail::WriteFieldOmitted(_output, GetTypeId(value), id, metadata);
101 return false;
102 }
103
104 WriteField(id, metadata, value);
105 return false;
106 }
107
108 template <typename T>
109 typename boost::disable_if_c<may_omit_fields<Writer>::value && !is_bond_type<T>::value, bool>::type
110 Field(uint16_t id, const Metadata& metadata, const T& value) const
111 {
112 BOOST_ASSERT(!detail::omit_field<Writer>(metadata, value));
113
114 WriteField(id, metadata, value);
115 return false;
116 }
117
118 template <typename T, typename Reader>
119 bool Field(uint16_t id, const Metadata& metadata, const value<T, Reader>& value) const
120 {
121 BOOST_ASSERT(!detail::omit_field<Writer>(metadata, value));
122
123 WriteField(id, metadata, value);
124 return false;
125 }
126
127 template <typename T, typename W = Writer>
128 typename boost::enable_if<may_omit_fields<W>, bool>::type
129 Field(uint16_t id, const Metadata& metadata, const maybe<T>& value) const
130 {
131 if (detail::omit_field<Writer>(metadata, value))
132 {
133 detail::WriteFieldOmitted(_output, get_type_id<T>::value, id, metadata);
134 return false;
135 }
136
137 WriteField(id, metadata, value.value());
138 return false;
139 }
140
141 template <typename T, typename W = Writer>
142 typename boost::disable_if<may_omit_fields<W>, bool>::type
143 Field(uint16_t id, const Metadata& metadata, const maybe<T>& value) const
144 {
145 BOOST_ASSERT(!detail::omit_field<Writer>(metadata, value));
146
147 WriteField(id, metadata, value.value());
148 return false;
149 }
150
151 // unknown field
152 template <typename T>
153 bool UnknownField(uint16_t id, const T& value) const
154 {
155 _output.WriteFieldBegin(GetTypeId(value), id);
156 Write(value);
157 _output.WriteFieldEnd();
158 return false;
159 }
160
161
162 // omitted field
163 bool OmittedField(uint16_t id, const Metadata& metadata, BondDataType type) const
164 {
165 detail::WriteFieldOmitted(_output, type, id, metadata);
166 return false;
167 }
168
169
170 template <typename T>
171 void Container(const T& element, uint32_t size) const
172 {
173 _output.WriteContainerBegin(size, GetTypeId(element));
174
175 while (size--)
176 Write(element);
177
178 _output.WriteContainerEnd();
179 }
180
181
182 template <typename Key, typename T>
183 void Container(const Key& key, const T& value, uint32_t size) const
184 {
185 _output.WriteContainerBegin(size, std::make_pair(GetTypeId(key), GetTypeId(value)));
186
187 while (size--)
188 {
189 Write(key);
190 Write(value);
191 }
192
193 _output.WriteContainerEnd();
194 }
195
196private:
197 // basic type field
198 template <typename T>
199 typename boost::enable_if_c<is_basic_type<T>::value && !is_type_alias<T>::value && true>::type
200 WriteField(uint16_t id, const Metadata& metadata, const T& value) const
201 {
202 _output.WriteField(id, metadata, value);
203 }
204
205 // struct or container field
206 template <typename T>
207 typename boost::disable_if_c<is_basic_type<T>::value && !is_type_alias<T>::value>::type
208 WriteField(uint16_t id, const Metadata& metadata, const T& value) const
209 {
210 _output.WriteFieldBegin(GetTypeId(value), id, metadata);
211 Write(value);
212 _output.WriteFieldEnd();
213 }
214
215 // basic type value
216 template <typename T>
217 typename boost::enable_if_c<is_basic_type<T>::value && !is_type_alias<T>::value && true>::type
218 Write(const T& value) const
219 {
220 _output.Write(value);
221 }
222
223 // type alias
224 template <typename T>
225 typename boost::enable_if<is_type_alias<T> >::type
226 Write(const T& value) const
227 {
228 Write(get_aliased_value(value));
229 }
230
231 // struct value or bonded<T>
232 template <typename T>
233 typename boost::enable_if<is_bond_type<T> >::type
234 Write(const T& value) const
235 {
236 Apply<Protocols>(Serializer(_output), value);
237 }
238
239 // bonded<T> and untagged writer
240 template <typename T>
241 typename boost::enable_if<uses_marshaled_bonded<typename Writer::Reader, T> >::type
242 Write(const bonded<T>& value) const
243 {
244 detail::MarshalToBlob<Protocols>(value, _output);
245 }
246
247 // bonded<void> and untagged writer
248 template <typename Reader>
249 typename boost::enable_if<uses_marshaled_bonded<typename Writer::Reader, Reader> >::type
250 Write(const bonded<void, Reader>& value) const
251 {
252 value.template Serialize<Protocols>(_output);
253 }
254
255 // 2-tuple
256 template <typename T1, typename T2>
257 void Write(const std::pair<T1, T2>& value) const
258 {
259 Write(value.first);
260 Write(value.second);
261 }
262
263 // container value
264 template <typename T>
265 typename boost::enable_if<is_container<T> >::type
266 Write(const T& value) const
267 {
268 _output.WriteContainerBegin(container_size(value), get_type_id<typename element_type<T>::type>::value);
269
270 for (const_enumerator<T> items(value); items.more();)
271 {
272 Write(items.next());
273 }
274
275 _output.WriteContainerEnd();
276 }
277
278
279 // blob
280 void Write(const blob& value) const
281 {
282 _output.WriteContainerBegin(value.length(), get_type_id<blob::value_type>::value);
283 _output.Write(value);
284 _output.WriteContainerEnd();
285 }
286
287
288 // serialized value
289 template <typename Reader, typename T>
290 typename boost::enable_if<is_basic_type<T> >::type
291 Write(const value<T, Reader>& value) const
292 {
293 T data = T();
294
295 value.template Deserialize<Protocols>(data);
296 Write(data);
297 }
298
299 template <typename Reader, typename T>
300 typename boost::disable_if<is_basic_type<T> >::type
301 Write(const value<T, Reader>& value) const
302 {
303 Apply<Protocols>(Serializer(_output), value);
304 }
305
306
307 template <typename T, typename WriterT, typename ProtocolsT>
308 friend class Merger;
309
310 template <typename T, typename Reader, typename Enable>
311 friend class value;
312
313 template <typename T, typename Schema, typename Transform>
314 friend class detail::_Parser;
315
316 template <typename ProtocolsT, typename Transform, typename T>
317 friend bool detail::DoublePassApply(const Transform&, const T&);
318
319protected:
320 Writer& _output;
321 const bool _base;
322};
323
324
325// SerializeTo
326template <typename Protocols, typename Writer>
327Serializer<Writer, Protocols> SerializeTo(Writer& output)
328{
329 return Serializer<Writer, Protocols>(output);
330}
331
332
333template <typename Writer, typename Protocols>
334class Marshaler
335 : protected Serializer<Writer, Protocols>
336{
337public:
338 typedef Writer writer_type;
339
340 Marshaler(Writer& output)
341 : Serializer<Writer, Protocols>(output)
342 {}
343
344 template <typename T>
345 bool Marshal(const T& value) const
346 {
347 this->_output.WriteVersion();
348 return Apply<Protocols>(static_cast<const Serializer<Writer, Protocols>&>(*this), value);
349 }
350};
351
352
353namespace detail
354{
355
356template <typename Protocols, typename Writer, typename T, typename Reader>
357bool inline
358ApplyTransform(const Marshaler<Writer, Protocols>& marshaler, const bonded<T, Reader>& bonded)
359{
360 return marshaler.Marshal(bonded);
361}
362
363
364template <typename Protocols, typename Writer, typename T>
365bool inline
366ApplyTransform(const Marshaler<Writer, Protocols>& marshaler, const T& value)
367{
368 return marshaler.Marshal(value);
369}
370
371} // namespace detail
372
373
374// MarshalTo
375template <typename Protocols, typename Writer>
376Marshaler<Writer, Protocols> MarshalTo(Writer& output)
377{
378 return Marshaler<Writer, Protocols>(output);
379}
380
381
382template <typename T>
383class RequiredFieldValiadator
384{
385protected:
386 void Begin() const
387 {
388 _required = next_required_field<typename schema<T>::type::fields>::value;
389 }
390
391 template <typename Head>
392 typename boost::enable_if<std::is_same<typename Head::field_modifier,
393 reflection::required_field_modifier> >::type
394 Validate() const
395 {
396 if (_required == Head::id)
397 _required = next_required_field<typename schema<T>::type::fields, Head::id + 1>::value;
398 else
399 MissingFieldException();
400 }
401
402
403 template <typename Schema>
404 typename boost::enable_if_c<next_required_field<typename Schema::fields>::value
405 != invalid_field_id>::type
406 Validate() const
407 {
408 if (_required != invalid_field_id)
409 MissingFieldException();
410 }
411
412
413 template <typename Head>
414 typename boost::disable_if<std::is_same<typename Head::field_modifier,
415 reflection::required_field_modifier> >::type
416 Validate() const
417 {}
418
419
420 template <typename Schema>
421 typename boost::disable_if_c<next_required_field<typename Schema::fields>::value
422 != invalid_field_id>::type
423 Validate() const
424 {}
425
426private:
427 [[noreturn]] void MissingFieldException() const;
428
429 mutable uint16_t _required;
430};
431
432template <typename T>
433void RequiredFieldValiadator<T>::MissingFieldException() const
434{
435 // Force instantiation of template statics
436 (void)typename schema<T>::type();
437
438 BOND_THROW(CoreException,
439 "De-serialization failed: required field " << _required <<
440 " is missing from " << schema<T>::type::metadata.qualified_name);
441}
442
443//
444// To<T> transforms the input field-by-field, matching both field ids and types,
445// into an instance of a static bond type T.
446//
447namespace detail
448{
449
450class To
451 : public DeserializingTransform
452{
453public:
454 void UnknownEnd() const
455 {}
456
457 template <typename X>
458 bool UnknownField(uint16_t /*id*/, const X& /*value*/) const
459 {
460 return false;
461 }
462
463protected:
464 template <typename Protocols, typename V, typename X>
465 void AssignToVar(V& var, const X& value) const
466 {
467 value.template Deserialize<Protocols>(var);
468 }
469
470 template <typename Protocols, typename V, typename X>
471 void AssignToVar(maybe<V>& var, const X& value) const
472 {
473 AssignToVar<Protocols>(var.set_value(), value);
474 }
475
476 template <typename X>
477 bool AssignToField(const boost::mpl::l_iter<boost::mpl::l_end>&, uint16_t /*id*/, const X& /*value*/) const
478 {
479 return false;
480 }
481};
482
483} // namespace detail
484
485
486template <typename T, typename Protocols, typename Validator>
487class To
488 : public detail::To,
489 protected Validator
490{
491public:
492 To(T& var)
493 : _var(var)
494 {}
495
496 void Begin(const Metadata& /*metadata*/) const
497 {
498 // Type T must be a Bond struct (i.e. struct generated by Bond codegen
499 // from a .bond file). If the assert fails for a Bond struct, the likely
500 // reason is that you didn't include the generated file *_reflection.h.
501 BOOST_STATIC_ASSERT(has_schema<T>::value);
502
503#ifndef BOND_UNIT_TEST_ONLY_PERMIT_OBJECT_REUSE
504 // Triggering this assert means you are reusing an object w/o resetting
505 // it to default value first.
506 //
507 // This should only be disabled for unit tests.
508 BOOST_ASSERT(detail::OptionalDefault<T>(_var));
509#endif
510
511 Validator::Begin();
512 }
513
514 void End() const
515 {
516 Validator::template Validate<typename schema<T>::type>();
517 }
518
519 template <typename X>
520 bool Base(const X& value) const
521 {
522 return AssignToBase(value);
523 }
524
525
526 // Separate Field overloads for bonded<T>, basic types and containers allows us to use
527 // simpler predicates in boost::mpl::copy_if. This doesn't matter for runtime code but
528 // compiles significantly faster.
529 template <typename Reader, typename X>
530 bool Field(uint16_t id, const Metadata& /*metadata*/, const bonded<X, Reader>& value) const
531 {
532 return AssignToField(typename boost::mpl::begin<typename nested_fields<T>::type>::type(), id, value);
533 }
534
535
536 template <typename Reader, typename X>
537 bool Field(uint16_t id, const Metadata& /*metadata*/, const value<X, Reader>& value) const
538 {
539 return AssignToField(typename boost::mpl::begin<typename matching_fields<T, X>::type>::type(), id, value);
540 }
541
542
543 template <typename Reader>
544 bool Field(uint16_t id, const Metadata& /*metadata*/, const value<void, Reader>& value) const
545 {
546 return AssignToField(typename boost::mpl::begin<typename container_fields<T>::type>::type(), id, value);
547 }
548
549
550 // Fast path for the common case when parser is using compile-time schema schema<T>::type
551 // and thus already knows schema type for each field.
552 typedef T FastPathType;
553
554 template <typename FieldT, typename X>
555 bool Field(const FieldT&, const X& value) const
556 {
557 Validator::template Validate<FieldT>();
558 AssignToVar<Protocols>(FieldT::GetVariable(_var), value);
559 return false;
560 }
561
562private:
563 using detail::To::AssignToVar;
564 using detail::To::AssignToField;
565
566 template <typename X, typename U = T>
567 typename boost::enable_if<has_base<U>, bool>::type
568 AssignToBase(const X& value) const
569 {
570 bool done = Apply<Protocols>(To<typename schema<T>::type::base, Protocols>(_var), value);
571
572 if (done)
573 {
574 UnexpectedStructStopException();
575 }
576
577 return false;
578 }
579
580 template <typename X, typename U = T>
581 typename boost::disable_if<has_base<U>, bool>::type
582 AssignToBase(const X& /*value*/) const
583 {
584 return false;
585 }
586
587 template <typename Fields, typename X>
588 bool AssignToField(const Fields&, uint16_t id, const X& value) const
589 {
590 typedef typename boost::mpl::deref<Fields>::type Head;
591
592 if (id == Head::id)
593 {
594 return Field(Head(), value);
595 }
596 else
597 {
598 return AssignToField(typename boost::mpl::next<Fields>::type(), id, value);
599 }
600 }
601
602 [[noreturn]] void UnexpectedStructStopException() const
603 {
604 // Force instantiation of template statics
605 (void)typename schema<T>::type();
606
607 BOND_THROW(CoreException,
608 "De-serialization failed: unexpected struct stop encountered for "
609 << schema<T>::type::metadata.qualified_name);
610 }
611
612 T& _var;
613};
614
615
616struct Mapping;
617
618typedef std::vector<uint16_t> Path;
619typedef std::map<uint16_t, Mapping> Mappings;
620
621struct Mapping
622{
623 Path path;
624 Mappings fields;
625};
626
627BOND_STATIC_CONSTEXPR uint16_t mapping_base = invalid_field_id;
628
629//
630// MapTo<T> maps the input fields onto an instance of a static bond type T,
631// using provided mappings from field path in the source to field path in
632// the type T. Field paths are expressed as a lists of field ids.
633//
634namespace detail
635{
636
637class MapTo
638 : public DeserializingTransform
639{
640public:
641 void Begin(const Metadata& /*metadata*/) const
642 {}
643
644 void End(bool = false) const
645 {}
646
647 void UnknownEnd() const
648 {}
649
650 template <typename T>
651 bool UnknownField(uint16_t, const T&) const
652 {
653 return false;
654 }
655
656protected:
657 struct PathView
658 : boost::noncopyable
659 {
660 PathView(const Path& path)
661 : path(path),
662 current(path.begin())
663 {}
664
665 PathView(const Path& path, Path::const_iterator current)
666 : path(path),
667 current(current)
668 {}
669
670 size_t size() const
671 {
672 return path.end() - current;
673 }
674
675 const Path& path;
676 const Path::const_iterator current;
677 };
678
679
680 template <typename Protocols, typename V, typename X>
681 bool Assign(V& var, const PathView& ids, const X& value) const
682 {
683 BOOST_ASSERT(ids.size() > 0);
684
685 if (*ids.current == mapping_base)
686 return AssignToBase<Protocols>(base_class<typename schema<V>::type>(), var, ids, value);
687
688 if (ids.size() == 1)
689 return AssignToField<Protocols>(var, *ids.current, value);
690 else
691 return AssignToNested<Protocols>(var, ids, value);
692 }
693
694
695 template <typename Protocols, typename V, typename X>
696 bool AssignToNested(V& var, const PathView& ids, const X& value) const
697 {
698 return AssignToNested<Protocols>(typename boost::mpl::begin<typename struct_fields<V>::type>::type(), var, ids, value);
699 }
700
701
702 template <typename Protocols, typename BaseT, typename V, typename X>
703 bool AssignToBase(const BaseT*, V& var, const PathView& ids, const X& value) const
704 {
705 return Assign<Protocols>(static_cast<BaseT&>(var), PathView(ids.path, ids.current + 1), value);
706 }
707
708
709 template <typename Protocols, typename V, typename X>
710 bool AssignToBase(const no_base*, V& /*var*/, const PathView& /*ids*/, const X& /*value*/) const
711 {
712 return false;
713 }
714
715
716 template <typename Protocols, typename Nested, typename V, typename X>
717 bool AssignToNested(const Nested&, V& var, const PathView& ids, const X& value) const
718 {
719 typedef typename boost::mpl::deref<Nested>::type Head;
720
721 if (*ids.current == Head::id)
722 return Assign<Protocols>(Head::GetVariable(var), PathView(ids.path, ids.current + 1), value);
723 else
724 return AssignToNested<Protocols>(typename boost::mpl::next<Nested>::type(), var, ids, value);
725 }
726
727 template <typename Protocols, typename V, typename X>
728 bool AssignToNested(const boost::mpl::l_iter<boost::mpl::l_end>&, V& /*var*/, const PathView& /*ids*/, const X& /*value*/) const
729 {
730 return false;
731 }
732
733
734 // Separate AssignToField overloads for bonded<T>, basic types and containers allows us
735 // to use simpler predicates in boost::mpl::copy_if. This doesn't matter for runtime code
736 // but compiles significantly faster.
737 template <typename Protocols, typename Reader, typename V, typename X>
738 bool AssignToField(V& var, uint16_t id, const bonded<X, Reader>& value) const
739 {
740 return AssignToField<Protocols>(typename boost::mpl::begin<typename nested_fields<V>::type>::type(), var, id, value);
741 }
742
743
744 template <typename Protocols, typename Reader, typename V, typename X>
745 bool AssignToField(V& var, uint16_t id, const value<X, Reader>& value) const
746 {
747 return AssignToField<Protocols>(typename boost::mpl::begin<typename matching_fields<V, X>::type>::type(), var, id, value);
748 }
749
750
751 template <typename Protocols, typename Reader, typename V>
752 bool AssignToField(V& var, uint16_t id, const value<void, Reader>& value) const
753 {
754 return AssignToField<Protocols>(typename boost::mpl::begin<typename container_fields<V>::type>::type(), var, id, value);
755 }
756
757
758 template <typename Protocols, typename Fields, typename V, typename X>
759 bool AssignToField(const Fields&, V& var, uint16_t id, const X& value) const
760 {
761 typedef typename boost::mpl::deref<Fields>::type Head;
762
763 if (id == Head::id)
764 {
765 AssignToVar<Protocols>(Head::GetVariable(var), value);
766 return false;
767 }
768 else
769 {
770 return AssignToField<Protocols>(typename boost::mpl::next<Fields>::type(), var, id, value);
771 }
772 }
773
774
775 template <typename Protocols, typename V, typename X>
776 bool AssignToField(const boost::mpl::l_iter<boost::mpl::l_end>&, V& /*var*/, uint16_t /*id*/, const X& /*value*/) const
777 {
778 return false;
779 }
780
781
782 template <typename Protocols, typename V, typename X>
783 void AssignToVar(V& var, const X& value) const
784 {
785 value.template Deserialize<Protocols>(var);
786 }
787
788
789 template <typename Protocols, typename V, typename X>
790 void AssignToVar(maybe<V>& var, const X& value) const
791 {
792 AssignToVar<Protocols>(var.set_value(), value);
793 }
794};
795
796} // namespace detail
797
798
799template <typename T, typename Protocols = BuiltInProtocols>
800class MapTo
801 : public detail::MapTo
802{
803public:
804 BOOST_STATIC_ASSERT(has_schema<T>::value);
805
806 MapTo(T& var, const Mappings& mappings)
807 : _var(var),
808 _mappings(mappings)
809 {}
810
811
812 template <typename X>
813 bool Base(const X& value) const
814 {
815 Mappings::const_iterator it = _mappings.find(mapping_base);
816
817 if (it != _mappings.end())
818 return Apply<Protocols>(MapTo(_var, it->second.fields), value);
819 else
820 return false;
821 }
822
823 template <typename Reader, typename X>
824 bool Field(uint16_t id, const Metadata& /*metadata*/, const bonded<X, Reader>& value) const
825 {
826 BOOST_ASSERT(id != mapping_base);
827
828 Mappings::const_iterator it = _mappings.find(id);
829
830 if (it != _mappings.end())
831 {
832 if (!it->second.fields.empty())
833 return Apply<Protocols>(MapTo(_var, it->second.fields), value);
834
835 if (!it->second.path.empty())
836 return Assign<Protocols>(_var, it->second.path, value);
837 }
838
839 return false;
840 }
841
842 template <typename X>
843 bool Field(uint16_t id, const Metadata& /*metadata*/, const X& value) const
844 {
845 BOOST_ASSERT(id != mapping_base);
846
847 Mappings::const_iterator it = _mappings.find(id);
848
849 if (it != _mappings.end() && !it->second.path.empty())
850 return Assign<Protocols>(_var, it->second.path, value);
851 else
852 return false;
853 }
854
855private:
856 using detail::MapTo::Assign;
857
858 T& _var;
859 const Mappings& _mappings;
860};
861
862} // namespace bond
namespace bond
Definition: apply.h:17
bool Validate(const RuntimeSchema &src, const RuntimeSchema &dst)
Validate compatibility of schemas.
Definition: validate.h:24
void Marshal(const T &obj, Writer &output)
Marshal an object using a protocol writer.
Definition: bond.h:63