Coverage Report

Created: 2025-11-15 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/flatbuffers/include/flatbuffers/reflection.h
Line
Count
Source
1
/*
2
 * Copyright 2015 Google Inc. All rights reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#ifndef FLATBUFFERS_REFLECTION_H_
18
#define FLATBUFFERS_REFLECTION_H_
19
20
// This is somewhat of a circular dependency because flatc (and thus this
21
// file) is needed to generate this header in the first place.
22
// Should normally not be a problem since it can be generated by the
23
// previous version of flatc whenever this code needs to change.
24
// See scripts/generate_code.py for generation.
25
#include "flatbuffers/reflection_generated.h"
26
27
// Helper functionality for reflection.
28
29
namespace flatbuffers {
30
31
// ------------------------- GETTERS -------------------------
32
33
0
inline bool IsScalar(reflection::BaseType t) {
34
0
  return t >= reflection::UType && t <= reflection::Double;
35
0
}
36
0
inline bool IsInteger(reflection::BaseType t) {
37
0
  return t >= reflection::UType && t <= reflection::ULong;
38
0
}
39
0
inline bool IsFloat(reflection::BaseType t) {
40
0
  return t == reflection::Float || t == reflection::Double;
41
0
}
42
0
inline bool IsLong(reflection::BaseType t) {
43
0
  return t == reflection::Long || t == reflection::ULong;
44
0
}
45
46
// Size of a basic type, don't use with structs.
47
0
inline size_t GetTypeSize(reflection::BaseType base_type) {
48
0
  // This needs to correspond to the BaseType enum.
49
0
  static size_t sizes[] = {
50
0
      0,  // None
51
0
      1,  // UType
52
0
      1,  // Bool
53
0
      1,  // Byte
54
0
      1,  // UByte
55
0
      2,  // Short
56
0
      2,  // UShort
57
0
      4,  // Int
58
0
      4,  // UInt
59
0
      8,  // Long
60
0
      8,  // ULong
61
0
      4,  // Float
62
0
      8,  // Double
63
0
      4,  // String
64
0
      4,  // Vector
65
0
      4,  // Obj
66
0
      4,  // Union
67
0
      0,  // Array. Only used in structs. 0 was chosen to prevent out-of-bounds
68
0
          // errors.
69
0
      8,  // Vector64
70
0
71
0
      0  // MaxBaseType. This must be kept the last entry in this array.
72
0
  };
73
0
  static_assert(sizeof(sizes) / sizeof(size_t) == reflection::MaxBaseType + 1,
74
0
                "Size of sizes[] array does not match the count of BaseType "
75
0
                "enum values.");
76
0
  return sizes[base_type];
77
0
}
78
79
// Same as above, but now correctly returns the size of a struct if
80
// the field (or vector element) is a struct.
81
inline size_t GetTypeSizeInline(reflection::BaseType base_type, int type_index,
82
0
                                const reflection::Schema& schema) {
83
0
  if (base_type == reflection::Obj &&
84
0
      schema.objects()->Get(type_index)->is_struct()) {
85
0
    return schema.objects()->Get(type_index)->bytesize();
86
0
  } else {
87
0
    return GetTypeSize(base_type);
88
0
  }
89
0
}
90
91
// Get the root, regardless of what type it is.
92
0
inline Table* GetAnyRoot(uint8_t* const flatbuf) {
93
0
  return GetMutableRoot<Table>(flatbuf);
94
0
}
95
96
0
inline const Table* GetAnyRoot(const uint8_t* const flatbuf) {
97
0
  return GetRoot<Table>(flatbuf);
98
0
}
99
100
0
inline Table* GetAnySizePrefixedRoot(uint8_t* const flatbuf) {
101
0
  return GetMutableSizePrefixedRoot<Table>(flatbuf);
102
0
}
103
104
0
inline const Table* GetAnySizePrefixedRoot(const uint8_t* const flatbuf) {
105
0
  return GetSizePrefixedRoot<Table>(flatbuf);
106
0
}
107
108
// Get a field's default, if you know it's an integer, and its exact type.
109
template <typename T>
110
0
T GetFieldDefaultI(const reflection::Field& field) {
111
0
  FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
112
0
  return static_cast<T>(field.default_integer());
113
0
}
114
115
// Get a field's default, if you know it's floating point and its exact type.
116
template <typename T>
117
0
T GetFieldDefaultF(const reflection::Field& field) {
118
0
  FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
119
0
  return static_cast<T>(field.default_real());
120
0
}
121
122
// Get a field, if you know it's an integer, and its exact type.
123
template <typename T>
124
0
T GetFieldI(const Table& table, const reflection::Field& field) {
125
0
  FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
126
0
  return table.GetField<T>(field.offset(),
127
0
                           static_cast<T>(field.default_integer()));
128
0
}
129
130
// Get a field, if you know it's floating point and its exact type.
131
template <typename T>
132
T GetFieldF(const Table& table, const reflection::Field& field) {
133
  FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
134
  return table.GetField<T>(field.offset(),
135
                           static_cast<T>(field.default_real()));
136
}
137
138
// Get a field, if you know it's a string.
139
inline const String* GetFieldS(const Table& table,
140
0
                               const reflection::Field& field) {
141
0
  FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::String);
142
0
  return table.GetPointer<const String*>(field.offset());
143
0
}
144
145
// Get a field, if you know it's a vector.
146
template <typename T>
147
Vector<T>* GetFieldV(const Table& table, const reflection::Field& field) {
148
  FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Vector &&
149
                     sizeof(T) == GetTypeSize(field.type()->element()));
150
  return table.GetPointer<Vector<T>*>(field.offset());
151
}
152
153
// Get a field, if you know it's a vector, generically.
154
// To actually access elements, use the return value together with
155
// field.type()->element() in any of GetAnyVectorElemI below etc.
156
inline VectorOfAny* GetFieldAnyV(const Table& table,
157
0
                                 const reflection::Field& field) {
158
0
  return table.GetPointer<VectorOfAny*>(field.offset());
159
0
}
160
161
// Get a field, if you know it's a table.
162
0
inline Table* GetFieldT(const Table& table, const reflection::Field& field) {
163
0
  FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj ||
164
0
                     field.type()->base_type() == reflection::Union);
165
0
  return table.GetPointer<Table*>(field.offset());
166
0
}
167
168
// Get a field, if you know it's a struct.
169
inline const Struct* GetFieldStruct(const Table& table,
170
0
                                    const reflection::Field& field) {
171
0
  // TODO: This does NOT check if the field is a table or struct, but we'd need
172
0
  // access to the schema to check the is_struct flag.
173
0
  FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj);
174
0
  return table.GetStruct<const Struct*>(field.offset());
175
0
}
176
177
// Get a structure's field, if you know it's a struct.
178
inline const Struct* GetFieldStruct(const Struct& structure,
179
0
                                    const reflection::Field& field) {
180
0
  FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj);
181
0
  return structure.GetStruct<const Struct*>(field.offset());
182
0
}
183
184
// Raw helper functions used below: get any value in memory as a 64bit int, a
185
// double or a string.
186
// All scalars get static_cast to an int64_t, strings use strtoull, every other
187
// data type returns 0.
188
int64_t GetAnyValueI(reflection::BaseType type, const uint8_t* data);
189
// All scalars static cast to double, strings use strtod, every other data
190
// type is 0.0.
191
double GetAnyValueF(reflection::BaseType type, const uint8_t* data);
192
// All scalars converted using stringstream, strings as-is, and all other
193
// data types provide some level of debug-pretty-printing.
194
std::string GetAnyValueS(reflection::BaseType type, const uint8_t* data,
195
                         const reflection::Schema* schema, int type_index);
196
197
// Get any table field as a 64bit int, regardless of what type it is.
198
inline int64_t GetAnyFieldI(const Table& table,
199
0
                            const reflection::Field& field) {
200
0
  auto field_ptr = table.GetAddressOf(field.offset());
201
0
  return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr)
202
0
                   : field.default_integer();
203
0
}
204
205
// Get any table field as a double, regardless of what type it is.
206
0
inline double GetAnyFieldF(const Table& table, const reflection::Field& field) {
207
0
  auto field_ptr = table.GetAddressOf(field.offset());
208
0
  return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr)
209
0
                   : field.default_real();
210
0
}
211
212
// Get any table field as a string, regardless of what type it is.
213
// You may pass nullptr for the schema if you don't care to have fields that
214
// are of table type pretty-printed.
215
inline std::string GetAnyFieldS(const Table& table,
216
                                const reflection::Field& field,
217
0
                                const reflection::Schema* schema) {
218
0
  auto field_ptr = table.GetAddressOf(field.offset());
219
0
  return field_ptr ? GetAnyValueS(field.type()->base_type(), field_ptr, schema,
220
0
                                  field.type()->index())
221
0
                   : "";
222
0
}
223
224
// Get any struct field as a 64bit int, regardless of what type it is.
225
0
inline int64_t GetAnyFieldI(const Struct& st, const reflection::Field& field) {
226
0
  return GetAnyValueI(field.type()->base_type(),
227
0
                      st.GetAddressOf(field.offset()));
228
0
}
229
230
// Get any struct field as a double, regardless of what type it is.
231
0
inline double GetAnyFieldF(const Struct& st, const reflection::Field& field) {
232
0
  return GetAnyValueF(field.type()->base_type(),
233
0
                      st.GetAddressOf(field.offset()));
234
0
}
235
236
// Get any struct field as a string, regardless of what type it is.
237
inline std::string GetAnyFieldS(const Struct& st,
238
0
                                const reflection::Field& field) {
239
0
  return GetAnyValueS(field.type()->base_type(),
240
0
                      st.GetAddressOf(field.offset()), nullptr, -1);
241
0
}
242
243
// Get any vector element as a 64bit int, regardless of what type it is.
244
inline int64_t GetAnyVectorElemI(const VectorOfAny* vec,
245
0
                                 reflection::BaseType elem_type, size_t i) {
246
0
  return GetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i);
247
0
}
248
249
// Get any vector element as a double, regardless of what type it is.
250
inline double GetAnyVectorElemF(const VectorOfAny* vec,
251
0
                                reflection::BaseType elem_type, size_t i) {
252
0
  return GetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i);
253
0
}
254
255
// Get any vector element as a string, regardless of what type it is.
256
inline std::string GetAnyVectorElemS(const VectorOfAny* vec,
257
0
                                     reflection::BaseType elem_type, size_t i) {
258
0
  return GetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i,
259
0
                      nullptr, -1);
260
0
}
261
262
// Get a vector element that's a table/string/vector from a generic vector.
263
// Pass Table/String/VectorOfAny as template parameter.
264
// Warning: does no typechecking.
265
template <typename T>
266
T* GetAnyVectorElemPointer(const VectorOfAny* vec, size_t i) {
267
  auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i;
268
  return reinterpret_cast<T*>(elem_ptr + ReadScalar<uoffset_t>(elem_ptr));
269
}
270
271
// Get the inline-address of a vector element. Useful for Structs (pass Struct
272
// as template arg), or being able to address a range of scalars in-line.
273
// Get elem_size from GetTypeSizeInline().
274
// Note: little-endian data on all platforms, use EndianScalar() instead of
275
// raw pointer access with scalars).
276
template <typename T>
277
T* GetAnyVectorElemAddressOf(const VectorOfAny* vec, size_t i,
278
                             size_t elem_size) {
279
  return reinterpret_cast<T*>(vec->Data() + elem_size * i);
280
}
281
282
// Similarly, for elements of tables.
283
template <typename T>
284
T* GetAnyFieldAddressOf(const Table& table, const reflection::Field& field) {
285
  return reinterpret_cast<T*>(table.GetAddressOf(field.offset()));
286
}
287
288
// Similarly, for elements of structs.
289
template <typename T>
290
T* GetAnyFieldAddressOf(const Struct& st, const reflection::Field& field) {
291
  return reinterpret_cast<T*>(st.GetAddressOf(field.offset()));
292
}
293
294
// Loop over all the fields of the provided `object` and call `func` on each one
295
// in increasing order by their field->id(). If `reverse` is true, `func` is
296
// called in descending order
297
void ForAllFields(const reflection::Object* object, bool reverse,
298
                  std::function<void(const reflection::Field*)> func);
299
300
// ------------------------- SETTERS -------------------------
301
302
// Set any scalar field, if you know its exact type.
303
template <typename T>
304
bool SetField(Table* table, const reflection::Field& field, T val) {
305
  reflection::BaseType type = field.type()->base_type();
306
  if (!IsScalar(type)) {
307
    return false;
308
  }
309
  FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(type));
310
  T def;
311
  if (IsInteger(type)) {
312
    def = GetFieldDefaultI<T>(field);
313
  } else {
314
    FLATBUFFERS_ASSERT(IsFloat(type));
315
    def = GetFieldDefaultF<T>(field);
316
  }
317
  return table->SetField(field.offset(), val, def);
318
}
319
320
// Raw helper functions used below: set any value in memory as a 64bit int, a
321
// double or a string.
322
// These work for all scalar values, but do nothing for other data types.
323
// To set a string, see SetString below.
324
void SetAnyValueI(reflection::BaseType type, uint8_t* data, int64_t val);
325
void SetAnyValueF(reflection::BaseType type, uint8_t* data, double val);
326
void SetAnyValueS(reflection::BaseType type, uint8_t* data, const char* val);
327
328
// Set any table field as a 64bit int, regardless of type what it is.
329
inline bool SetAnyFieldI(Table* table, const reflection::Field& field,
330
0
                         int64_t val) {
331
0
  auto field_ptr = table->GetAddressOf(field.offset());
332
0
  if (!field_ptr) return val == GetFieldDefaultI<int64_t>(field);
333
0
  SetAnyValueI(field.type()->base_type(), field_ptr, val);
334
0
  return true;
335
0
}
336
337
// Set any table field as a double, regardless of what type it is.
338
inline bool SetAnyFieldF(Table* table, const reflection::Field& field,
339
0
                         double val) {
340
0
  auto field_ptr = table->GetAddressOf(field.offset());
341
0
  if (!field_ptr) return val == GetFieldDefaultF<double>(field);
342
0
  SetAnyValueF(field.type()->base_type(), field_ptr, val);
343
0
  return true;
344
0
}
345
346
// Set any table field as a string, regardless of what type it is.
347
inline bool SetAnyFieldS(Table* table, const reflection::Field& field,
348
0
                         const char* val) {
349
0
  auto field_ptr = table->GetAddressOf(field.offset());
350
0
  if (!field_ptr) return false;
351
0
  SetAnyValueS(field.type()->base_type(), field_ptr, val);
352
0
  return true;
353
0
}
354
355
// Set any struct field as a 64bit int, regardless of type what it is.
356
inline void SetAnyFieldI(Struct* st, const reflection::Field& field,
357
0
                         int64_t val) {
358
0
  SetAnyValueI(field.type()->base_type(), st->GetAddressOf(field.offset()),
359
0
               val);
360
0
}
361
362
// Set any struct field as a double, regardless of type what it is.
363
inline void SetAnyFieldF(Struct* st, const reflection::Field& field,
364
0
                         double val) {
365
0
  SetAnyValueF(field.type()->base_type(), st->GetAddressOf(field.offset()),
366
0
               val);
367
0
}
368
369
// Set any struct field as a string, regardless of type what it is.
370
inline void SetAnyFieldS(Struct* st, const reflection::Field& field,
371
0
                         const char* val) {
372
0
  SetAnyValueS(field.type()->base_type(), st->GetAddressOf(field.offset()),
373
0
               val);
374
0
}
375
376
// Set any vector element as a 64bit int, regardless of type what it is.
377
inline void SetAnyVectorElemI(VectorOfAny* vec, reflection::BaseType elem_type,
378
0
                              size_t i, int64_t val) {
379
0
  SetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
380
0
}
381
382
// Set any vector element as a double, regardless of type what it is.
383
inline void SetAnyVectorElemF(VectorOfAny* vec, reflection::BaseType elem_type,
384
0
                              size_t i, double val) {
385
0
  SetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
386
0
}
387
388
// Set any vector element as a string, regardless of type what it is.
389
inline void SetAnyVectorElemS(VectorOfAny* vec, reflection::BaseType elem_type,
390
0
                              size_t i, const char* val) {
391
0
  SetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
392
0
}
393
394
// ------------------------- RESIZING SETTERS -------------------------
395
396
// "smart" pointer for use with resizing vectors: turns a pointer inside
397
// a vector into a relative offset, such that it is not affected by resizes.
398
template <typename T, typename U>
399
class pointer_inside_vector {
400
 public:
401
  pointer_inside_vector(T* ptr, std::vector<U>& vec)
402
      : offset_(reinterpret_cast<uint8_t*>(ptr) -
403
                reinterpret_cast<uint8_t*>(vec.data())),
404
        vec_(vec) {}
405
406
  T* operator*() const {
407
    return reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(vec_.data()) +
408
                                offset_);
409
  }
410
  T* operator->() const { return operator*(); }
411
412
 private:
413
  size_t offset_;
414
  std::vector<U>& vec_;
415
};
416
417
// Helper to create the above easily without specifying template args.
418
template <typename T, typename U>
419
pointer_inside_vector<T, U> piv(T* ptr, std::vector<U>& vec) {
420
  return pointer_inside_vector<T, U>(ptr, vec);
421
}
422
423
2.60k
inline const char* UnionTypeFieldSuffix() { return "_type"; }
424
425
// Helper to figure out the actual table type a union refers to.
426
inline const reflection::Object& GetUnionType(
427
    const reflection::Schema& schema, const reflection::Object& parent,
428
0
    const reflection::Field& unionfield, const Table& table) {
429
0
  auto enumdef = schema.enums()->Get(unionfield.type()->index());
430
0
  // TODO: this is clumsy and slow, but no other way to find it?
431
0
  auto type_field = parent.fields()->LookupByKey(
432
0
      (unionfield.name()->str() + UnionTypeFieldSuffix()).c_str());
433
0
  FLATBUFFERS_ASSERT(type_field);
434
0
  auto union_type = GetFieldI<uint8_t>(table, *type_field);
435
0
  auto enumval = enumdef->values()->LookupByKey(union_type);
436
0
  return *schema.objects()->Get(enumval->union_type()->index());
437
0
}
438
439
// Changes the contents of a string inside a FlatBuffer. FlatBuffer must
440
// live inside a std::vector so we can resize the buffer if needed.
441
// "str" must live inside "flatbuf" and may be invalidated after this call.
442
// If your FlatBuffer's root table is not the schema's root table, you should
443
// pass in your root_table type as well.
444
void SetString(const reflection::Schema& schema, const std::string& val,
445
               const String* str, std::vector<uint8_t>* flatbuf,
446
               const reflection::Object* root_table = nullptr);
447
448
// Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must
449
// live inside a std::vector so we can resize the buffer if needed.
450
// "vec" must live inside "flatbuf" and may be invalidated after this call.
451
// If your FlatBuffer's root table is not the schema's root table, you should
452
// pass in your root_table type as well.
453
uint8_t* ResizeAnyVector(const reflection::Schema& schema, uoffset_t newsize,
454
                         const VectorOfAny* vec, uoffset_t num_elems,
455
                         uoffset_t elem_size, std::vector<uint8_t>* flatbuf,
456
                         const reflection::Object* root_table = nullptr);
457
458
template <typename T>
459
void ResizeVector(const reflection::Schema& schema, uoffset_t newsize, T val,
460
                  const Vector<T>* vec, std::vector<uint8_t>* flatbuf,
461
                  const reflection::Object* root_table = nullptr) {
462
  auto delta_elem = static_cast<int>(newsize) - static_cast<int>(vec->size());
463
  auto newelems = ResizeAnyVector(
464
      schema, newsize, reinterpret_cast<const VectorOfAny*>(vec), vec->size(),
465
      static_cast<uoffset_t>(sizeof(T)), flatbuf, root_table);
466
  // Set new elements to "val".
467
  for (int i = 0; i < delta_elem; i++) {
468
    auto loc = newelems + i * sizeof(T);
469
    auto is_scalar = flatbuffers::is_scalar<T>::value;
470
    if (is_scalar) {
471
      WriteScalar(loc, val);
472
    } else {  // struct
473
      *reinterpret_cast<T*>(loc) = val;
474
    }
475
  }
476
}
477
478
// Adds any new data (in the form of a new FlatBuffer) to an existing
479
// FlatBuffer. This can be used when any of the above methods are not
480
// sufficient, in particular for adding new tables and new fields.
481
// This is potentially slightly less efficient than a FlatBuffer constructed
482
// in one piece, since the new FlatBuffer doesn't share any vtables with the
483
// existing one.
484
// The return value can now be set using Vector::MutateOffset or SetFieldT
485
// below.
486
const uint8_t* AddFlatBuffer(std::vector<uint8_t>& flatbuf,
487
                             const uint8_t* newbuf, size_t newlen);
488
489
inline bool SetFieldT(Table* table, const reflection::Field& field,
490
0
                      const uint8_t* val) {
491
0
  FLATBUFFERS_ASSERT(sizeof(uoffset_t) ==
492
0
                     GetTypeSize(field.type()->base_type()));
493
0
  return table->SetPointer(field.offset(), val);
494
0
}
495
496
// ------------------------- COPYING -------------------------
497
498
// Generic copying of tables from a FlatBuffer into a FlatBuffer builder.
499
// Can be used to do any kind of merging/selecting you may want to do out
500
// of existing buffers. Also useful to reconstruct a whole buffer if the
501
// above resizing functionality has introduced garbage in a buffer you want
502
// to remove.
503
// Note: this does not deal with DAGs correctly. If the table passed forms a
504
// DAG, the copy will be a tree instead (with duplicates). Strings can be
505
// shared however, by passing true for use_string_pooling.
506
507
Offset<const Table*> CopyTable(FlatBufferBuilder& fbb,
508
                               const reflection::Schema& schema,
509
                               const reflection::Object& objectdef,
510
                               const Table& table,
511
                               bool use_string_pooling = false);
512
513
// Verifies the provided flatbuffer using reflection.
514
// root should point to the root type for this flatbuffer.
515
// buf should point to the start of flatbuffer data.
516
// length specifies the size of the flatbuffer data.
517
bool Verify(const reflection::Schema& schema, const reflection::Object& root,
518
            const uint8_t* buf, size_t length, uoffset_t max_depth = 64,
519
            uoffset_t max_tables = 1000000);
520
521
bool VerifySizePrefixed(const reflection::Schema& schema,
522
                        const reflection::Object& root, const uint8_t* buf,
523
                        size_t length, uoffset_t max_depth = 64,
524
                        uoffset_t max_tables = 1000000);
525
526
}  // namespace flatbuffers
527
528
#endif  // FLATBUFFERS_REFLECTION_H_