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
24inline RuntimeSchema::RuntimeSchema(const RuntimeSchema& schema)
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
170
171template <typename Unused> struct
172uses_marshaled_bonded<SchemaReader&, Unused> : std::false_type
173{};
174
175
176//
177// InitSchemaDef transform creates an instance of runtime schema for the input
178//
179class InitSchemaDef
180 : public SerializingTransform
181{
182public:
183 InitSchemaDef(SchemaDef& schema)
184 : _schema(schema),
185 _struct_def(schema.structs.size())
186 {
187 _schema.structs.push_back(StructDef());
188 }
189
190
191 void Begin(const Metadata& metadata) const
192 {
193 This().metadata = metadata;
194 }
195
196 void End() const
197 {
198 }
199
200 template <typename T>
201 bool Base(const T& /*value*/) const
202 {
203 TypeDef type = GetTypeDef<typename remove_bonded<T>::type>();
204 This().base_def.set(type);
205 return false;
206 }
207
208 // field
209 template <typename T>
210 bool Field(uint16_t id, const Metadata& metadata, const T&) const
211 {
212 FieldDef field;
213
214 field.id = id;
215 field.metadata = metadata;
216 field.type = GetTypeDef<typename remove_bonded_value<T>::type>();
217
218 This().fields.push_back(field);
219 return false;
220 }
221
222private:
223 template <typename T>
224 typename boost::enable_if<is_basic_type<T>, TypeDef>::type
225 GetTypeDef() const
226 {
227 TypeDef type;
228
229 type.id = get_type_id<T>::value;
230
231 return type;
232 }
233
234
235 template <typename T>
236 typename boost::enable_if_c<is_container<T>::value && !is_map_container<T>::value, TypeDef>::type
237 GetTypeDef() const
238 {
239 TypeDef type;
240
241 type.id = get_type_id<T>::value;
242 type.element.set() = GetTypeDef<typename element_type<T>::type>();
243
244 return type;
245 }
246
247
248 template <typename T>
249 typename boost::enable_if<is_map_container<T>, TypeDef>::type
250 GetTypeDef() const
251 {
252 TypeDef type;
253
254 type.id = get_type_id<T>::value;
255 type.key.set() = GetTypeDef<typename element_type<T>::type::first_type>();
256 type.element.set() = GetTypeDef<typename element_type<T>::type::second_type>();
257
258 return type;
259 }
260
261
262 template <typename T>
263 typename boost::enable_if<is_bond_type<T>, TypeDef>::type
264 GetTypeDef() const
265 {
266 TypeDef type;
267
268 type.id = get_type_id<T>::value;
269 type.struct_def = GetStructDef<typename remove_bonded<T>::type>();
270 type.bonded_type = is_bonded<T>::value;
271
272 return type;
273 }
274
275
276 template <typename T>
277 uint16_t GetStructDef() const
278 {
279 const auto& structs = _schema.structs;
280
281 BOOST_ASSERT(structs.size() <= (std::numeric_limits<uint16_t>::max)());
282
283 auto it = std::find_if(
284 std::begin(structs),
285 std::end(structs),
286 [](const StructDef& def)
287 {
288 return def.metadata.qualified_name == schema<T>::type::metadata.qualified_name;
289 });
290
291 const auto index = static_cast<uint16_t>(std::distance(std::begin(structs), it));
292
293 if (it == std::end(structs))
294 {
295 detail::SchemaCache<T>::AppendStructDef(&_schema);
296 }
297
298 return index;
299 }
300
301
302 // Note that This() returns a reference to StructDef in the structs vector
303 // which may be invalidated when the vector grows. In particular This()
304 // can't be used in an expression that may result in adding items to the
305 // vector.
306 StructDef& This() const
307 {
308 BOOST_ASSERT(_schema.structs.size() > _struct_def);
309 return _schema.structs[_struct_def];
310 }
311
312
313 SchemaDef& _schema;
314 const size_t _struct_def;
315};
316
317namespace detail
318{
319
320template <typename T, typename Unused>
321void SchemaCache<T, Unused>::AppendStructDef(SchemaDef* s)
322{
323 Apply<T>(InitSchemaDef{ *s });
324}
325
326} // detail
327
329template <typename T>
331{
332 return RuntimeSchema(detail::SchemaCache<T>::Get());
333}
334
335template <typename T>
336inline RuntimeSchema GetRuntimeSchema(const T&)
337{
338 return RuntimeSchema(detail::SchemaCache<T>::Get());
339}
340
341
342inline RuntimeSchema element_schema(const RuntimeSchema& schema)
343{
344 if (!schema.GetType().element.empty())
345 return RuntimeSchema(schema, schema.GetType().element.value());
346 else
347 return GetRuntimeSchema<Unknown>();
348}
349
350
351inline RuntimeSchema key_schema(const RuntimeSchema& schema)
352{
353 if (!schema.GetType().key.empty())
354 return RuntimeSchema(schema, schema.GetType().key.value());
355 else
356 return GetRuntimeSchema<Unknown>();
357}
358
359
361template <typename T, typename Map = std::map<T, std::string> >
362inline const Map& GetEnumValues()
363{
364 return GetValueToNameMap(T{}, detail::mpl::identity<Map>{});
365}
366
367
369template <typename T, typename Map = std::map<std::string, T> >
370inline const Map& GetEnumNames()
371{
372 return GetNameToValueMap(T{}, detail::mpl::identity<Map>{});
373}
374
375
376} // 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:370
RuntimeSchema GetRuntimeSchema()
Returns an instance of RuntimeSchema for a user defined struct.
Definition: schema.h:330
void Serialize(const T &obj, Writer &output)
Serialize an object using a protocol writer.
Definition: bond.h:19
void Marshal(const T &obj, Writer &output)
Marshal an object using a protocol writer.
Definition: bond.h:63
const Map & GetEnumValues()
Returns a const reference to a map of values for a user defined enum.
Definition: schema.h:362