diff options
author | Wouter van Oortmerssen <aardappel@gmail.com> | 2017-08-24 17:44:03 -0700 |
---|---|---|
committer | Wouter van Oortmerssen <aardappel@gmail.com> | 2017-09-22 16:17:02 -0700 |
commit | 72a99abfb7db64dc49720b28b41f382b5ec7cde0 (patch) | |
tree | 2687abf7c654e5c04b0dd27815846238812be2ef | |
parent | 21a81219820f4acc1e50569ff280b24b1af29a36 (diff) | |
download | flatbuffers-72a99abfb7db64dc49720b28b41f382b5ec7cde0.tar.gz flatbuffers-72a99abfb7db64dc49720b28b41f382b5ec7cde0.tar.bz2 flatbuffers-72a99abfb7db64dc49720b28b41f382b5ec7cde0.zip |
Added support for mini-reflection tables.
Change-Id: I83453d074685fa57bbf1c7c87b1d9392ce972085
Tested: on Linux.
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rwxr-xr-x | docs/source/Compiler.md | 3 | ||||
-rwxr-xr-x | docs/source/CppUsage.md | 24 | ||||
-rw-r--r-- | include/flatbuffers/flatbuffers.h | 67 | ||||
-rw-r--r-- | include/flatbuffers/idl.h | 5 | ||||
-rw-r--r-- | include/flatbuffers/minireflect.h | 352 | ||||
-rw-r--r-- | samples/monster_generated.h | 114 | ||||
-rw-r--r-- | src/flatc.cpp | 6 | ||||
-rw-r--r-- | src/idl_gen_cpp.cpp | 165 | ||||
-rw-r--r-- | tests/generate_code.bat | 6 | ||||
-rwxr-xr-x | tests/generate_code.sh | 8 | ||||
-rw-r--r-- | tests/monster_test.bfbs | bin | 4480 -> 5400 bytes | |||
-rw-r--r-- | tests/monster_test_generated.h | 312 | ||||
-rw-r--r-- | tests/namespace_test/namespace_test1_generated.h | 53 | ||||
-rw-r--r-- | tests/namespace_test/namespace_test2_generated.h | 79 | ||||
-rw-r--r-- | tests/test.cpp | 70 | ||||
-rw-r--r-- | tests/union_vector/union_vector_generated.h | 101 |
17 files changed, 1340 insertions, 27 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e8353252..e9ba5288 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ set(FlatBuffers_Library_SRCS include/flatbuffers/stl_emulation.h include/flatbuffers/flexbuffers.h include/flatbuffers/registry.h + include/flatbuffers/minireflect.h src/code_generators.cpp src/idl_parser.cpp src/idl_gen_text.cpp @@ -200,6 +201,7 @@ function(compile_flatbuffers_schema_to_cpp SRC_FBS) COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable --gen-object-api -o "${SRC_FBS_DIR}" --cpp-ptr-type flatbuffers::unique_ptr # Used to test with C++98 STLs + --reflect-names -I "${CMAKE_CURRENT_SOURCE_DIR}/tests/include_test" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" DEPENDS flatc) diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 80f81e8f..aaa274c3 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -125,5 +125,8 @@ Additional options: - `--keep-prefix` : Keep original prefix of schema include statement. +- `--reflect-types` : Add minimal type reflection to code generation. +- `--reflect-names` : Add minimal type/name reflection. + NOTE: short-form options for generators are deprecated, use the long form whenever possible. diff --git a/docs/source/CppUsage.md b/docs/source/CppUsage.md index cff3071b..2ded6110 100755 --- a/docs/source/CppUsage.md +++ b/docs/source/CppUsage.md @@ -231,6 +231,30 @@ schema, as well as a lot of helper functions. And example of usage, for the time being, can be found in `test.cpp/ReflectionTest()`. +## Mini Reflection + +A more limited form of reflection is available for direct inclusion in +generated code, which doesn't any (binary) schema access at all. It was designed +to keep the overhead of reflection as low as possible (on the order of 2-6 +bytes per field added to your executable), but doesn't contain all the +information the (binary) schema contains. + +You add this information to your generated code by specifying `--reflect-types` +(or instead `--reflect-names` if you also want field / enum names). + +You can now use this information, for example to print a FlatBuffer to text: + + auto s = flatbuffers::FlatBufferToString(flatbuf, MonsterTypeTable()); + +`MonsterTypeTable()` is declared in the generated code for each type. The +string produced is very similar to the JSON produced by the `Parser` based +text generator. + +You'll need `flatbuffers/minireflect.h` for this functionality. In there is also +a convenient visitor/iterator so you can write your own output / functionality +based on the mini reflection tables without having to know the FlatBuffers or +reflection encoding. + ## Storing maps / dictionaries in a FlatBuffer FlatBuffers doesn't support maps natively, but there is support to diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index b7bad539..3b56ba49 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -2066,6 +2066,73 @@ inline int LookupEnum(const char **names, const char *name) { #error Unknown compiler, please define structure alignment macros #endif +// Minimal reflection via code generation. +// Besides full-fat reflection (see reflection.h) and parsing/printing by +// loading schemas (see idl.h), we can also have code generation for mimimal +// reflection data which allows pretty-printing and other uses without needing +// a schema or a parser. +// Generate code with --reflect-types (types only) or --reflect-names (names +// also) to enable. +// See minireflect.h for utilities using this functionality. + +// These types are organized slightly differently as the ones in idl.h. +enum SequenceType { ST_TABLE, ST_STRUCT, ST_UNION, ST_ENUM }; + +// Scalars have the same order as in idl.h +#define FLATBUFFERS_GEN_ELEMENTARY_TYPES(ET) \ + ET(ET_UTYPE) \ + ET(ET_BOOL) \ + ET(ET_CHAR) \ + ET(ET_UCHAR) \ + ET(ET_SHORT) \ + ET(ET_USHORT) \ + ET(ET_INT) \ + ET(ET_UINT) \ + ET(ET_LONG) \ + ET(ET_ULONG) \ + ET(ET_FLOAT) \ + ET(ET_DOUBLE) \ + ET(ET_STRING) \ + ET(ET_SEQUENCE) // See SequenceType. + +enum ElementaryType { + #define FLATBUFFERS_ET(E) E, + FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) + #undef FLATBUFFERS_ET +}; + +inline const char **ElementaryTypeNames() { + static const char *names[] = { + #define FLATBUFFERS_ET(E) #E, + FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) + #undef FLATBUFFERS_ET + }; + return names; +} + +// Basic type info cost just 16bits per field! +struct TypeCode { + uint16_t base_type : 4; // ElementaryType + uint16_t is_vector : 1; + int16_t sequence_ref : 11; // Index into type_refs below, or -1 for none. +}; + +static_assert(sizeof(TypeCode) == 2, "TypeCode"); + +struct TypeTable; + +// Signature of the static method present in each type. +typedef TypeTable *(*TypeFunction)(); + +struct TypeTable { + SequenceType st; + size_t num_elems; // of each of the arrays below. + const TypeCode *type_codes; + const TypeFunction *type_refs; + const int32_t *values; // Only set for non-consecutive enum/union or structs. + const char **names; // Only set if compiled with --reflect-names. +}; + // String which identifies the current version of FlatBuffers. // flatbuffer_version_string is used by Google developers to identify which // applications uploaded to Google Play are using this library. This allows diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index a6f2c4d2..3ba5e0d5 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -396,6 +396,10 @@ struct IDLOptions { Language lang; + enum MiniReflect { kNone, kTypes, kTypesAndNames }; + + MiniReflect mini_reflect; + // The corresponding language bit will be set if a language is included // for code generation. unsigned long lang_to_generate; @@ -426,6 +430,7 @@ struct IDLOptions { reexport_ts_modules(true), protobuf_ascii_alike(false), lang(IDLOptions::kJava), + mini_reflect(IDLOptions::kNone), lang_to_generate(0) {} }; diff --git a/include/flatbuffers/minireflect.h b/include/flatbuffers/minireflect.h new file mode 100644 index 00000000..cb3f4038 --- /dev/null +++ b/include/flatbuffers/minireflect.h @@ -0,0 +1,352 @@ +/* + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_MINIREFLECT_H_ +#define FLATBUFFERS_MINIREFLECT_H_ + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/util.h" + +namespace flatbuffers { + +// Utilities that can be used with the "mini reflection" tables present +// in generated code with --reflect-types (only types) or --reflect-names +// (also names). +// This allows basic reflection functionality such as pretty-printing +// that does not require the use of the schema parser or loading of binary +// schema files at runtime (reflection.h). + +// For any of the functions below that take `const TypeTable *`, you pass +// `FooTypeTable()` if the type of the root is `Foo`. + +// First, a generic iterator that can be used by multiple algorithms. + +struct IterationVisitor { + // These mark the scope of a table or struct. + virtual void StartSequence() {} + virtual void EndSequence() {} + // Called for each field regardless of wether it is present or not. + // If not present, val == nullptr. set_idx is the index of all set fields. + virtual void Field(size_t /*field_idx*/, size_t /*set_idx*/, + ElementaryType /*type*/, bool /*is_vector*/, + const TypeTable * /*type_table*/, const char * /*name*/, + const uint8_t * /*val*/) {} + // Called for a value that is actually present, after a field, or as part + // of a vector. + virtual void UType(uint8_t, const char *) {} + virtual void Bool(bool) {} + virtual void Char(int8_t, const char *) {} + virtual void UChar(uint8_t, const char *) {} + virtual void Short(int16_t, const char *) {} + virtual void UShort(uint16_t, const char *) {} + virtual void Int(int32_t, const char *) {} + virtual void UInt(uint32_t, const char *) {} + virtual void Long(int64_t) {} + virtual void ULong(uint64_t) {} + virtual void Float(float) {} + virtual void Double(double) {} + virtual void String(const String *) {} + virtual void Unknown(const uint8_t *) {} // From a future version. + // These mark the scope of a vector. + virtual void StartVector() {} + virtual void EndVector() {} + virtual void Element(size_t /*i*/, ElementaryType /*type*/, + const TypeTable * /*type_table*/, + const uint8_t * /*val*/) + {} + virtual ~IterationVisitor() {} +}; + +inline size_t InlineSize(ElementaryType type, const TypeTable *type_table) { + switch (type) { + case ET_UTYPE: + case ET_BOOL: + case ET_CHAR: + case ET_UCHAR: + return 1; + case ET_SHORT: + case ET_USHORT: + return 2; + case ET_INT: + case ET_UINT: + case ET_FLOAT: + case ET_STRING: + return 4; + case ET_LONG: + case ET_ULONG: + case ET_DOUBLE: + return 8; + case ET_SEQUENCE: + switch (type_table->st) { + case ST_TABLE: + case ST_UNION: + return 4; + case ST_STRUCT: + return type_table->values[type_table->num_elems]; + default: + assert(false); + return 1; + } + default: + assert(false); + return 1; + } +} + +inline int32_t LookupEnum(int32_t enum_val, const int32_t *values, + size_t num_values) { + if (!values) return enum_val; + for (size_t i = 0; i < num_values; i++) { + if (enum_val == values[i]) return static_cast<int32_t>(i); + } + return -1; // Unknown enum value. +} + +template<typename T> const char *EnumName(T tval, const TypeTable *type_table) { + if (!type_table || !type_table->names) return nullptr; + auto i = LookupEnum(static_cast<int32_t>(tval), type_table->values, + type_table->num_elems); + if (i >= 0 && i < static_cast<int32_t>(type_table->num_elems)) { + return type_table->names[i]; + } + return nullptr; +} + +void IterateObject(const uint8_t *obj, const TypeTable *type_table, + IterationVisitor *visitor); + +inline void IterateValue(ElementaryType type, const uint8_t *val, + const TypeTable *type_table, + const uint8_t *prev_val, + soffset_t vector_index, + IterationVisitor *visitor) { + switch (type) { + case ET_UTYPE: { + auto tval = *reinterpret_cast<const uint8_t *>(val); + visitor->UType(tval, EnumName(tval, type_table)); + break; + } + case ET_BOOL: { + visitor->Bool(*reinterpret_cast<const uint8_t *>(val) != 0); + break; + } + case ET_CHAR: { + auto tval = *reinterpret_cast<const int8_t *>(val); + visitor->Char(tval, EnumName(tval, type_table)); + break; + } + case ET_UCHAR: { + auto tval = *reinterpret_cast<const uint8_t *>(val); + visitor->UChar(tval, EnumName(tval, type_table)); + break; + } + case ET_SHORT: { + auto tval = *reinterpret_cast<const int16_t *>(val); + visitor->Short(tval, EnumName(tval, type_table)); + break; + } + case ET_USHORT: { + auto tval = *reinterpret_cast<const uint16_t *>(val); + visitor->UShort(tval, EnumName(tval, type_table)); + break; + } + case ET_INT: { + auto tval = *reinterpret_cast<const int32_t *>(val); + visitor->Int(tval, EnumName(tval, type_table)); + break; + } + case ET_UINT: { + auto tval = *reinterpret_cast<const uint32_t *>(val); + visitor->UInt(tval, EnumName(tval, type_table)); + break; + } + case ET_LONG: { + visitor->Long(*reinterpret_cast<const int64_t *>(val)); + break; + } + case ET_ULONG: { + visitor->ULong(*reinterpret_cast<const uint64_t *>(val)); + break; + } + case ET_FLOAT: { + visitor->Float(*reinterpret_cast<const float *>(val)); + break; + } + case ET_DOUBLE: { + visitor->Double(*reinterpret_cast<const double *>(val)); + break; + } + case ET_STRING: { + val += ReadScalar<uoffset_t>(val); + visitor->String(reinterpret_cast<const String *>(val)); + break; + } + case ET_SEQUENCE: { + switch (type_table->st) { + case ST_TABLE: + val += ReadScalar<uoffset_t>(val); + IterateObject(val, type_table, visitor); + break; + case ST_STRUCT: + IterateObject(val, type_table, visitor); + break; + case ST_UNION: { + val += ReadScalar<uoffset_t>(val); + assert(prev_val); + auto union_type = *prev_val; // Always a uint8_t. + if (vector_index >= 0) { + auto type_vec = reinterpret_cast<const Vector<uint8_t> *>(prev_val); + union_type = type_vec->Get(static_cast<uoffset_t>(vector_index)); + } + auto type_code_idx = LookupEnum(union_type, type_table->values, + type_table->num_elems); + if (type_code_idx >= 0 && type_code_idx < + static_cast<int32_t>(type_table->num_elems)) { + auto type_code = type_table->type_codes[type_code_idx]; + switch (type_code.base_type) { + case ET_SEQUENCE: { + auto ref = type_table->type_refs[type_code.sequence_ref](); + IterateObject(val, ref, visitor); + break; + } + case ET_STRING: + visitor->String(reinterpret_cast<const String *>(val)); + break; + default: + visitor->Unknown(val); + } + } else { + visitor->Unknown(val); + } + break; + } + case ST_ENUM: + assert(false); + break; + } + break; + } + default: { + visitor->Unknown(val); + break; + } + } +} + +inline void IterateObject(const uint8_t *obj, const TypeTable *type_table, + IterationVisitor *visitor) { + visitor->StartSequence(); + const uint8_t *prev_val = nullptr; + size_t set_idx = 0; + for (size_t i = 0; i < type_table->num_elems; i++) { + auto type_code = type_table->type_codes[i]; + auto type = static_cast<ElementaryType>(type_code.base_type); + auto is_vector = type_code.is_vector != 0; + auto ref_idx = type_code.sequence_ref; + const TypeTable *ref = nullptr; + if (ref_idx >= 0) { + ref = type_table->type_refs[ref_idx](); + } + auto name = type_table->names ? type_table->names[i] : nullptr; + const uint8_t *val = nullptr; + if (type_table->st == ST_TABLE) { + val = reinterpret_cast<const Table *>(obj)->GetAddressOf( + FieldIndexToOffset(static_cast<voffset_t>(i))); + } else { + val = obj + type_table->values[i]; + } + visitor->Field(i, set_idx, type, is_vector, ref, name, val); + if (val) { + set_idx++; + if (is_vector) { + val += ReadScalar<uoffset_t>(val); + auto vec = reinterpret_cast<const Vector<uint8_t> *>(val); + visitor->StartVector(); + auto elem_ptr = vec->Data(); + for (size_t j = 0; j < vec->size(); j++) { + visitor->Element(j, type, ref, elem_ptr); + IterateValue(type, elem_ptr, ref, prev_val, static_cast<soffset_t>(j), + visitor); + elem_ptr += InlineSize(type, ref); + } + visitor->EndVector(); + } else { + IterateValue(type, val, ref, prev_val, -1, visitor); + } + } + prev_val = val; + } + visitor->EndSequence(); +} + +inline void IterateFlatBuffer(const uint8_t *buffer, + const TypeTable *type_table, + IterationVisitor *callback) { + IterateObject(GetRoot<uint8_t>(buffer), type_table, callback); +} + +// Outputting a Flatbuffer to a string. Tries to conform as close to JSON / +// the output generated by idl_gen_text.cpp. + +struct ToStringVisitor : public IterationVisitor { + std::string s; + void StartSequence() { s += "{ "; } + void EndSequence() { s += " }"; } + void Field(size_t /*field_idx*/, size_t set_idx, ElementaryType /*type*/, + bool /*is_vector*/, const TypeTable * /*type_table*/, + const char *name, const uint8_t *val) { + if (!val) return; + if (set_idx) s += ", "; + if (name) { s += name; s += ": "; } + } + template<typename T> void Named(T x, const char *name) { + if (name) s+= name; + else s+= NumToString(x); + } + void UType(uint8_t x, const char *name) { Named(x, name); } + void Bool(bool x) { s+= x ? "true" : "false"; } + void Char(int8_t x, const char *name) { Named(x, name); } + void UChar(uint8_t x, const char *name) { Named(x, name); } + void Short(int16_t x, const char *name) { Named(x, name); } + void UShort(uint16_t x, const char *name) { Named(x, name); } + void Int(int32_t x, const char *name) { Named(x, name); } + void UInt(uint32_t x, const char *name) { Named(x, name); } + void Long(int64_t x) { s+= NumToString(x); } + void ULong(uint64_t x) { s+= NumToString(x); } + void Float(float x) { s+= NumToString(x); } + void Double(double x) { s+= NumToString(x); } + void String(const struct String *str) { + EscapeString(str->c_str(), str->size(), &s, true); + } + void Unknown(const uint8_t *) { s += "(?)"; } + void StartVector() { s += "[ "; } + void EndVector() { s += " ]"; } + void Element(size_t i, ElementaryType /*type*/, + const TypeTable * /*type_table*/, const uint8_t * /*val*/) { + if (i) s += ", "; + } +}; + +inline std::string FlatBufferToString(const uint8_t *buffer, + const TypeTable *type_table) { + ToStringVisitor tostring_visitor; + IterateFlatBuffer(buffer, type_table, &tostring_visitor); + return tostring_visitor.s; +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_MINIREFLECT_H_ diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 523a136b..0560d46a 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -605,6 +605,120 @@ inline void EquipmentUnion::Reset() { type = Equipment_NONE; } +flatbuffers::TypeTable *Vec3TypeTable(); + +flatbuffers::TypeTable *MonsterTypeTable(); + +flatbuffers::TypeTable *WeaponTypeTable(); + +flatbuffers::TypeTable *ColorTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + ColorTypeTable + }; + static const char *names[] = { + "Red", + "Green", + "Blue" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_ENUM, 3, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *EquipmentTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, -1 }, + { flatbuffers::ET_SEQUENCE, 0, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + WeaponTypeTable + }; + static const char *names[] = { + "NONE", + "Weapon" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_UNION, 2, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *Vec3TypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 } + }; + static const int32_t values[] = { 0, 4, 8, 12 }; + static const char *names[] = { + "x", + "y", + "z" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 3, type_codes, nullptr, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *MonsterTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_SHORT, 0, -1 }, + { flatbuffers::ET_SHORT, 0, -1 }, + { flatbuffers::ET_STRING, 0, -1 }, + { flatbuffers::ET_BOOL, 0, -1 }, + { flatbuffers::ET_UCHAR, 1, -1 }, + { flatbuffers::ET_CHAR, 0, 1 }, + { flatbuffers::ET_SEQUENCE, 1, 2 }, + { flatbuffers::ET_UTYPE, 0, 3 }, + { flatbuffers::ET_SEQUENCE, 0, 3 } + }; + static flatbuffers::TypeFunction type_refs[] = { + Vec3TypeTable, + ColorTypeTable, + WeaponTypeTable, + EquipmentTypeTable + }; + static const char *names[] = { + "pos", + "mana", + "hp", + "name", + "friendly", + "inventory", + "color", + "weapons", + "equipped_type", + "equipped" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 10, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *WeaponTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_STRING, 0, -1 }, + { flatbuffers::ET_SHORT, 0, -1 } + }; + static const char *names[] = { + "name", + "damage" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 2, type_codes, nullptr, nullptr, names + }; + return &tt; +} + inline const MyGame::Sample::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<MyGame::Sample::Monster>(buf); } diff --git a/src/flatc.cpp b/src/flatc.cpp index 405cc6d5..abc948c2 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -111,6 +111,8 @@ std::string FlatCompiler::GetUsageString(const char* program_name) const { " --keep-prefix Keep original prefix of schema include statement.\n" " --no-fb-import Don't include flatbuffers import statement for TypeScript.\n" " --no-ts-reexport Don't re-export imported dependencies for TypeScript.\n" + " --reflect-types Add minimal type reflection to code generation.\n" + " --reflect-names Add minimal type/name reflection.\n" "FILEs may be schemas (must end in .fbs), or JSON files (conforming to preceding\n" "schema). FILEs after the -- must be binary flatbuffer format files.\n" "Output files are named using the base file name of the input,\n" @@ -243,6 +245,10 @@ int FlatCompiler::Compile(int argc, const char** argv) { opts.skip_flatbuffers_import = true; } else if(arg == "--no-ts-reexport") { opts.reexport_ts_modules = false; + } else if(arg == "--reflect-types") { + opts.mini_reflect = IDLOptions::kTypes; + } else if(arg == "--reflect-names") { + opts.mini_reflect = IDLOptions::kTypesAndNames; } else { for (size_t i = 0; i < params_.num_generators; ++i) { if (arg == params_.generators[i].generator_opt_long || diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 29f93d3c..93941b80 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -169,6 +169,37 @@ class CppGenerator : public BaseGenerator { } } + // Generate code for mini reflection. + if (parser_.opts.mini_reflect != IDLOptions::kNone) { + // To break cyclic dependencies, first pre-declare all tables/structs. + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + const auto &struct_def = **it; + if (!struct_def.generated) { + SetNameSpace(struct_def.defined_namespace); + GenMiniReflectPre(&struct_def); + } + } + // Then the unions/enums that may refer to them. + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + const auto &enum_def = **it; + if (!enum_def.generated) { + SetNameSpace(enum_def.defined_namespace); + GenMiniReflect(nullptr, &enum_def); + } + } + // Then the full tables/structs. + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + const auto &struct_def = **it; + if (!struct_def.generated) { + SetNameSpace(struct_def.defined_namespace); + GenMiniReflect(&struct_def, nullptr); + } + } + } + // Generate convenient global helper functions: if (parser_.root_struct_def_) { auto &struct_def = *parser_.root_struct_def_; @@ -559,6 +590,140 @@ class CppGenerator : public BaseGenerator { (inclass ? " = nullptr" : "") + ") const"; } + void GenMiniReflectPre(const StructDef *struct_def) { + code_.SetValue("NAME", struct_def->name); + code_ += "flatbuffers::TypeTable *{{NAME}}TypeTable();"; + code_ += ""; + } + + void GenMiniReflect(const StructDef *struct_def, + const EnumDef *enum_def) { + code_.SetValue("NAME", struct_def ? struct_def->name : enum_def->name); + code_.SetValue("SEQ_TYPE", struct_def + ? (struct_def->fixed ? "ST_STRUCT" : "ST_TABLE") + : (enum_def->is_union ? "ST_UNION" : "ST_ENUM")); + auto num_fields = struct_def + ? struct_def->fields.vec.size() + : enum_def->vals.vec.size(); + code_.SetValue("NUM_FIELDS", NumToString(num_fields)); + std::vector<std::string> names; + std::vector<Type> types; + bool consecutive_enum_from_zero = true; + if (struct_def) { + for (auto it = struct_def->fields.vec.begin(); + it != struct_def->fields.vec.end(); ++it) { + const auto &field = **it; + names.push_back(field.name); + types.push_back(field.value.type); + } + } else { + for (auto it = enum_def->vals.vec.begin(); it != enum_def->vals.vec.end(); + ++it) { + const auto &ev = **it; + names.push_back(ev.name); + types.push_back(enum_def->is_union ? ev.union_type + : Type(enum_def->underlying_type)); + if (static_cast<int64_t>(it - enum_def->vals.vec.begin()) != ev.value) { + consecutive_enum_from_zero = false; + } + } + } + std::string ts; + std::vector<std::string> type_refs; + for (auto it = types.begin(); it != types.end(); ++it) { + auto &type = *it; + if (!ts.empty()) ts += ",\n "; + auto is_vector = type.base_type == BASE_TYPE_VECTOR; + auto bt = is_vector ? type.element : type.base_type; + auto et = IsScalar(bt) || bt == BASE_TYPE_STRING + ? bt - BASE_TYPE_UTYPE + ET_UTYPE + : ET_SEQUENCE; + int ref_idx = -1; + std::string ref_name = type.struct_def + ? WrapInNameSpace(*type.struct_def) + : type.enum_def + ? WrapInNameSpace(*type.enum_def) + : ""; + if (!ref_name.empty()) { + auto rit = type_refs.begin(); + for (; rit != type_refs.end(); ++rit) { + if (*rit == ref_name) { + ref_idx = static_cast<int>(rit - type_refs.begin()); + break; + } + } + if (rit == type_refs.end()) { + ref_idx = static_cast<int>(type_refs.size()); + type_refs.push_back(ref_name); + } + } + ts += "{ flatbuffers::" + std::string(ElementaryTypeNames()[et]) + ", " + + NumToString(is_vector) + ", " + NumToString(ref_idx) + " }"; + } + std::string rs; + for (auto it = type_refs.begin(); it != type_refs.end(); ++it) { + if (!rs.empty()) rs += ",\n "; + rs += *it + "TypeTable"; + } + std::string ns; + for (auto it = names.begin(); it != names.end(); ++it) { + if (!ns.empty()) ns += ",\n "; + ns += "\"" + *it + "\""; + } + std::string vs; + if (enum_def && !consecutive_enum_from_zero) { + for (auto it = enum_def->vals.vec.begin(); it != enum_def->vals.vec.end(); + ++it) { + const auto &ev = **it; + if (!vs.empty()) vs += ", "; + vs += NumToString(ev.value); + } + } else if (struct_def && struct_def->fixed) { + for (auto it = struct_def->fields.vec.begin(); + it != struct_def->fields.vec.end(); ++it) { + const auto &field = **it; + vs += NumToString(field.value.offset); + vs += ", "; + } + vs += NumToString(struct_def->bytesize); + } + code_.SetValue("TYPES", ts); + code_.SetValue("REFS", rs); + code_.SetValue("NAMES", ns); + code_.SetValue("VALUES", vs); + code_ += "flatbuffers::TypeTable *{{NAME}}TypeTable() {"; + if (num_fields) { + code_ += " static flatbuffers::TypeCode type_codes[] = {"; + code_ += " {{TYPES}}"; + code_ += " };"; + } + if (!type_refs.empty()) { + code_ += " static flatbuffers::TypeFunction type_refs[] = {"; + code_ += " {{REFS}}"; + code_ += " };"; + } + if (!vs.empty()) { + code_ += " static const int32_t values[] = { {{VALUES}} };"; + } + auto has_names = num_fields && + parser_.opts.mini_reflect == IDLOptions::kTypesAndNames; + if (has_names) { + code_ += " static const char *names[] = {"; + code_ += " {{NAMES}}"; + code_ += " };"; + } + code_ += " static flatbuffers::TypeTable tt = {"; + code_ += std::string(" flatbuffers::{{SEQ_TYPE}}, {{NUM_FIELDS}}, ") + + (num_fields ? "type_codes, " : "nullptr, ") + + (!type_refs.empty() ? "type_refs, ": "nullptr, " ) + + (!vs.empty() ? "values, " : "nullptr, ") + + (has_names ? "names" : "nullptr"); + code_ += " };"; + code_ += " return &tt;"; + code_ += "}"; + code_ += ""; + } + // Generate an enum declaration, // an enum string lookup table, // and an enum array of values diff --git a/tests/generate_code.bat b/tests/generate_code.bat index f594542b..1437c7c2 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -15,9 +15,9 @@ set buildtype=Release if "%1"=="-b" set buildtype=%2 -..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json -..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs -..\%buildtype%\flatc.exe --cpp --js --ts --php --gen-mutable --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs +..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json +..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs +..\%buildtype%\flatc.exe --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs ..\%buildtype%\flatc.exe -b --schema --bfbs-comments -I include_test monster_test.fbs ..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs cd ../samples diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 31f4dee5..4b6db29b 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -14,11 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -../flatc --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json -../flatc --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs -../flatc --cpp --js --ts --php --gen-mutable --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs +../flatc --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json +../flatc --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs +../flatc --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs ../flatc -b --schema --bfbs-comments -I include_test monster_test.fbs ../flatc --jsonschema --schema -I include_test monster_test.fbs cd ../samples -../flatc --cpp --gen-mutable --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr monster.fbs +../flatc --cpp --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr monster.fbs cd ../reflection diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs Binary files differindex 15d39986..843e308c 100644 --- a/tests/monster_test.bfbs +++ b/tests/monster_test.bfbs diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 3423a108..f02a4dbd 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -1899,6 +1899,318 @@ inline void AnyUnion::Reset() { type = Any_NONE; } +} // namespace Example + +flatbuffers::TypeTable *InParentNamespaceTypeTable(); + +namespace Example2 { + +flatbuffers::TypeTable *MonsterTypeTable(); + +} // namespace Example2 + +namespace Example { + +flatbuffers::TypeTable *TestTypeTable(); + +flatbuffers::TypeTable *TestSimpleTableWithEnumTypeTable(); + +flatbuffers::TypeTable *Vec3TypeTable(); + +flatbuffers::TypeTable *AbilityTypeTable(); + +flatbuffers::TypeTable *StatTypeTable(); + +flatbuffers::TypeTable *MonsterTypeTable(); + +flatbuffers::TypeTable *TypeAliasesTypeTable(); + +flatbuffers::TypeTable *ColorTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + ColorTypeTable + }; + static const int32_t values[] = { 1, 2, 8 }; + static const char *names[] = { + "Red", + "Green", + "Blue" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_ENUM, 3, type_codes, type_refs, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *AnyTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, -1 }, + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_SEQUENCE, 0, 1 }, + { flatbuffers::ET_SEQUENCE, 0, 2 } + }; + static flatbuffers::TypeFunction type_refs[] = { + MonsterTypeTable, + TestSimpleTableWithEnumTypeTable, + MyGame::Example2::MonsterTypeTable + }; + static const char *names[] = { + "NONE", + "Monster", + "TestSimpleTableWithEnum", + "MyGame_Example2_Monster" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_UNION, 4, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +} // namespace Example + +flatbuffers::TypeTable *InParentNamespaceTypeTable() { + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 0, nullptr, nullptr, nullptr, nullptr + }; + return &tt; +} + +namespace Example2 { + +flatbuffers::TypeTable *MonsterTypeTable() { + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 0, nullptr, nullptr, nullptr, nullptr + }; + return &tt; +} + +} // namespace Example2 + +namespace Example { + +flatbuffers::TypeTable *TestTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SHORT, 0, -1 }, + { flatbuffers::ET_CHAR, 0, -1 } + }; + static const int32_t values[] = { 0, 2, 4 }; + static const char *names[] = { + "a", + "b" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 2, type_codes, nullptr, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *TestSimpleTableWithEnumTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_CHAR, 0, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + ColorTypeTable + }; + static const char *names[] = { + "color" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *Vec3TypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_DOUBLE, 0, -1 }, + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_SEQUENCE, 0, 1 } + }; + static flatbuffers::TypeFunction type_refs[] = { + ColorTypeTable, + TestTypeTable + }; + static const int32_t values[] = { 0, 4, 8, 16, 24, 26, 32 }; + static const char *names[] = { + "x", + "y", + "z", + "test1", + "test2", + "test3" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 6, type_codes, type_refs, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *AbilityTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_UINT, 0, -1 }, + { flatbuffers::ET_UINT, 0, -1 } + }; + static const int32_t values[] = { 0, 4, 8 }; + static const char *names[] = { + "id", + "distance" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 2, type_codes, nullptr, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *StatTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_STRING, 0, -1 }, + { flatbuffers::ET_LONG, 0, -1 }, + { flatbuffers::ET_USHORT, 0, -1 } + }; + static const char *names[] = { + "id", + "val", + "count" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 3, type_codes, nullptr, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *MonsterTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_SHORT, 0, -1 }, + { flatbuffers::ET_SHORT, 0, -1 }, + { flatbuffers::ET_STRING, 0, -1 }, + { flatbuffers::ET_BOOL, 0, -1 }, + { flatbuffers::ET_UCHAR, 1, -1 }, + { flatbuffers::ET_CHAR, 0, 1 }, + { flatbuffers::ET_UTYPE, 0, 2 }, + { flatbuffers::ET_SEQUENCE, 0, 2 }, + { flatbuffers::ET_SEQUENCE, 1, 3 }, + { flatbuffers::ET_STRING, 1, -1 }, + { flatbuffers::ET_SEQUENCE, 1, 4 }, + { flatbuffers::ET_SEQUENCE, 0, 4 }, + { flatbuffers::ET_UCHAR, 1, -1 }, + { flatbuffers::ET_SEQUENCE, 0, 5 }, + { flatbuffers::ET_BOOL, 0, -1 }, + { flatbuffers::ET_INT, 0, -1 }, + { flatbuffers::ET_UINT, 0, -1 }, + { flatbuffers::ET_LONG, 0, -1 }, + { flatbuffers::ET_ULONG, 0, -1 }, + { flatbuffers::ET_INT, 0, -1 }, + { flatbuffers::ET_UINT, 0, -1 }, + { flatbuffers::ET_LONG, 0, -1 }, + { flatbuffers::ET_ULONG, 0, -1 }, + { flatbuffers::ET_BOOL, 1, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_STRING, 1, -1 }, + { flatbuffers::ET_SEQUENCE, 1, 6 }, + { flatbuffers::ET_UCHAR, 1, -1 }, + { flatbuffers::ET_SEQUENCE, 1, 3 }, + { flatbuffers::ET_LONG, 1, -1 }, + { flatbuffers::ET_DOUBLE, 1, -1 }, + { flatbuffers::ET_SEQUENCE, 0, 7 } + }; + static flatbuffers::TypeFunction type_refs[] = { + Vec3TypeTable, + ColorTypeTable, + AnyTypeTable, + TestTypeTable, + MonsterTypeTable, + StatTypeTable, + AbilityTypeTable, + MyGame::InParentNamespaceTypeTable + }; + static const char *names[] = { + "pos", + "mana", + "hp", + "name", + "friendly", + "inventory", + "color", + "test_type", + "test", + "test4", + "testarrayofstring", + "testarrayoftables", + "enemy", + "testnestedflatbuffer", + "testempty", + "testbool", + "testhashs32_fnv1", + "testhashu32_fnv1", + "testhashs64_fnv1", + "testhashu64_fnv1", + "testhashs32_fnv1a", + "testhashu32_fnv1a", + "testhashs64_fnv1a", + "testhashu64_fnv1a", + "testarrayofbools", + "testf", + "testf2", + "testf3", + "testarrayofstring2", + "testarrayofsortedstruct", + "flex", + "test5", + "vector_of_longs", + "vector_of_doubles", + "parent_namespace_test" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 35, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *TypeAliasesTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_CHAR, 0, -1 }, + { flatbuffers::ET_UCHAR, 0, -1 }, + { flatbuffers::ET_SHORT, 0, -1 }, + { flatbuffers::ET_USHORT, 0, -1 }, + { flatbuffers::ET_INT, 0, -1 }, + { flatbuffers::ET_UINT, 0, -1 }, + { flatbuffers::ET_LONG, 0, -1 }, + { flatbuffers::ET_ULONG, 0, -1 }, + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_DOUBLE, 0, -1 }, + { flatbuffers::ET_CHAR, 1, -1 }, + { flatbuffers::ET_DOUBLE, 1, -1 } + }; + static const char *names[] = { + "i8", + "u8", + "i16", + "u16", + "i32", + "u32", + "i64", + "u64", + "f32", + "f64", + "v8", + "vf64" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 12, type_codes, nullptr, nullptr, names + }; + return &tt; +} + inline const MyGame::Example::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<MyGame::Example::Monster>(buf); } diff --git a/tests/namespace_test/namespace_test1_generated.h b/tests/namespace_test/namespace_test1_generated.h index 6fc6df7a..0beea4cc 100644 --- a/tests/namespace_test/namespace_test1_generated.h +++ b/tests/namespace_test/namespace_test1_generated.h @@ -119,6 +119,59 @@ inline flatbuffers::Offset<TableInNestedNS> CreateTableInNestedNS( return builder_.Finish(); } +flatbuffers::TypeTable *TableInNestedNSTypeTable(); + +flatbuffers::TypeTable *StructInNestedNSTypeTable(); + +flatbuffers::TypeTable *EnumInNestedNSTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + EnumInNestedNSTypeTable + }; + static const char *names[] = { + "A", + "B", + "C" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_ENUM, 3, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *TableInNestedNSTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 0, -1 } + }; + static const char *names[] = { + "foo" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *StructInNestedNSTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 0, -1 }, + { flatbuffers::ET_INT, 0, -1 } + }; + static const int32_t values[] = { 0, 4, 8 }; + static const char *names[] = { + "a", + "b" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 2, type_codes, nullptr, values, names + }; + return &tt; +} + } // namespace NamespaceB } // namespace NamespaceA diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index 7e650f43..a88d40ea 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -212,6 +212,85 @@ namespace NamespaceC { namespace NamespaceA { +flatbuffers::TypeTable *TableInFirstNSTypeTable(); + +} // namespace NamespaceA + +namespace NamespaceC { + +flatbuffers::TypeTable *TableInCTypeTable(); + +} // namespace NamespaceC + +namespace NamespaceA { + +flatbuffers::TypeTable *SecondTableInATypeTable(); + +flatbuffers::TypeTable *TableInFirstNSTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 1 }, + { flatbuffers::ET_SEQUENCE, 0, 2 } + }; + static flatbuffers::TypeFunction type_refs[] = { + NamespaceA::NamespaceB::TableInNestedNSTypeTable, + NamespaceA::NamespaceB::EnumInNestedNSTypeTable, + NamespaceA::NamespaceB::StructInNestedNSTypeTable + }; + static const char *names[] = { + "foo_table", + "foo_enum", + "foo_struct" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +} // namespace NamespaceA + +namespace NamespaceC { + +flatbuffers::TypeTable *TableInCTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_SEQUENCE, 0, 1 } + }; + static flatbuffers::TypeFunction type_refs[] = { + NamespaceA::TableInFirstNSTypeTable, + NamespaceA::SecondTableInATypeTable + }; + static const char *names[] = { + "refer_to_a1", + "refer_to_a2" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +} // namespace NamespaceC + +namespace NamespaceA { + +flatbuffers::TypeTable *SecondTableInATypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + NamespaceC::TableInCTypeTable + }; + static const char *names[] = { + "refer_to_c" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + }; + return &tt; +} + } // namespace NamespaceA #endif // FLATBUFFERS_GENERATED_NAMESPACETEST2_NAMESPACEA_H_ diff --git a/tests/test.cpp b/tests/test.cpp index 63a73c73..a698b128 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -18,6 +18,7 @@ #include "flatbuffers/idl.h" #include "flatbuffers/util.h" #include "flatbuffers/registry.h" +#include "flatbuffers/minireflect.h" #include "monster_test_generated.h" #include "namespace_test/namespace_test1_generated.h" @@ -46,8 +47,9 @@ int testing_fails = 0; void TestFail(const char *expval, const char *val, const char *exp, const char *file, int line) { - TEST_OUTPUT_LINE("TEST FAILED: %s:%d, %s (%s) != %s", file, line, - exp, expval, val); + TEST_OUTPUT_LINE("VALUE: \"%s\"", expval); + TEST_OUTPUT_LINE("EXPECTED: \"%s\"", val); + TEST_OUTPUT_LINE("TEST FAILED: %s:%d, %s", file, line, exp); assert(0); testing_fails++; } @@ -561,11 +563,7 @@ void ParseAndGenerateTextTest() { std::string jsongen; auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); TEST_EQ(result, true); - - if (jsongen != jsonfile) { - TEST_OUTPUT_LINE("%s----------------\n%s", jsongen.c_str(), jsonfile.c_str()); - TEST_NOTNULL(NULL); - } + TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str()); // We can also do the above using the convenient Registry that knows about // a set of file_identifiers mapped to schemas. @@ -772,6 +770,34 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) { fbb.GetBufferPointer(), fbb.GetSize()), true); } +void MiniReflectFlatBuffersTest(uint8_t *flatbuf) { + auto s = flatbuffers::FlatBufferToString(flatbuf, MonsterTypeTable()); + TEST_EQ_STR(s.c_str(), + "{ " + "pos: { x: 1.0, y: 2.0, z: 3.0, test1: 0.0, test2: Red, test3: " + "{ a: 10, b: 20 } }, " + "hp: 80, " + "name: \"MyMonster\", " + "inventory: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], " + "test_type: Monster, " + "test: { name: \"Fred\" }, " + "test4: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], " + "testarrayofstring: [ \"bob\", \"fred\", \"bob\", \"fred\" ], " + "testarrayoftables: [ { hp: 1000, name: \"Barney\" }, { name: \"Fred\" }, " + "{ name: \"Wilma\" } ], " + // TODO(wvo): should really print this nested buffer correctly. + "testnestedflatbuffer: [ 20, 0, 0, 0, 77, 79, 78, 83, 12, 0, 12, 0, 0, 0, " + "4, 0, 6, 0, 8, 0, 12, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 13, 0, 0, 0, 78, " + "101, 115, 116, 101, 100, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0 ], " + "testarrayofstring2: [ \"jane\", \"mary\" ], " + "testarrayofsortedstruct: [ { id: 1, distance: 10 }, " + "{ id: 2, distance: 20 }, { id: 3, distance: 30 }, " + "{ id: 4, distance: 40 } ], " + "flex: [ 210, 4, 5, 2 ], " + "test5: [ { a: 10, b: 20 }, { a: 30, b: 40 } ] " + "}"); +} + // Parse a .proto schema, output as .fbs void ParseProtoTest() { // load the .proto and the golden file from disk @@ -800,11 +826,7 @@ void ParseProtoTest() { // Ensure generated file is parsable. flatbuffers::Parser parser2; TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true); - - if (fbs != goldenfile) { - TEST_OUTPUT_LINE("%s----------------\n%s", fbs.c_str(), goldenfile.c_str()); - TEST_NOTNULL(NULL); - } + TEST_EQ_STR(fbs.c_str(), goldenfile.c_str()); } template<typename T> void CompareTableFieldValue(flatbuffers::Table *table, @@ -1248,10 +1270,9 @@ void UnicodeTest() { parser.opts.indent_step = -1; auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); TEST_EQ(result, true); - TEST_EQ(jsongen, - std::string( + TEST_EQ_STR(jsongen.c_str(), "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC" - "\\u5225\\u30B5\\u30A4\\u30C8\\u20AC\\u0080\\uD83D\\uDE0E\"}")); + "\\u5225\\u30B5\\u30A4\\u30C8\\u20AC\\u0080\\uD83D\\uDE0E\"}"); } void UnicodeTestAllowNonUTF8() { @@ -1265,10 +1286,9 @@ void UnicodeTestAllowNonUTF8() { parser.opts.indent_step = -1; auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); TEST_EQ(result, true); - TEST_EQ(jsongen, - std::string( + TEST_EQ_STR(jsongen.c_str(), "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC" - "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}")); + "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}"); } void UnicodeTestGenerateTextFailsOnNonUTF8() { @@ -1300,7 +1320,7 @@ void UnicodeSurrogatesTest() { parser.builder_.GetBufferPointer()); auto string = root->GetPointer<flatbuffers::String *>( flatbuffers::FieldIndexToOffset(0)); - TEST_EQ(strcmp(string->c_str(), "\xF0\x9F\x92\xA9"), 0); + TEST_EQ_STR(string->c_str(), "\xF0\x9F\x92\xA9"); } void UnicodeInvalidSurrogatesTest() { @@ -1437,7 +1457,7 @@ void UnknownFieldsTest() { parser.opts.indent_step = -1; auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); TEST_EQ(result, true); - TEST_EQ(jsongen == "{str: \"test\",i: 10}", true); + TEST_EQ_STR(jsongen.c_str(), "{str: \"test\",i: 10}"); } void ParseUnionTest() { @@ -1548,6 +1568,14 @@ void UnionVectorTest() { auto repacked_movie = GetMovie(fbb.GetBufferPointer()); TestMovie(repacked_movie); + + auto s = flatbuffers::FlatBufferToString(fbb.GetBufferPointer(), + MovieTypeTable()); + TEST_EQ_STR(s.c_str(), + "{ main_character_type: Rapunzel, main_character: { hair_length: 6 }, " + "characters_type: [ Belle, MuLan, BookFan, Other, Unused ], " + "characters: [ { books_read: 7 }, { sword_attack_damage: 5 }, " + "{ books_read: 2 }, \"Other\", \"Unused\" ] }"); } void ConformTest() { @@ -1774,6 +1802,8 @@ int main(int /*argc*/, const char * /*argv*/[]) { ObjectFlatBuffersTest(flatbuf.data()); + MiniReflectFlatBuffersTest(flatbuf.data()); + SizePrefixedTest(); #ifndef FLATBUFFERS_NO_FILE_TESTS diff --git a/tests/union_vector/union_vector_generated.h b/tests/union_vector/union_vector_generated.h index a21cb000..3975ec8e 100644 --- a/tests/union_vector/union_vector_generated.h +++ b/tests/union_vector/union_vector_generated.h @@ -600,6 +600,107 @@ inline void CharacterUnion::Reset() { type = Character_NONE; } +flatbuffers::TypeTable *AttackerTypeTable(); + +flatbuffers::TypeTable *RapunzelTypeTable(); + +flatbuffers::TypeTable *BookReaderTypeTable(); + +flatbuffers::TypeTable *MovieTypeTable(); + +flatbuffers::TypeTable *CharacterTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, -1 }, + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_SEQUENCE, 0, 1 }, + { flatbuffers::ET_SEQUENCE, 0, 2 }, + { flatbuffers::ET_SEQUENCE, 0, 2 }, + { flatbuffers::ET_STRING, 0, -1 }, + { flatbuffers::ET_STRING, 0, -1 } + }; + static flatbuffers::TypeFunction type_refs[] = { + AttackerTypeTable, + RapunzelTypeTable, + BookReaderTypeTable + }; + static const char *names[] = { + "NONE", + "MuLan", + "Rapunzel", + "Belle", + "BookFan", + "Other", + "Unused" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_UNION, 7, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *AttackerTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 0, -1 } + }; + static const char *names[] = { + "sword_attack_damage" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, names + }; + return &tt; +} + +flatbuffers::TypeTable *RapunzelTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 0, -1 } + }; + static const int32_t values[] = { 0, 4 }; + static const char *names[] = { + "hair_length" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 1, type_codes, nullptr, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *BookReaderTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 0, -1 } + }; + static const int32_t values[] = { 0, 4 }; + static const char *names[] = { + "books_read" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 1, type_codes, nullptr, values, names + }; + return &tt; +} + +flatbuffers::TypeTable *MovieTypeTable() { + static flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_UTYPE, 0, 0 }, + { flatbuffers::ET_SEQUENCE, 0, 0 }, + { flatbuffers::ET_UTYPE, 1, 0 }, + { flatbuffers::ET_SEQUENCE, 1, 0 } + }; + static flatbuffers::TypeFunction type_refs[] = { + CharacterTypeTable + }; + static const char *names[] = { + "main_character_type", + "main_character", + "characters_type", + "characters" + }; + static flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 4, type_codes, type_refs, nullptr, names + }; + return &tt; +} + inline const Movie *GetMovie(const void *buf) { return flatbuffers::GetRoot<Movie>(buf); } |