Bond
 
Loading...
Searching...
No Matches
schema.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 "detail/once.h"
9#include "detail/tags.h"
10#include "reflection.h"
11#include "runtime_schema.h"
12
13#include <boost/bind/bind.hpp>
14#include <boost/make_shared.hpp>
15
16#include <limits>
17#include <algorithm>
18#include <iterator>
19
20namespace bond
21{
22
23
25 : schema(schema.schema),
26 type(schema.type),
27 instance(schema.instance)
28{}
29
30inline RuntimeSchema::RuntimeSchema(const SchemaDef& schema)
31 : schema(&schema),
32 type(&schema.root)
33{}
34
35inline RuntimeSchema::RuntimeSchema(const boost::shared_ptr<SchemaDef>& schema)
36 : schema(schema.get()),
37 type(&schema->root),
38 instance(schema)
39{}
40
41inline RuntimeSchema::RuntimeSchema(const RuntimeSchema& schema, const TypeDef& type)
42 : schema(schema.schema),
43 type(&type),
44 instance(schema.instance)
45{}
46
47inline RuntimeSchema::RuntimeSchema(const RuntimeSchema& schema, const FieldDef& field)
48 : schema(schema.schema),
49 type(&field.type),
50 instance(schema.instance)
51{}
52
53inline bool RuntimeSchema::HasBase() const
54{
55 return !GetStruct().base_def.empty();
56}
57
58inline RuntimeSchema RuntimeSchema::GetBaseSchema() const
59{
60 return RuntimeSchema(*this, GetStruct().base_def.value());
61}
62
63inline const StructDef& RuntimeSchema::GetStruct() const
64{
65 BOOST_ASSERT(type->id == BT_STRUCT);
66 return schema->structs[type->struct_def];
67}
68
69inline BondDataType RuntimeSchema::GetTypeId() const
70{
71 return type->id;
72}
73
74
75template <typename Protocols = BuiltInProtocols, typename Writer>
76inline void Serialize(const RuntimeSchema& schema, Writer& output)
77{
78 Apply<Protocols>(SerializeTo<Protocols>(output), schema.GetSchema());
79}
80
81
82template <typename Protocols = BuiltInProtocols, typename Writer>
83inline void Marshal(const RuntimeSchema& schema, Writer& output)
84{
85 Apply<Protocols>(MarshalTo<Protocols>(output), schema.GetSchema());
86}
87
88class InitSchemaDef;
89
90namespace detail
91{
92 inline uint16_t schema_depth(const RuntimeSchema& schema)
93 {
94 uint16_t depth = 1;
95
96 if (schema.HasBase())
97 depth += schema_depth(schema.GetBaseSchema());
98
99 return depth;
100 }
101
102 template <typename T, typename Unused = void>
103 class SchemaCache
104 {
105 public:
106 static const SchemaDef& Get()
107 {
108 // The schema object can't be initialized as a global static
109 // because InitSchemaDef uses schema's and fields' metadata which
110 // are global static variables, and we can't depends on them being
111 // initialized before the schema. Instead we initialize the schema
112 // on the first call to Get().
113 // Note that older versions of GNU C++ don't handle rvalue argument
114 // forwarding in Boost call_once implementation so we are using
115 // the old trusty boost::bind.
116 call_once(flag, boost::bind(&AppendStructDef, &schema));
117 return schema;
118 }
119
120 static void AppendStructDef(SchemaDef* s);
121
122 private:
123 static SchemaDef schema;
124 static once_flag flag;
125 };
126
127 // We need the Unused template parameter because otherwise the 'schema'
128 // static would not be a static member of a class template and would have
129 // to be defined in a .cpp and we want Bond to be header-only for some
130 // scenarios.
131 template <typename Unused>
132 class SchemaCache<Unknown, Unused>
133 {
134 public:
135 static const SchemaDef& Get()
136 {
137 return schema;
138 }
139
140 static SchemaDef NewSchemaDef()
141 {
142 // SchemaDef for unknown types: struct with no fields
143 SchemaDef s;
144 s.root.id = BT_STRUCT;
145 s.structs.resize(1);
146 return s;
147 }
148
149 private:
150 static const SchemaDef schema;
151 };
152
153 template <typename T, typename Unused>
154 SchemaDef SchemaCache<T, Unused>::schema;
155
156 template <typename Unused>
157 const SchemaDef SchemaCache<Unknown, Unused>::schema
158 = SchemaCache<Unknown>::NewSchemaDef();
159
160 template <typename T, typename Unused>
161 once_flag SchemaCache<T, Unused>::flag;
162}
163
164
165struct SchemaReader
166{
167 using Parser = StaticParser<SchemaReader&>;
168
169 template<typename T>
170 bool CanReadArray(uint32_t /*num_elems*/) const
171 {
172 return true;
173 }
174};
175
176
177template <typename Unused> struct
178uses_marshaled_bonded<SchemaReader&, Unused> : std::false_type
179{};
180
181
182//
183// InitSchemaDef transform creates an instance of runtime schema for the input
184//
185class InitSchemaDef
186 : public SerializingTransform
187{
188public:
189 InitSchemaDef(SchemaDef& schema)
190 : _schema(schema),
191 _struct_def(schema.structs.size())
192 {
193 _schema.structs.push_back(StructDef());
194 }
195
196
197 void Begin(const Metadata& metadata) const
198 {
199 This().metadata = metadata;
200 }
201
202 void End() const
203 {
204 }
205
206 template <typename T>
207 bool Base(const T& /*value*/) const
208 {
209 TypeDef type = GetTypeDef<typename remove_bonded<T>::type>();
210 This().base_def.set(type);
211 return false;
212 }
213
214 // field
215 template <typename T>
216 bool Field(uint16_t id, const Metadata& metadata, const T&) const
217 {
218 FieldDef field;
219
220 field.id = id;
221 field.metadata = metadata;
222 field.type = GetTypeDef<typename remove_bonded_value<T>::type>();
223
224 This().fields.push_back(field);
225 return false;
226 }
227
228private:
229 template <typename T>
230 typename boost::enable_if<is_basic_type<T>, TypeDef>::type
231 GetTypeDef() const
232 {
233 TypeDef type;
234
235 type.id = get_type_id<T>::value;
236
237 return type;
238 }
239
240
241 template <typename T>
242 typename boost::enable_if_c<is_container<T>::value && !is_map_container<T>::value, TypeDef>::type
243 GetTypeDef() const
244 {
245 TypeDef type;
246
247 type.id = get_type_id<T>::value;
248 type.element.set() = GetTypeDef<typename element_type<T>::type>();
249
250 return type;
251 }
252
253
254 template <typename T>
255 typename boost::enable_if<is_map_container<T>, TypeDef>::type
256 GetTypeDef() const
257 {
258 TypeDef type;
259
260 type.id = get_type_id<T>::value;
261 type.key.set() = GetTypeDef<typename element_type<T>::type::first_type>();
262 type.element.set() = GetTypeDef<typename element_type<T>::type::second_type>();
263
264 return type;
265 }
266
267
268 template <typename T>
269 typename boost::enable_if<is_bond_type<T>, TypeDef>::type
270 GetTypeDef() const
271 {
272 TypeDef type;
273
274 type.id = get_type_id<T>::value;
275 type.struct_def = GetStructDef<typename remove_bonded<T>::type>();
276 type.bonded_type = is_bonded<T>::value;
277
278 return type;
279 }
280
281
282 template <typename T>
283 uint16_t GetStructDef() const
284 {
285 const auto& structs = _schema.structs;
286
287 BOOST_ASSERT(structs.size() <= (std::numeric_limits<uint16_t>::max)());
288
289 auto it = std::find_if(
290 std::begin(structs),
291 std::end(structs),
292 [](const StructDef& def)
293 {
294 return def.metadata.qualified_name == schema<T>::type::metadata.qualified_name;
295 });
296
297 const auto index = static_cast<uint16_t>(std::distance(std::begin(structs), it));
298
299 if (it == std::end(structs))
300 {
301 detail::SchemaCache<T>::AppendStructDef(&_schema);
302 }
303
304 return index;
305 }
306
307
308 // Note that This() returns a reference to StructDef in the structs vector
309 // which may be invalidated when the vector grows. In particular This()
310 // can't be used in an expression that may result in adding items to the
311 // vector.
312 StructDef& This() const
313 {
314 BOOST_ASSERT(_schema.structs.size() > _struct_def);
315 return _schema.structs[_struct_def];
316 }
317
318
319 SchemaDef& _schema;
320 const size_t _struct_def;
321};
322
323namespace detail
324{
325
326template <typename T, typename Unused>
327void SchemaCache<T, Unused>::AppendStructDef(SchemaDef* s)
328{
329 Apply<T>(InitSchemaDef{ *s });
330}
331
332} // detail
333
335template <typename T>
337{
338 return RuntimeSchema(detail::SchemaCache<T>::Get());
339}
340
341template <typename T>
342inline RuntimeSchema GetRuntimeSchema(const T&)
343{
344 return RuntimeSchema(detail::SchemaCache<T>::Get());
345}
346
347
348inline RuntimeSchema element_schema(const RuntimeSchema& schema)
349{
350 if (!schema.GetType().element.empty())
351 return RuntimeSchema(schema, schema.GetType().element.value());
352 else
354}
355
356
357inline RuntimeSchema key_schema(const RuntimeSchema& schema)
358{
359 if (!schema.GetType().key.empty())
360 return RuntimeSchema(schema, schema.GetType().key.value());
361 else
363}
364
365
367template <typename T, typename Map = std::map<T, std::string> >
368inline const Map& GetEnumValues()
369{
370 return GetValueToNameMap(T{}, detail::mpl::identity<Map>{});
371}
372
373
375template <typename T, typename Map = std::map<std::string, T> >
376inline const Map& GetEnumNames()
377{
378 return GetNameToValueMap(T{}, detail::mpl::identity<Map>{});
379}
380
381
382} // namespace bond
Represents runtime schema See User's Manual
Definition runtime_schema.h:26
RuntimeSchema()
Default constructor.
Definition runtime_schema.h:29
namespace bond
Definition apply.h:17
const Map & GetEnumNames()
Returns a const reference to a map of names for a user defined enum.
Definition schema.h:376
RuntimeSchema GetRuntimeSchema()
Returns an instance of RuntimeSchema for a user defined struct.
Definition schema.h:336
void Serialize(const T &obj, Writer &output)
Serialize an object using a protocol writer.
Definition bond.h:20
void Marshal(const T &obj, Writer &output)
Marshal an object using a protocol writer.
Definition bond.h:64
const Map & GetEnumValues()
Returns a const reference to a map of values for a user defined enum.
Definition schema.h:368
Base class for serializing transforms.
Definition tags.h:61