diff options
89 files changed, 5559 insertions, 918 deletions
@@ -40,6 +40,8 @@ flatsamplebinary flatsamplebinary.exe flatsampletext flatsampletext.exe +grpctest +grpctest.exe snapshot.sh tests/go_gen tests/monsterdata_java_wire.mon @@ -55,9 +57,10 @@ build/Xcode/FlatBuffers.xcodeproj/xcuserdata/** FlatBuffers.xcodeproj/ java/.idea java/*.iml -java/target -**/*.pyc .idea +*.iml +target +**/*.pyc build/VS2010/FlatBuffers.sdf build/VS2010/FlatBuffers.opensdf build/VS2010/ipch/**/*.ipch diff --git a/CMakeLists.txt b/CMakeLists.txt index b1e56d6b..cfb84bae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,9 +6,12 @@ project(FlatBuffers) option(FLATBUFFERS_CODE_COVERAGE "Enable the code coverage build option." OFF) option(FLATBUFFERS_BUILD_TESTS "Enable the build of tests and samples." ON) option(FLATBUFFERS_INSTALL "Enable the installation of targets." ON) -option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library" ON) -option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler" ON) +option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library" + ON) +option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler" + ON) option(FLATBUFFERS_BUILD_FLATHASH "Enable the build of flathash" ON) +option(FLATBUFFERS_BUILD_GRPCTEST "Enable the build of grpctest" OFF) if(NOT FLATBUFFERS_BUILD_FLATC AND FLATBUFFERS_BUILD_TESTS) message(WARNING @@ -39,7 +42,10 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_php.cpp src/idl_gen_python.cpp src/idl_gen_fbs.cpp + src/idl_gen_grpc.cpp src/flatc.cpp + grpc/src/compiler/cpp_generator.h + grpc/src/compiler/cpp_generator.cc ) set(FlatHash_SRCS @@ -76,12 +82,23 @@ set(FlatBuffers_Sample_Text_SRCS ${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h ) +set(FlatBuffers_GRPCTest_SRCS + include/flatbuffers/flatbuffers.h + include/flatbuffers/grpc.h + tests/monster_test.grpc.fb.h + tests/monster_test.grpc.fb.cc + grpc/tests/grpctest.cpp + # file generated by running compiler on samples/monster.fbs + ${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h +) + # source_group(Compiler FILES ${FlatBuffers_Compiler_SRCS}) # source_group(Tests FILES ${FlatBuffers_Tests_SRCS}) if(APPLE) set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror -Wextra") + "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror + -Wextra") elseif(CMAKE_COMPILER_IS_GNUCXX) if(CYGWIN) set(CMAKE_CXX_FLAGS @@ -104,7 +121,8 @@ elseif(CMAKE_COMPILER_IS_GNUCXX) elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++ -Wall -pedantic -Werror -Wextra") + "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++ -Wall -pedantic -Werror + -Wextra") if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++abi") @@ -129,6 +147,7 @@ if(BIICODE) endif() include_directories(include) +include_directories(grpc) if(FLATBUFFERS_BUILD_FLATLIB) add_library(flatbuffers STATIC ${FlatBuffers_Library_SRCS}) @@ -150,7 +169,9 @@ function(compile_flatbuffers_schema_to_cpp SRC_FBS) string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS}) add_custom_command( OUTPUT ${GEN_HEADER} - COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable -o "${SRC_FBS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" + COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable + --gen-object-api -o "${SRC_FBS_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" DEPENDS flatc) endfunction() @@ -159,7 +180,8 @@ function(compile_flatbuffers_schema_to_binary SRC_FBS) string(REGEX REPLACE "\\.fbs$" ".bfbs" GEN_BINARY_SCHEMA ${SRC_FBS}) add_custom_command( OUTPUT ${GEN_BINARY_SCHEMA} - COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -b --schema -o "${SRC_FBS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" + COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -b --schema -o "${SRC_FBS_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" DEPENDS flatc) endfunction() @@ -174,6 +196,14 @@ if(FLATBUFFERS_BUILD_TESTS) add_executable(flatsampletext ${FlatBuffers_Sample_Text_SRCS}) endif() +if(FLATBUFFERS_BUILD_GRPCTEST) + if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") + endif() + add_executable(grpctest ${FlatBuffers_GRPCTest_SRCS}) + target_link_libraries(grpctest grpc++_unsecure grpc pthread dl) +endif() + if(FLATBUFFERS_INSTALL) install(DIRECTORY include/flatbuffers DESTINATION include) if(FLATBUFFERS_BUILD_FLATLIB) diff --git a/CONTRIBUTING b/CONTRIBUTING.md index 15887529..15887529 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING.md diff --git a/build_ide/VS2010/flatc.vcxproj b/build_ide/VS2010/flatc.vcxproj index 31cd0134..5aef2384 100755 --- a/build_ide/VS2010/flatc.vcxproj +++ b/build_ide/VS2010/flatc.vcxproj @@ -81,7 +81,7 @@ </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>../../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../../include;../../grpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <CompileAs>CompileAsCpp</CompileAs> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> @@ -127,7 +127,7 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> - <AdditionalIncludeDirectories>../../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../../include;../../grpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <CompileAs>CompileAsCpp</CompileAs> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> @@ -173,7 +173,7 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>../../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../../include;../../grpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <CompileAs>CompileAsCpp</CompileAs> <ExceptionHandling>Sync</ExceptionHandling> <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> @@ -219,7 +219,7 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> - <AdditionalIncludeDirectories>../../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>../../include;../../grpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <CompileAs>CompileAsCpp</CompileAs> <ExceptionHandling>Sync</ExceptionHandling> <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> @@ -263,6 +263,8 @@ </ProjectReference> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="..\..\grpc\src\compiler\cpp_generator.cc" /> + <ClCompile Include="..\..\src\idl_gen_grpc.cpp" /> <ClCompile Include="..\..\src\util.cpp" /> <ClInclude Include="..\..\include\flatbuffers\flatbuffers.h" /> <ClInclude Include="..\..\include\flatbuffers\idl.h" /> diff --git a/build_ide/VS2010/flatc.vcxproj.user b/build_ide/VS2010/flatc.vcxproj.user index 5ef05f7e..1132b8ab 100755 --- a/build_ide/VS2010/flatc.vcxproj.user +++ b/build_ide/VS2010/flatc.vcxproj.user @@ -3,7 +3,7 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <LocalDebuggerWorkingDirectory>..\..\tests</LocalDebuggerWorkingDirectory> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> - <LocalDebuggerCommandArguments>-j -c -n -g --php --no-includes --gen-mutable monster_test.fbs</LocalDebuggerCommandArguments> + <LocalDebuggerCommandArguments>-j -c -n -g --php --no-includes --gen-mutable --gen-object-api monster_test.fbs</LocalDebuggerCommandArguments> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <LocalDebuggerWorkingDirectory>..\..</LocalDebuggerWorkingDirectory> diff --git a/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj b/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj index ee18e9e2..e67f7356 100644 --- a/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj +++ b/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj @@ -11,10 +11,12 @@ 5AC48C391ACA9A0A008132C5 /* idl_gen_general.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD8717A19CB937D0012A827 /* idl_gen_general.cpp */; }; 61823BBC53544106B6DBC38E /* idl_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3709AC883348409592530AE6 /* idl_parser.cpp */; settings = {COMPILER_FLAGS = ""; }; }; 61FF3C34FBEC4819A1C30F92 /* sample_text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ECCEBFFA6977404F858F9739 /* sample_text.cpp */; settings = {COMPILER_FLAGS = ""; }; }; - 8C2AAE0A1CB338A8000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; settings = {ASSET_TAGS = (); }; }; - 8C2AAE0B1CB338CD000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; settings = {ASSET_TAGS = (); }; }; - 8C2AAE0C1CB338CE000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; settings = {ASSET_TAGS = (); }; }; + 8C2AAE0A1CB338A8000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; }; + 8C2AAE0B1CB338CD000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; }; + 8C2AAE0C1CB338CE000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; }; 8C303C591975D6A700D7C1C5 /* idl_gen_go.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */; }; + 8C547D661D3FF05C00AE7A25 /* idl_gen_grpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C547D651D3FF05C00AE7A25 /* idl_gen_grpc.cpp */; }; + 8C547D681D3FF07D00AE7A25 /* cpp_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8C547D671D3FF07D00AE7A25 /* cpp_generator.cc */; }; 8C6905FD19F835B400CB8866 /* idl_gen_fbs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */; }; 8C78573E1BD5AE2C00C53C34 /* idl_gen_js.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C78573D1BD5AE2C00C53C34 /* idl_gen_js.cpp */; }; 8C8774631B703D4800E693F5 /* reflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C8774621B703D4800E693F5 /* reflection.cpp */; }; @@ -43,6 +45,8 @@ 6AD24EEB3D024825A37741FF /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test.cpp; path = tests/test.cpp; sourceTree = SOURCE_ROOT; }; 8C2AAE091CB338A8000CC78D /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = util.cpp; path = src/util.cpp; sourceTree = "<group>"; }; 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_go.cpp; path = src/idl_gen_go.cpp; sourceTree = "<group>"; }; + 8C547D651D3FF05C00AE7A25 /* idl_gen_grpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_grpc.cpp; path = src/idl_gen_grpc.cpp; sourceTree = "<group>"; }; + 8C547D671D3FF07D00AE7A25 /* cpp_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cpp_generator.cc; path = grpc/src/compiler/cpp_generator.cc; sourceTree = "<group>"; }; 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_fbs.cpp; path = src/idl_gen_fbs.cpp; sourceTree = "<group>"; }; 8C78573D1BD5AE2C00C53C34 /* idl_gen_js.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_js.cpp; path = src/idl_gen_js.cpp; sourceTree = "<group>"; }; 8C8774621B703D4800E693F5 /* reflection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = reflection.cpp; path = src/reflection.cpp; sourceTree = "<group>"; }; @@ -62,6 +66,8 @@ 28237E300FE042DEADA302D3 /* Source Files */ = { isa = PBXGroup; children = ( + 8C547D671D3FF07D00AE7A25 /* cpp_generator.cc */, + 8C547D651D3FF05C00AE7A25 /* idl_gen_grpc.cpp */, 8C2AAE091CB338A8000CC78D /* util.cpp */, D2DA271C1BFFBC06000F9168 /* idl_gen_php.cpp */, 8C78573D1BD5AE2C00C53C34 /* idl_gen_js.cpp */, @@ -267,12 +273,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8C547D681D3FF07D00AE7A25 /* cpp_generator.cc in Sources */, 8C303C591975D6A700D7C1C5 /* idl_gen_go.cpp in Sources */, AA9BACF55EB3456BA2F633BB /* flatc.cpp in Sources */, BE03D7B0C9584DD58B50ED34 /* idl_gen_cpp.cpp in Sources */, AD71FEBEE4E846529002C1F0 /* idl_gen_text.cpp in Sources */, 8C2AAE0A1CB338A8000CC78D /* util.cpp in Sources */, 8C8774641B703E1200E693F5 /* idl_gen_fbs.cpp in Sources */, + 8C547D661D3FF05C00AE7A25 /* idl_gen_grpc.cpp in Sources */, A9C9A99F719A4ED58DC2D2FC /* idl_parser.cpp in Sources */, 8CA854B31B04244A00040A06 /* idl_gen_python.cpp in Sources */, 8CD8717B19CB937D0012A827 /* idl_gen_general.cpp in Sources */, @@ -715,7 +723,10 @@ GCC_OPTIMIZATION_LEVEL = 3; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ( @@ -754,7 +765,10 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = " -std=c++0x"; @@ -787,7 +801,10 @@ GCC_OPTIMIZATION_LEVEL = 2; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ( @@ -919,7 +936,10 @@ GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ( diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 886fbd9b..b71b8c46 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -33,6 +33,8 @@ For any schema input files, one or more generators can be specified: - `--php`: Generate PHP code. +- `--grpc`: Generate RPC stub code for GRPC. + For any data input files: - `--binary`, `-b` : If data is contained in this file, generate a @@ -79,6 +81,11 @@ Additional options: - `--gen-mutable` : Generate additional non-const accessors for mutating FlatBuffers in-place. + `--gen-object-api` : Generate an additional object-based API. This API is + more convenient for object construction and mutation than the base API, + at the cost of efficiency (object allocation). Recommended only to be used + if other options are insufficient. + - `--gen-onefile` : Generate single output file (useful for C#) - `--gen-all`: Generate not just code for the current schema files, but @@ -101,5 +108,9 @@ Additional options: to the reflection/reflection.fbs schema. Loading this binary file is the basis for reflection functionality. +- `--conform FILE` : Specify a schema the following schemas should be + an evolution of. Gives errors if not. Useful to check if schema + modifications don't break schema evolution rules. + 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 d4abe66e..060432b0 100755 --- a/docs/source/CppUsage.md +++ b/docs/source/CppUsage.md @@ -85,6 +85,27 @@ convenient accessors for all fields, e.g. `hp()`, `mana()`, etc: *Note: That we never stored a `mana` value, so it will return the default.* +## Object based API. + +FlatBuffers is all about memory efficiency, which is why its base API is written +around using as little as possible of it. This does make the API clumsier +(requiring pre-order construction of all data, and making mutation harder). + +For times when efficiency is less important a more convenient object based API +can be used (through `--gen-object-api`) that is able to unpack & pack a +FlatBuffer into objects and standard STL containers, allowing for convenient +construction, access and mutation. + +To use: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + auto monsterobj = GetMonster(buffer)->UnPack(); + cout << monsterobj->name; // This is now a std::string! + monsterobj->name = "Bob"; // Change the name. + FlatBufferBuilder fbb; + monsterobj->Pack(fbb); // Serialize into new buffer. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ## Reflection (& Resizing) There is experimental support for reflection in FlatBuffers, allowing you to diff --git a/docs/source/GoUsage.md b/docs/source/GoUsage.md index a3a0e928..ab6ddbd8 100644 --- a/docs/source/GoUsage.md +++ b/docs/source/GoUsage.md @@ -67,6 +67,29 @@ Now you can access values like this: pos := monster.Pos(nil) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases it's necessary to modify values in an existing FlatBuffer in place (without creating a copy). For this reason, scalar fields of a Flatbuffer table or struct can be mutated. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go} + monster := example.GetRootAsMonster(buf, 0) + + // Set table field. + if ok := monster.MutateHp(10); !ok { + panic("failed to mutate Hp") + } + + // Set struct field. + monster.Pos().MutateZ(4) + + // This mutation will fail because the mana field is not available in + // the buffer. It should be set when creating the buffer. + if ok := monster.MutateMana(20); !ok { + panic("failed to mutate Hp") + } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The term `mutate` is used instead of `set` to indicate that this is a special use case. All mutate functions return a boolean value which is false if the field we're trying to mutate is not available in the buffer. + ## Text Parsing There currently is no support for parsing text (Schema's and JSON) directly diff --git a/docs/source/Schemas.md b/docs/source/Schemas.md index 4004803d..5bcaccb3 100755 --- a/docs/source/Schemas.md +++ b/docs/source/Schemas.md @@ -237,7 +237,8 @@ as the response (both of which must be table types): } What code this produces and how it is used depends on language and RPC system -used, FlatBuffers itself does not offer this functionality. +used, there is preliminary support for GRPC through the `--grpc` code generator, +see `grpc/tests` for an example. ### Comments & documentation @@ -271,7 +272,7 @@ Current understood attributes: the union field should have id 8, and the unions type field will implicitly be 7. IDs allow the fields to be placed in any order in the schema. - When a new field is added to the schema is must use the next available ID. + When a new field is added to the schema it must use the next available ID. - `deprecated` (on a field): do not generate accessors for this field anymore, code should stop using this data. - `required` (on a non-scalar table field): this field must always be set. @@ -333,6 +334,10 @@ JSON: - A field that has the value `null` (e.g. `field: null`) is intended to have the default value for that field (thus has the same effect as if that field wasn't specified at all). +- It has some built in conversion functions, so you can write for example + `rad(180)` where ever you'd normally write `3.14159`. + Currently supports the following functions: `rad`, `deg`, `cos`, `sin`, + `tan`, `acos`, `asin`, `atan`. When parsing JSON, it recognizes the following escape codes in strings: diff --git a/docs/source/Tutorial.md b/docs/source/Tutorial.md index 1e48796d..100191d7 100644 --- a/docs/source/Tutorial.md +++ b/docs/source/Tutorial.md @@ -727,8 +727,8 @@ offsets. // Place the two weapons into an array, and pass it to the `createWeaponsVector()` method to // create a FlatBuffer vector. int[] weaps = new int[2]; - weaps[1] = sword; - weaps[2] = axe; + weaps[0] = sword; + weaps[1] = axe; // Pass the `weaps` array into the `createWeaponsVector()` method to create a FlatBuffer vector. int weapons = Monster.createWeaponsVector(builder, weaps); @@ -790,6 +790,14 @@ offsets. ~~~ </div> +<div class="language-cpp"> +<br> +Note there's additional convenience overloads of `CreateVector`, allowing you +to work with data that's not in a `std::vector`, or allowing you to generate +elements by calling a lambda. For the common case of `std::vector<std::string>` +there's also `CreateVectorOfStrings`. +</div> + To create a `struct`, use the `Vec3` class/struct that was generated by the schema compiler: @@ -1075,7 +1083,7 @@ Here is a repetition these lines, to help highlight them more clearly: <div class="language-c"> ~~~{.c} // Add union type and data simultanously. - ns(Monster_equipped_Weapon_add(B, axe)); + ns(Monster_equipped_Weapon_add(B, axe)); ~~~ </div> @@ -1873,6 +1881,9 @@ One way to solve this is to call `ForceDefaults` on a FlatBufferBuilder to force all fields you set to actually be written. This, of course, increases the size of the buffer somewhat, but this may be acceptable for a mutable buffer. +If this is not sufficient, other ways of mutating FlatBuffers may be supported +in your language through an object based API (`--gen-object-api`) or reflection. +See the individual language documents for support. ## JSON with FlatBuffers diff --git a/go/table.go b/go/table.go index 976a7dba..01d3db12 100644 --- a/go/table.go +++ b/go/table.go @@ -293,3 +293,213 @@ func (t *Table) GetVOffsetTSlot(slot VOffsetT, d VOffsetT) VOffsetT { } return VOffsetT(off) } + +// MutateBool updates a bool at the given offset. +func (t *Table) MutateBool(off UOffsetT, n bool) bool { + WriteBool(t.Bytes[off:], n) + return true +} + +// MutateByte updates a Byte at the given offset. +func (t *Table) MutateByte(off UOffsetT, n byte) bool { + WriteByte(t.Bytes[off:], n) + return true +} + +// MutateUint8 updates a Uint8 at the given offset. +func (t *Table) MutateUint8(off UOffsetT, n uint8) bool { + WriteUint8(t.Bytes[off:], n) + return true +} + +// MutateUint16 updates a Uint16 at the given offset. +func (t *Table) MutateUint16(off UOffsetT, n uint16) bool { + WriteUint16(t.Bytes[off:], n) + return true +} + +// MutateUint32 updates a Uint32 at the given offset. +func (t *Table) MutateUint32(off UOffsetT, n uint32) bool { + WriteUint32(t.Bytes[off:], n) + return true +} + +// MutateUint64 updates a Uint64 at the given offset. +func (t *Table) MutateUint64(off UOffsetT, n uint64) bool { + WriteUint64(t.Bytes[off:], n) + return true +} + +// MutateInt8 updates a Int8 at the given offset. +func (t *Table) MutateInt8(off UOffsetT, n int8) bool { + WriteInt8(t.Bytes[off:], n) + return true +} + +// MutateInt16 updates a Int16 at the given offset. +func (t *Table) MutateInt16(off UOffsetT, n int16) bool { + WriteInt16(t.Bytes[off:], n) + return true +} + +// MutateInt32 updates a Int32 at the given offset. +func (t *Table) MutateInt32(off UOffsetT, n int32) bool { + WriteInt32(t.Bytes[off:], n) + return true +} + +// MutateInt64 updates a Int64 at the given offset. +func (t *Table) MutateInt64(off UOffsetT, n int64) bool { + WriteInt64(t.Bytes[off:], n) + return true +} + +// MutateFloat32 updates a Float32 at the given offset. +func (t *Table) MutateFloat32(off UOffsetT, n float32) bool { + WriteFloat32(t.Bytes[off:], n) + return true +} + +// MutateFloat64 updates a Float64 at the given offset. +func (t *Table) MutateFloat64(off UOffsetT, n float64) bool { + WriteFloat64(t.Bytes[off:], n) + return true +} + +// MutateUOffsetT updates a UOffsetT at the given offset. +func (t *Table) MutateUOffsetT(off UOffsetT, n UOffsetT) bool { + WriteUOffsetT(t.Bytes[off:], n) + return true +} + +// MutateVOffsetT updates a VOffsetT at the given offset. +func (t *Table) MutateVOffsetT(off UOffsetT, n VOffsetT) bool { + WriteVOffsetT(t.Bytes[off:], n) + return true +} + +// MutateSOffsetT updates a SOffsetT at the given offset. +func (t *Table) MutateSOffsetT(off UOffsetT, n SOffsetT) bool { + WriteSOffsetT(t.Bytes[off:], n) + return true +} + +// MutateBoolSlot updates the bool at given vtable location +func (t *Table) MutateBoolSlot(slot VOffsetT, n bool) bool { + if off := t.Offset(slot); off != 0 { + t.MutateBool(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateByteSlot updates the byte at given vtable location +func (t *Table) MutateByteSlot(slot VOffsetT, n byte) bool { + if off := t.Offset(slot); off != 0 { + t.MutateByte(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt8Slot updates the int8 at given vtable location +func (t *Table) MutateInt8Slot(slot VOffsetT, n int8) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt8(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint8Slot updates the uint8 at given vtable location +func (t *Table) MutateUint8Slot(slot VOffsetT, n uint8) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint8(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt16Slot updates the int16 at given vtable location +func (t *Table) MutateInt16Slot(slot VOffsetT, n int16) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt16(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint16Slot updates the uint16 at given vtable location +func (t *Table) MutateUint16Slot(slot VOffsetT, n uint16) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint16(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt32Slot updates the int32 at given vtable location +func (t *Table) MutateInt32Slot(slot VOffsetT, n int32) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt32(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint32Slot updates the uint32 at given vtable location +func (t *Table) MutateUint32Slot(slot VOffsetT, n uint32) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint32(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt64Slot updates the int64 at given vtable location +func (t *Table) MutateInt64Slot(slot VOffsetT, n int64) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt64(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint64Slot updates the uint64 at given vtable location +func (t *Table) MutateUint64Slot(slot VOffsetT, n uint64) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint64(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateFloat32Slot updates the float32 at given vtable location +func (t *Table) MutateFloat32Slot(slot VOffsetT, n float32) bool { + if off := t.Offset(slot); off != 0 { + t.MutateFloat32(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateFloat64Slot updates the float64 at given vtable location +func (t *Table) MutateFloat64Slot(slot VOffsetT, n float64) bool { + if off := t.Offset(slot); off != 0 { + t.MutateFloat64(t.Pos+UOffsetT(off), n) + return true + } + + return false +} diff --git a/grpc/README.md b/grpc/README.md new file mode 100644 index 00000000..13485198 --- /dev/null +++ b/grpc/README.md @@ -0,0 +1,11 @@ +GRPC implementation and test +============================ + +NOTE: files in `src/` are shared with the GRPC project, and maintained there +(any changes should be submitted to GRPC instead). These files are copied +from GRPC, and work with both the Protobuf and FlatBuffers code generator. + +`tests/` contains a GRPC specific test, you need to have built and installed +the GRPC libraries for this to compile. This test will build using the +`FLATBUFFERS_BUILD_GRPCTEST` option to the main FlatBuffers CMake project. + diff --git a/grpc/src/compiler/cpp_generator.cc b/grpc/src/compiler/cpp_generator.cc new file mode 100644 index 00000000..9319c419 --- /dev/null +++ b/grpc/src/compiler/cpp_generator.cc @@ -0,0 +1,1202 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <map> + +#include "src/compiler/cpp_generator.h" + +#include <sstream> + +namespace grpc_cpp_generator { +namespace { + +template <class T> +grpc::string as_string(T x) { + std::ostringstream out; + out << x; + return out.str(); +} + +grpc::string FilenameIdentifier(const grpc::string &filename) { + grpc::string result; + for (unsigned i = 0; i < filename.size(); i++) { + char c = filename[i]; + if (isalnum(c)) { + result.push_back(c); + } else { + static char hex[] = "0123456789abcdef"; + result.push_back('_'); + result.push_back(hex[(c >> 4) & 0xf]); + result.push_back(hex[c & 0xf]); + } + } + return result; +} +} // namespace + +template<class T, size_t N> +T *array_end(T (&array)[N]) { return array + N; } + +void PrintIncludes(Printer *printer, const std::vector<grpc::string>& headers, const Parameters ¶ms) { + std::map<grpc::string, grpc::string> vars; + + vars["l"] = params.use_system_headers ? '<' : '"'; + vars["r"] = params.use_system_headers ? '>' : '"'; + + if (!params.grpc_search_path.empty()) { + vars["l"] += params.grpc_search_path; + if (params.grpc_search_path.back() != '/') { + vars["l"] += '/'; + } + } + + for (auto i = headers.begin(); i != headers.end(); i++) { + vars["h"] = *i; + printer->Print(vars, "#include $l$$h$$r$\n"); + } +} + +grpc::string GetHeaderPrologue(File *file, const Parameters & /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + + vars["filename"] = file->filename(); + vars["filename_identifier"] = FilenameIdentifier(file->filename()); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = file->message_header_ext(); + + printer->Print(vars, "// Generated by the gRPC protobuf plugin.\n"); + printer->Print(vars, + "// If you make any local change, they will be lost.\n"); + printer->Print(vars, "// source: $filename$\n"); + printer->Print(vars, "#ifndef GRPC_$filename_identifier$__INCLUDED\n"); + printer->Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n"); + printer->Print(vars, "\n"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, "\n"); + } + return output; +} + +grpc::string GetHeaderIncludes(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + + static const char *headers_strs[] = { + "grpc++/impl/codegen/async_stream.h", + "grpc++/impl/codegen/async_unary_call.h", + "grpc++/impl/codegen/proto_utils.h", + "grpc++/impl/codegen/rpc_method.h", + "grpc++/impl/codegen/service_type.h", + "grpc++/impl/codegen/status.h", + "grpc++/impl/codegen/stub_options.h", + "grpc++/impl/codegen/sync_stream.h" + }; + std::vector<grpc::string> headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params); + printer->Print(vars, "\n"); + printer->Print(vars, "namespace grpc {\n"); + printer->Print(vars, "class CompletionQueue;\n"); + printer->Print(vars, "class Channel;\n"); + printer->Print(vars, "class RpcService;\n"); + printer->Print(vars, "class ServerCompletionQueue;\n"); + printer->Print(vars, "class ServerContext;\n"); + printer->Print(vars, "} // namespace grpc\n\n"); + + if (!file->package().empty()) { + std::vector<grpc::string> parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer->Print(vars, "namespace $part$ {\n"); + } + printer->Print(vars, "\n"); + } + } + return output; +} + +void PrintHeaderClientMethodInterfaces( + Printer *printer, const Method *method, + std::map<grpc::string, grpc::string> *vars, bool is_public) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + if (is_public) { + if (method->NoStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::Status $Method$(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) = 0;\n"); + printer->Print(*vars, + "std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>>(" + "Async$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>" + " $Method$(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>" + "($Method$Raw(context, response));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncWriterInterface< $Request$>>" + " Async$Method$(::grpc::ClientContext* context, $Response$* " + "response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncWriterInterface< $Request$>>(" + "Async$Method$Raw(context, response, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>" + " $Method$(::grpc::ClientContext* context, const $Request$& request)" + " {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>" + "($Method$Raw(context, request));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderInterface< $Response$>> " + "Async$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderInterface< $Response$>>(" + "Async$Method$Raw(context, request, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientReaderWriterInterface< " + "$Request$, $Response$>> " + "$Method$(::grpc::ClientContext* context) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientReaderWriterInterface< $Request$, $Response$>>(" + "$Method$Raw(context));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>>(" + "Async$Method$Raw(context, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else { + if (method->NoStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncResponseReaderInterface< $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) = 0;\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::ClientWriterInterface< $Request$>*" + " $Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) = 0;\n"); + printer->Print(*vars, + "virtual ::grpc::ClientAsyncWriterInterface< $Request$>*" + " Async$Method$Raw(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::ClientReaderInterface< $Response$>* $Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request) = 0;\n"); + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncReaderInterface< $Response$>* " + "Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "virtual ::grpc::ClientReaderWriterInterface< $Request$, " + "$Response$>* " + "$Method$Raw(::grpc::ClientContext* context) = 0;\n"); + printer->Print(*vars, + "virtual ::grpc::ClientAsyncReaderWriterInterface< " + "$Request$, $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } + } +} + +void PrintHeaderClientMethod(Printer *printer, + const Method *method, + std::map<grpc::string, grpc::string> *vars, + bool is_public) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (is_public) { + if (method->NoStreaming()) { + printer->Print( + *vars, + "::grpc::Status $Method$(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncResponseReader< $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReader< $Response$>>(" + "Async$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientWriter< $Request$>>" + " $Method$(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< ::grpc::ClientWriter< $Request$>>" + "($Method$Raw(context, response));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>" + " Async$Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>(" + "Async$Method$Raw(context, response, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReader< $Response$>>" + " $Method$(::grpc::ClientContext* context, const $Request$& request)" + " {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientReader< $Response$>>" + "($Method$Raw(context, request));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> " + "Async$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>>(" + "Async$Method$Raw(context, request, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReaderWriter< $Request$, $Response$>>" + " $Method$(::grpc::ClientContext* context) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientReaderWriter< $Request$, $Response$>>(" + "$Method$Raw(context));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderWriter< " + "$Request$, $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>>(" + "Async$Method$Raw(context, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else { + if (method->NoStreaming()) { + printer->Print(*vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) GRPC_OVERRIDE;\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "::grpc::ClientWriter< $Request$>* $Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) " + "GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncWriter< $Request$>* Async$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print(*vars, + "::grpc::ClientReader< $Response$>* $Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request)" + " GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReader< $Response$>* Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$Method$Raw(::grpc::ClientContext* context) GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } + } +} + +void PrintHeaderClientMethodData(Printer *printer, const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n"); +} + +void PrintHeaderServerMethodSync(Printer *printer, const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response);\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response);\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer);\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream);" + "\n"); + } +} + +void PrintHeaderServerMethodAsync( + Printer *printer, + const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + printer->Print(*vars, "template <class BaseClass>\n"); + printer->Print(*vars, + "class WithAsyncMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithAsyncMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodAsync($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithAsyncMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + if (method->NoStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, $Request$* request, " + "::grpc::ServerAsyncResponseWriter< $Response$>* response, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncUnary($Idx$, context, " + "request, response, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerAsyncReader< $Response$, $Request$>* reader, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncClientStreaming($Idx$, " + "context, reader, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, $Request$* request, " + "::grpc::ServerAsyncWriter< $Response$>* writer, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print( + *vars, + " ::grpc::Service::RequestAsyncServerStreaming($Idx$, " + "context, request, writer, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + "GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncBidiStreaming($Idx$, " + "context, stream, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderServerMethodGeneric( + Printer *printer, + const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + printer->Print(*vars, "template <class BaseClass>\n"); + printer->Print(*vars, + "class WithGenericMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithGenericMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodGeneric($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithGenericMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + if (method->NoStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + "GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderService(Printer *printer, + const Service *service, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "class $Service$ GRPC_FINAL {\n" + " public:\n"); + printer->Indent(); + + // Client side + printer->Print( + "class StubInterface {\n" + " public:\n"); + printer->Indent(); + printer->Print("virtual ~StubInterface() {}\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars, true); + } + printer->Outdent(); + printer->Print("private:\n"); + printer->Indent(); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars, false); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print( + "class Stub GRPC_FINAL : public StubInterface" + " {\n public:\n"); + printer->Indent(); + printer->Print("Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i).get(), vars, true); + } + printer->Outdent(); + printer->Print("\n private:\n"); + printer->Indent(); + printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i).get(), vars, false); + } + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodData(printer, service->method(i).get(), vars); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print( + "static std::unique_ptr<Stub> NewStub(const std::shared_ptr< " + "::grpc::ChannelInterface>& channel, " + "const ::grpc::StubOptions& options = ::grpc::StubOptions());\n"); + + printer->Print("\n"); + + // Server side - base + printer->Print( + "class Service : public ::grpc::Service {\n" + " public:\n"); + printer->Indent(); + printer->Print("Service();\n"); + printer->Print("virtual ~Service();\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderServerMethodSync(printer, service->method(i).get(), vars); + } + printer->Outdent(); + printer->Print("};\n"); + + // Server side - Asynchronous + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodAsync(printer, service->method(i).get(), vars); + } + + printer->Print("typedef "); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i).get()->name(); + printer->Print(*vars, "WithAsyncMethod_$method_name$<"); + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + printer->Print(" >"); + } + printer->Print(" AsyncService;\n"); + + // Server side - Generic + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodGeneric(printer, service->method(i).get(), vars); + } + + printer->Outdent(); + printer->Print("};\n"); +} + +grpc::string GetHeaderServices(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + // Package string is empty or ends with a dot. It is used to fully qualify + // method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } + + if (!params.services_namespace.empty()) { + vars["services_namespace"] = params.services_namespace; + printer->Print(vars, "\nnamespace $services_namespace$ {\n\n"); + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintHeaderService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); + } + + if (!params.services_namespace.empty()) { + printer->Print(vars, "} // namespace $services_namespace$\n\n"); + } + } + return output; +} + +grpc::string GetHeaderEpilogue(File *file, const Parameters & /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + + vars["filename"] = file->filename(); + vars["filename_identifier"] = FilenameIdentifier(file->filename()); + + if (!file->package().empty()) { + std::vector<grpc::string> parts = file->package_parts(); + + for (auto part = parts.rbegin(); part != parts.rend(); part++) { + vars["part"] = *part; + printer->Print(vars, "} // namespace $part$\n"); + } + printer->Print(vars, "\n"); + } + + printer->Print(vars, "\n"); + printer->Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); + } + return output; +} + +grpc::string GetSourcePrologue(File *file, const Parameters & /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + + vars["filename"] = file->filename(); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = file->message_header_ext(); + vars["service_header_ext"] = file->service_header_ext(); + + printer->Print(vars, "// Generated by the gRPC protobuf plugin.\n"); + printer->Print(vars, + "// If you make any local change, they will be lost.\n"); + printer->Print(vars, "// source: $filename$\n\n"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n"); + printer->Print(vars, file->additional_headers().c_str()); + printer->Print(vars, "\n"); + } + return output; +} + +grpc::string GetSourceIncludes(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + + static const char *headers_strs[] = { + "grpc++/impl/codegen/async_stream.h", + "grpc++/impl/codegen/async_unary_call.h", + "grpc++/impl/codegen/channel_interface.h", + "grpc++/impl/codegen/client_unary_call.h", + "grpc++/impl/codegen/method_handler_impl.h", + "grpc++/impl/codegen/rpc_service_method.h", + "grpc++/impl/codegen/service_type.h", + "grpc++/impl/codegen/sync_stream.h" + }; + std::vector<grpc::string> headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params); + + if (!file->package().empty()) { + std::vector<grpc::string> parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer->Print(vars, "namespace $part$ {\n"); + } + } + + printer->Print(vars, "\n"); + } + return output; +} + +void PrintSourceClientMethod(Printer *printer, + const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Stub::$Method$(" + "::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) {\n"); + printer->Print(*vars, + " return ::grpc::BlockingUnaryCall(channel_.get(), " + "rpcmethod_$Method$_, " + "context, request, response);\n" + "}\n\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Print(*vars, + " return new " + "::grpc::ClientAsyncResponseReader< $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "::grpc::ClientWriter< $Request$>* " + "$ns$$Service$::Stub::$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientWriter< $Request$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, response);\n" + "}\n\n"); + printer->Print(*vars, + "::grpc::ClientAsyncWriter< $Request$>* " + "$ns$$Service$::Stub::Async$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientAsyncWriter< $Request$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, response, tag);\n" + "}\n\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "::grpc::ClientReader< $Response$>* " + "$ns$$Service$::Stub::$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientReader< $Response$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + printer->Print(*vars, + "::grpc::ClientAsyncReader< $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientAsyncReader< $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request, tag);\n" + "}\n\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::$Method$Raw(::grpc::ClientContext* context) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientReaderWriter< " + "$Request$, $Response$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context);\n" + "}\n\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, tag);\n" + "}\n\n"); + } +} + +void PrintSourceServerMethod(Printer *printer, + const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "const $Request$* request, $Response$* response) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) request;\n"); + printer->Print(" (void) response;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) reader;\n"); + printer->Print(" (void) response;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) request;\n"); + printer->Print(" (void) writer;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* " + "stream) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) stream;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } +} + +void PrintSourceService(Printer *printer, + const Service *service, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "static const char* $prefix$$Service$_method_names[] = {\n"); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Method"] = service->method(i).get()->name(); + printer->Print(*vars, " \"/$Package$$Service$/$Method$\",\n"); + } + printer->Print(*vars, "};\n\n"); + + printer->Print(*vars, + "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub(" + "const std::shared_ptr< ::grpc::ChannelInterface>& channel, " + "const ::grpc::StubOptions& options) {\n" + " std::unique_ptr< $ns$$Service$::Stub> stub(new " + "$ns$$Service$::Stub(channel));\n" + " return stub;\n" + "}\n\n"); + printer->Print(*vars, + "$ns$$Service$::Stub::Stub(const std::shared_ptr< " + "::grpc::ChannelInterface>& channel)\n"); + printer->Indent(); + printer->Print(": channel_(channel)"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + (*vars)["Method"] = method->name(); + (*vars)["Idx"] = as_string(i); + if (method->NoStreaming()) { + (*vars)["StreamingType"] = "NORMAL_RPC"; + } else if (method->ClientOnlyStreaming()) { + (*vars)["StreamingType"] = "CLIENT_STREAMING"; + } else if (method->ServerOnlyStreaming()) { + (*vars)["StreamingType"] = "SERVER_STREAMING"; + } else { + (*vars)["StreamingType"] = "BIDI_STREAMING"; + } + printer->Print(*vars, + ", rpcmethod_$Method$_(" + "$prefix$$Service$_method_names[$Idx$], " + "::grpc::RpcMethod::$StreamingType$, " + "channel" + ")\n"); + } + printer->Print("{}\n\n"); + printer->Outdent(); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintSourceClientMethod(printer, service->method(i).get(), vars); + } + + printer->Print(*vars, "$ns$$Service$::Service::Service() {\n"); + printer->Indent(); + printer->Print(*vars, "(void)$prefix$$Service$_method_names;\n"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + (*vars)["Idx"] = as_string(i); + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::NORMAL_RPC,\n" + " new ::grpc::RpcMethodHandler< $ns$$Service$::Service, " + "$Request$, " + "$Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::CLIENT_STREAMING,\n" + " new ::grpc::ClientStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::SERVER_STREAMING,\n" + " new ::grpc::ServerStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::BIDI_STREAMING,\n" + " new ::grpc::BidiStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } + } + printer->Outdent(); + printer->Print(*vars, "}\n\n"); + printer->Print(*vars, + "$ns$$Service$::Service::~Service() {\n" + "}\n\n"); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintSourceServerMethod(printer, service->method(i).get(), vars); + } +} + +grpc::string GetSourceServices(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map<grpc::string, grpc::string> vars; + // Package string is empty or ends with a dot. It is used to fully qualify + // method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } + if (!params.services_namespace.empty()) { + vars["ns"] = params.services_namespace + "::"; + vars["prefix"] = params.services_namespace; + } else { + vars["ns"] = ""; + vars["prefix"] = ""; + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintSourceService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); + } + } + return output; +} + +grpc::string GetSourceEpilogue(File *file, const Parameters & /*params*/) { + grpc::string temp; + + if (!file->package().empty()) { + std::vector<grpc::string> parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + temp.append("} // namespace "); + temp.append(*part); + temp.append("\n"); + } + temp.append("\n"); + } + + return temp; +} + +} // namespace grpc_cpp_generator diff --git a/grpc/src/compiler/cpp_generator.h b/grpc/src/compiler/cpp_generator.h new file mode 100644 index 00000000..953ddfd5 --- /dev/null +++ b/grpc/src/compiler/cpp_generator.h @@ -0,0 +1,147 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H + +// cpp_generator.h/.cc do not directly depend on GRPC/ProtoBuf, such that they +// can be used to generate code for other serialization systems, such as +// FlatBuffers. + +#include <memory> +#include <vector> + +#ifndef GRPC_CUSTOM_STRING +#include <string> +#define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +namespace grpc_cpp_generator { + +// Contains all the parameters that are parsed from the command line. +struct Parameters { + // Puts the service into a namespace + grpc::string services_namespace; + // Use system includes (<>) or local includes ("") + bool use_system_headers; + // Prefix to any grpc include + grpc::string grpc_search_path; +}; + +// An abstract interface representing a method. +struct Method { + virtual ~Method() {} + + virtual grpc::string name() const = 0; + + virtual grpc::string input_type_name() const = 0; + virtual grpc::string output_type_name() const = 0; + + virtual bool NoStreaming() const = 0; + virtual bool ClientOnlyStreaming() const = 0; + virtual bool ServerOnlyStreaming() const = 0; + virtual bool BidiStreaming() const = 0; +}; + +// An abstract interface representing a service. +struct Service { + virtual ~Service() {} + + virtual grpc::string name() const = 0; + + virtual int method_count() const = 0; + virtual std::unique_ptr<const Method> method(int i) const = 0; +}; + +struct Printer { + virtual ~Printer() {} + + virtual void Print(const std::map<grpc::string, grpc::string> &vars, + const char *template_string) = 0; + virtual void Print(const char *string) = 0; + virtual void Indent() = 0; + virtual void Outdent() = 0; +}; + +// An interface that allows the source generated to be output using various +// libraries/idls/serializers. +struct File { + virtual ~File() {} + + virtual grpc::string filename() const = 0; + virtual grpc::string filename_without_ext() const = 0; + virtual grpc::string message_header_ext() const = 0; + virtual grpc::string service_header_ext() const = 0; + virtual grpc::string package() const = 0; + virtual std::vector<grpc::string> package_parts() const = 0; + virtual grpc::string additional_headers() const = 0; + + virtual int service_count() const = 0; + virtual std::unique_ptr<const Service> service(int i) const = 0; + + virtual std::unique_ptr<Printer> CreatePrinter(grpc::string *str) const = 0; +}; + +// Return the prologue of the generated header file. +grpc::string GetHeaderPrologue(File *file, const Parameters ¶ms); + +// Return the includes needed for generated header file. +grpc::string GetHeaderIncludes(File *file, const Parameters ¶ms); + +// Return the includes needed for generated source file. +grpc::string GetSourceIncludes(File *file, const Parameters ¶ms); + +// Return the epilogue of the generated header file. +grpc::string GetHeaderEpilogue(File *file, const Parameters ¶ms); + +// Return the prologue of the generated source file. +grpc::string GetSourcePrologue(File *file, const Parameters ¶ms); + +// Return the services for generated header file. +grpc::string GetHeaderServices(File *file, const Parameters ¶ms); + +// Return the services for generated source file. +grpc::string GetSourceServices(File *file, const Parameters ¶ms); + +// Return the epilogue of the generated source file. +grpc::string GetSourceEpilogue(File *file, const Parameters ¶ms); + +} // namespace grpc_cpp_generator + +#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H diff --git a/grpc/tests/grpctest.cpp b/grpc/tests/grpctest.cpp new file mode 100644 index 00000000..d1b53776 --- /dev/null +++ b/grpc/tests/grpctest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2014 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. + */ + +#include <thread> + +#include <grpc++/grpc++.h> + +#include "monster_test_generated.h" +#include "monster_test.grpc.fb.h" + +using namespace MyGame::Example; + +// The callback implementation of our server, that derives from the generated +// code. It implements all rpcs specified in the FlatBuffers schema. +class ServiceImpl final : public MyGame::Example::MonsterStorage::Service { + virtual ::grpc::Status Store(::grpc::ServerContext* context, + const flatbuffers::BufferRef<Monster> *request, + flatbuffers::BufferRef<Stat> *response) + override { + // Create a response from the incoming request name. + fbb_.Clear(); + auto stat_offset = CreateStat(fbb_, fbb_.CreateString("Hello, " + + request->GetRoot()->name()->str())); + fbb_.Finish(stat_offset); + // Since we keep reusing the same FlatBufferBuilder, the memory it owns + // remains valid until the next call (this BufferRef doesn't own the + // memory it points to). + *response = flatbuffers::BufferRef<Stat>(fbb_.GetBufferPointer(), + fbb_.GetSize()); + return grpc::Status::OK; + } + virtual ::grpc::Status Retrieve(::grpc::ServerContext *context, + const flatbuffers::BufferRef<Stat> *request, + flatbuffers::BufferRef<Monster> *response) + override { + assert(false); // We're not actually using this RPC. + return grpc::Status::CANCELLED; + } + + private: + flatbuffers::FlatBufferBuilder fbb_; +}; + +// Track the server instance, so we can terminate it later. +grpc::Server *server_instance = nullptr; +// Mutex to protec this variable. +std::mutex wait_for_server; +std::condition_variable server_instance_cv; + +// This function implements the server thread. +void RunServer() { + auto server_address = "0.0.0.0:50051"; + // Callback interface we implemented above. + ServiceImpl service; + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + + // Start the server. Lock to change the variable we're changing. + wait_for_server.lock(); + server_instance = builder.BuildAndStart().release(); + wait_for_server.unlock(); + server_instance_cv.notify_one(); + + std::cout << "Server listening on " << server_address << std::endl; + // This will block the thread and serve requests. + server_instance->Wait(); +} + +int main(int /*argc*/, const char * /*argv*/[]) { + // Launch server. + std::thread server_thread(RunServer); + + // wait for server to spin up. + std::unique_lock<std::mutex> lock(wait_for_server); + while (!server_instance) server_instance_cv.wait(lock); + + // Now connect the client. + auto channel = grpc::CreateChannel("localhost:50051", + grpc::InsecureChannelCredentials()); + auto stub = MyGame::Example::MonsterStorage::NewStub(channel); + + grpc::ClientContext context; + + // Build a request with the name set. + flatbuffers::FlatBufferBuilder fbb; + auto monster_offset = CreateMonster(fbb, 0, 0, 0, fbb.CreateString("Fred")); + fbb.Finish(monster_offset); + auto request = flatbuffers::BufferRef<Monster>(fbb.GetBufferPointer(), + fbb.GetSize()); + flatbuffers::BufferRef<Stat> response; + + // The actual RPC. + auto status = stub->Store(&context, request, &response); + + if (status.ok()) { + auto resp = response.GetRoot()->id(); + std::cout << "RPC response: " << resp->str() << std::endl; + } else { + std::cout << "RPC failed" << std::endl; + } + + server_instance->Shutdown(); + + server_thread.join(); + + delete server_instance; + + return 0; +} + diff --git a/include/flatbuffers/code_generators.h b/include/flatbuffers/code_generators.h index 95fa0c1a..8b097ef7 100644 --- a/include/flatbuffers/code_generators.h +++ b/include/flatbuffers/code_generators.h @@ -24,11 +24,12 @@ class BaseGenerator { virtual bool generate() = 0; static const std::string NamespaceDir(const Parser &parser, - const std::string &path) { + const std::string &path, + const Namespace &ns) { EnsureDirExists(path.c_str()); if (parser.opts.one_file) return path; std::string namespace_dir = path; // Either empty or ends in separator. - auto &namespaces = parser.namespaces_.back()->components; + auto &namespaces = ns.components; for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { namespace_dir += *it + kPathSeparator; EnsureDirExists(namespace_dir.c_str()); @@ -38,17 +39,24 @@ class BaseGenerator { protected: BaseGenerator(const Parser &parser, const std::string &path, - const std::string &file_name) + const std::string &file_name, + const std::string qualifying_start, + const std::string qualifying_separator) : parser_(parser), path_(path), file_name_(file_name), - namespace_dir_(BaseGenerator::NamespaceDir(parser, path)){}; + qualifying_start_(qualifying_start), + qualifying_separator_(qualifying_separator){}; virtual ~BaseGenerator(){}; // No copy/assign. BaseGenerator &operator=(const BaseGenerator &); BaseGenerator(const BaseGenerator &); + const std::string NamespaceDir(const Namespace &ns) { + return BaseGenerator::NamespaceDir(parser_, path_, ns); + } + const char *FlatBuffersGeneratedWarning() { return "automatically generated by the FlatBuffers compiler," " do not modify\n\n"; @@ -66,9 +74,9 @@ class BaseGenerator { return true; } - std::string FullNamespace(const char *separator) { + std::string FullNamespace(const char *separator, const Namespace &ns) { std::string namespace_name; - auto &namespaces = parser_.namespaces_.back()->components; + auto &namespaces = ns.components; for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { if (namespace_name.length()) namespace_name += separator; namespace_name += *it; @@ -76,15 +84,39 @@ class BaseGenerator { return namespace_name; } - const std::string LastNamespacePart() { - auto &namespaces = parser_.namespaces_.back()->components; - if (namespaces.size()) return *(namespaces.end() - 1); else return std::string(""); + const std::string LastNamespacePart(const Namespace &ns) { + auto &namespaces = ns.components; + if (namespaces.size()) + return *(namespaces.end() - 1); + else + return std::string(""); + } + + // tracks the current namespace for early exit in WrapInNameSpace + // c++, java and csharp returns a different namespace from + // the following default (no early exit, always fully qualify), + // which works for js and php + virtual const Namespace *CurrentNameSpace() { return nullptr; } + + // Ensure that a type is prefixed with its namespace whenever it is used + // outside of its namespace. + std::string WrapInNameSpace(const Namespace *ns, const std::string &name) { + if (CurrentNameSpace() == ns) return name; + std::string qualified_name = qualifying_start_; + for (auto it = ns->components.begin(); it != ns->components.end(); ++it) + qualified_name += *it + qualifying_separator_; + return qualified_name + name; + } + + std::string WrapInNameSpace(const Definition &def) { + return WrapInNameSpace(def.defined_namespace, def.name); } const Parser &parser_; const std::string &path_; const std::string &file_name_; - const std::string namespace_dir_; + const std::string qualifying_start_; + const std::string qualifying_separator_; }; } // namespace flatbuffers diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index ff83da6d..8cab3cd3 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -24,28 +24,36 @@ #include <cstdlib> #include <cstring> #include <string> +#include <utility> #include <type_traits> #include <vector> #include <set> #include <algorithm> -#include <functional> #include <memory> +#ifdef _STLPORT_VERSION + #define FLATBUFFERS_CPP98_STL +#endif +#ifndef FLATBUFFERS_CPP98_STL + #include <functional> +#endif + /// @cond FLATBUFFERS_INTERNAL #if __cplusplus <= 199711L && \ (!defined(_MSC_VER) || _MSC_VER < 1600) && \ (!defined(__GNUC__) || \ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400)) - #error A C++11 compatible compiler with support for the auto typing is required for FlatBuffers. + #error A C++11 compatible compiler with support for the auto typing is \ + required for FlatBuffers. #error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ #endif #if !defined(__clang__) && \ defined(__GNUC__) && \ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600) - // Backwards compatability for g++ 4.4, and 4.5 which don't have the nullptr and constexpr - // keywords. Note the __clang__ check is needed, because clang presents itself as an older GNUC - // compiler. + // Backwards compatability for g++ 4.4, and 4.5 which don't have the nullptr + // and constexpr keywords. Note the __clang__ check is needed, because clang + // presents itself as an older GNUC compiler. #ifndef nullptr_t const class nullptr_t { public: @@ -122,9 +130,11 @@ typedef uintmax_t largest_scalar_t; // In 32bits, this evaluates to 2GB - 1 #define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(soffset_t) * 8 - 1)) - 1) +#ifndef FLATBUFFERS_CPP98_STL // Pointer to relinquished memory. typedef std::unique_ptr<uint8_t, std::function<void(uint8_t * /* unused */)>> unique_ptr_t; +#endif // Wrapper for uoffset_t to allow safe template specialization. template<typename T> struct Offset { @@ -233,23 +243,19 @@ template<typename T> struct IndirectHelper<const T *> { // An STL compatible iterator implementation for Vector below, effectively // calling Get() for every element. -template<typename T, bool bConst> -struct VectorIterator : public - std::iterator < std::input_iterator_tag, - typename std::conditional < bConst, - const typename IndirectHelper<T>::return_type, - typename IndirectHelper<T>::return_type > ::type, uoffset_t > { - - typedef std::iterator<std::input_iterator_tag, - typename std::conditional<bConst, - const typename IndirectHelper<T>::return_type, - typename IndirectHelper<T>::return_type>::type, uoffset_t> super_type; +template<typename T, typename IT> +struct VectorIterator + : public std::iterator<std::input_iterator_tag, IT, uoffset_t> { + + typedef std::iterator<std::input_iterator_tag, IT, uoffset_t> super_type; public: VectorIterator(const uint8_t *data, uoffset_t i) : data_(data + IndirectHelper<T>::element_stride * i) {}; VectorIterator(const VectorIterator &other) : data_(other.data_) {} + #ifndef FLATBUFFERS_CPP98_STL VectorIterator(VectorIterator &&other) : data_(std::move(other.data_)) {} + #endif VectorIterator &operator=(const VectorIterator &other) { data_ = other.data_; @@ -287,7 +293,7 @@ public: } VectorIterator operator++(int) { - VectorIterator temp(data_); + VectorIterator temp(data_,0); data_ += IndirectHelper<T>::element_stride; return temp; } @@ -300,8 +306,10 @@ private: // Vector::data() assumes the vector elements start after the length field. template<typename T> class Vector { public: - typedef VectorIterator<T, false> iterator; - typedef VectorIterator<T, true> const_iterator; + typedef VectorIterator<T, typename IndirectHelper<T>::mutable_return_type> + iterator; + typedef VectorIterator<T, typename IndirectHelper<T>::return_type> + const_iterator; uoffset_t size() const { return EndianScalar(length_); } @@ -470,6 +478,7 @@ class vector_downward { cur_ = buf_ + reserved_; } + #ifndef FLATBUFFERS_CPP98_STL // Relinquish the pointer to the caller. unique_ptr_t release() { // Actually deallocate from the start of the allocated memory. @@ -485,6 +494,7 @@ class vector_downward { return retval; } + #endif size_t growth_policy(size_t bytes) { return (bytes / 2) & ~(sizeof(largest_scalar_t) - 1); @@ -561,6 +571,10 @@ inline voffset_t FieldIndexToOffset(voffset_t field_id) { inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { return ((~buf_size) + 1) & (scalar_size - 1); } + +template <typename T> const T* data(const std::vector<T> &v) { + return v.empty() ? nullptr : &v.front(); +} /// @endcond /// @addtogroup flatbuffers_cpp_api @@ -626,6 +640,7 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a `uint8_t` pointer to the unfinished buffer. uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } + #ifndef FLATBUFFERS_CPP98_STL /// @brief Get the released pointer to the serialized buffer. /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! /// @return The `unique_ptr` returned has a special allocator that knows how @@ -636,6 +651,7 @@ FLATBUFFERS_FINAL_CLASS Finished(); return buf_.release(); } + #endif /// @cond FLATBUFFERS_INTERNAL void Finished() const { @@ -673,11 +689,13 @@ FLATBUFFERS_FINAL_CLASS void PopBytes(size_t amount) { buf_.pop(amount); } template<typename T> void AssertScalarT() { + #ifndef FLATBUFFERS_CPP98_STL // The code assumes power of 2 sizes and endian-swap-ability. static_assert(std::is_scalar<T>::value // The Offset<T> type is essentially a scalar but fails is_scalar. || sizeof(T) == sizeof(Offset<void>), "T must be a scalar type"); + #endif } // Write a single aligned scalar to the buffer @@ -875,7 +893,7 @@ FLATBUFFERS_FINAL_CLASS /// @param[in] str A const pointer to a `String` struct to add to the buffer. /// @return Returns the offset in the buffer where the string starts Offset<String> CreateString(const String *str) { - return CreateString(str->c_str(), str->Length()); + return str ? CreateString(str->c_str(), str->Length()) : 0; } /// @brief Store a string in the buffer, which can contain any binary data. @@ -980,7 +998,47 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template<typename T> Offset<Vector<T>> CreateVector(const std::vector<T> &v) { - return CreateVector(v.data(), v.size()); + return CreateVector(data(v), v.size()); + } + + // vector<bool> may be implemented using a bit-set, so we can't access it as + // an array. Instead, read elements manually. + // Background: https://isocpp.org/blog/2012/11/on-vectorbool + Offset<Vector<uint8_t>> CreateVector(const std::vector<bool> &v) { + StartVector(v.size(), sizeof(uint8_t)); + for (auto i = v.size(); i > 0; ) { + PushElement(static_cast<uint8_t>(v[--i])); + } + return Offset<Vector<uint8_t>>(EndVector(v.size())); + } + + #ifndef FLATBUFFERS_CPP98_STL + /// @brief Serialize values returned by a function into a FlatBuffer `vector`. + /// This is a convenience function that takes care of iteration for you. + /// @tparam T The data type of the `std::vector` elements. + /// @param f A function that takes the current iteration 0..vector_size-1 and + /// returns any type that you can construct a FlatBuffers vector out of. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template<typename T> Offset<Vector<T>> CreateVector(size_t vector_size, + const std::function<T (size_t i)> &f) { + std::vector<T> elems(vector_size); + for (size_t i = 0; i < vector_size; i++) elems[i] = f(i); + return CreateVector(elems); + } + #endif + + /// @brief Serialize a `std::vector<std::string>` into a FlatBuffer `vector`. + /// This is a convenience function for a common case. + /// @param v A const reference to the `std::vector` to serialize into the + /// buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + Offset<Vector<Offset<String>>> CreateVectorOfStrings( + const std::vector<std::string> &v) { + std::vector<Offset<String>> offsets(v.size()); + for (size_t i = 0; i < v.size(); i++) offsets[i] = CreateString(v[i]); + return CreateVector(offsets); } /// @brief Serialize an array of structs into a FlatBuffer `vector`. @@ -991,7 +1049,7 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template<typename T> Offset<Vector<const T *>> CreateVectorOfStructs( - const T *v, size_t len) { + const T *v, size_t len) { StartVector(len * sizeof(T) / AlignOf<T>(), AlignOf<T>()); PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len); return Offset<Vector<const T *>>(EndVector(len)); @@ -1004,8 +1062,8 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template<typename T> Offset<Vector<const T *>> CreateVectorOfStructs( - const std::vector<T> &v) { - return CreateVectorOfStructs(v.data(), v.size()); + const std::vector<T> &v) { + return CreateVectorOfStructs(data(v), v.size()); } /// @cond FLATBUFFERS_INTERNAL @@ -1033,7 +1091,7 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template<typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables( - Offset<T> *v, size_t len) { + Offset<T> *v, size_t len) { std::sort(v, v + len, TableKeyComparator<T>(buf_)); return CreateVector(v, len); } @@ -1046,7 +1104,7 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template<typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables( - std::vector<Offset<T>> *v) { + std::vector<Offset<T>> *v) { return CreateVectorOfSortedTables(v->data(), v->size()); } @@ -1077,7 +1135,7 @@ FLATBUFFERS_FINAL_CLASS /// written to at a later time to serialize the data into a `vector` /// in the buffer. template<typename T> Offset<Vector<T>> CreateUninitializedVector( - size_t len, T **buf) { + size_t len, T **buf) { return CreateUninitializedVector(len, sizeof(T), reinterpret_cast<uint8_t **>(buf)); } @@ -1162,13 +1220,16 @@ template<typename T> const T *GetRoot(const void *buf) { } /// Helpers to get a typed pointer to objects that are currently beeing built. -/// @warning Creating new objects will lead to reallocations and invalidates the pointer! -template<typename T> T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) { +/// @warning Creating new objects will lead to reallocations and invalidates +/// the pointer! +template<typename T> T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, + Offset<T> offset) { return reinterpret_cast<T *>(fbb.GetCurrentBufferPointer() + fbb.GetSize() - offset.o); } -template<typename T> const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) { +template<typename T> const T *GetTemporaryPointer(FlatBufferBuilder &fbb, + Offset<T> offset) { return GetMutableTemporaryPointer<T>(fbb, offset); } @@ -1185,6 +1246,9 @@ class Verifier FLATBUFFERS_FINAL_CLASS { size_t _max_tables = 1000000) : buf_(buf), end_(buf + buf_len), depth_(0), max_depth_(_max_depth), num_tables_(0), max_tables_(_max_tables) + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + , upper_bound_(buf) + #endif {} // Central location where any verification failures register. @@ -1192,11 +1256,20 @@ class Verifier FLATBUFFERS_FINAL_CLASS { #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE assert(ok); #endif + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + if (!ok) + upper_bound_ = buf_; + #endif return ok; } // Verify any range within the buffer. bool Verify(const void *elem, size_t elem_len) const { + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + auto upper_bound = reinterpret_cast<const uint8_t *>(elem) + elem_len; + if (upper_bound_ < upper_bound) + upper_bound_ = upper_bound; + #endif return Check(elem_len <= (size_t) (end_ - buf_) && elem >= buf_ && elem <= end_ - elem_len); @@ -1271,11 +1344,20 @@ class Verifier FLATBUFFERS_FINAL_CLASS { } // Verify this whole buffer, starting with root type T. - template<typename T> bool VerifyBuffer() { + template<typename T> bool VerifyBuffer(const char *identifier) { + if (identifier && (size_t(end_ - buf_) < 2 * sizeof(flatbuffers::uoffset_t) || + !BufferHasIdentifier(buf_, identifier))) { + return false; + } + // Call T::Verify, which must be in the generated code for this type. return Verify<uoffset_t>(buf_) && reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))-> - Verify(*this); + Verify(*this) + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + && GetComputedSize() + #endif + ; } // Called at the start of a table to increase counters measuring data @@ -1294,6 +1376,16 @@ class Verifier FLATBUFFERS_FINAL_CLASS { return true; } + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + // Returns the message size in bytes + size_t GetComputedSize() const { + uintptr_t size = upper_bound_ - buf_; + // Align the size to uoffset_t + size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); + return (buf_ + size > end_) ? 0 : size; + } + #endif + private: const uint8_t *buf_; const uint8_t *end_; @@ -1301,6 +1393,32 @@ class Verifier FLATBUFFERS_FINAL_CLASS { size_t max_depth_; size_t num_tables_; size_t max_tables_; + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + mutable const uint8_t *upper_bound_; + #endif +}; + +// Convenient way to bundle a buffer and its length, to pass it around +// typed by its root. +// A BufferRef does not own its buffer. +struct BufferRefBase {}; // for std::is_base_of +template<typename T> struct BufferRef : BufferRefBase { + BufferRef() : buf(nullptr), len(0), must_free(false) {} + BufferRef(uint8_t *_buf, uoffset_t _len) + : buf(_buf), len(_len), must_free(false) {} + + ~BufferRef() { if (must_free) free(buf); } + + const T *GetRoot() const { return flatbuffers::GetRoot<T>(buf); } + + bool Verify() { + Verifier verifier(buf, len); + return verifier.VerifyBuffer<T>(); + } + + uint8_t *buf; + uoffset_t len; + bool must_free; }; // "structs" are flat structures that do not have an offset table, thus @@ -1435,6 +1553,12 @@ class Table { uint8_t data_[1]; }; +// Base class for native objects (FlatBuffer data de-serialized into native +// C++ data structures). +// Contains no functionality, purely documentative. +struct NativeTable { +}; + // Helper function to test if a field is present, using any of the field // enums in the generated code. // `table` must be a generated table type. Since this is a template parameter, diff --git a/include/flatbuffers/grpc.h b/include/flatbuffers/grpc.h new file mode 100644 index 00000000..52a64408 --- /dev/null +++ b/include/flatbuffers/grpc.h @@ -0,0 +1,72 @@ +/* + * Copyright 2014 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_GRPC_H_ +#define FLATBUFFERS_GRPC_H_ + +// Helper functionality to glue FlatBuffers and GRPC. + +#include "grpc++/support/byte_buffer.h" +#include "grpc/byte_buffer_reader.h" + +namespace grpc { + +template <class T> +class SerializationTraits<T, typename std::enable_if<std::is_base_of< + flatbuffers::BufferRefBase, T>::value>::type> { + public: + // The type we're passing here is a BufferRef, which is already serialized + // FlatBuffer data, which then gets passed to GRPC. + static grpc::Status Serialize(const T& msg, + grpc_byte_buffer **buffer, + bool *own_buffer) { + // TODO(wvo): make this work without copying. + auto slice = gpr_slice_from_copied_buffer( + reinterpret_cast<const char *>(msg.buf), msg.len); + *buffer = grpc_raw_byte_buffer_create(&slice, 1); + *own_buffer = true; + return grpc::Status(); + } + + // There is no de-serialization step in FlatBuffers, so we just receive + // the data from GRPC. + static grpc::Status Deserialize(grpc_byte_buffer *buffer, + T *msg, + int max_message_size) { + // TODO(wvo): make this more efficient / zero copy when possible. + auto len = grpc_byte_buffer_length(buffer); + msg->buf = reinterpret_cast<uint8_t *>(malloc(len)); + msg->len = static_cast<flatbuffers::uoffset_t>(len); + msg->must_free = true; + uint8_t *current = msg->buf; + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer); + gpr_slice slice; + while (grpc_byte_buffer_reader_next(&reader, &slice)) { + memcpy(current, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice)); + current += GPR_SLICE_LENGTH(slice); + gpr_slice_unref(slice); + } + GPR_ASSERT(current == msg->buf + msg->len); + grpc_byte_buffer_reader_destroy(&reader); + grpc_byte_buffer_destroy(buffer); + return grpc::Status(); + } +}; + +} // namespace grpc; + +#endif // FLATBUFFERS_GRPC_H_ diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index dc385efa..1f450162 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -311,6 +311,14 @@ struct EnumDef : public Definition { Type underlying_type; }; +inline bool EqualByName(const Type &a, const Type &b) { + return a.base_type == b.base_type && a.element == b.element && + (a.struct_def == b.struct_def || + a.struct_def->name == b.struct_def->name) && + (a.enum_def == b.enum_def || + a.enum_def->name == b.enum_def->name); +} + struct RPCCall { std::string name; SymbolTable<Value> attributes; @@ -337,6 +345,8 @@ struct IDLOptions { bool generate_all; bool skip_unexpected_fields_in_json; bool generate_name_strings; + bool escape_proto_identifiers; + bool generate_object_based_api; // Possible options for the more general generator below. enum Language { kJava, kCSharp, kGo, kMAX }; @@ -356,9 +366,24 @@ struct IDLOptions { generate_all(false), skip_unexpected_fields_in_json(false), generate_name_strings(false), + escape_proto_identifiers(false), + generate_object_based_api(false), lang(IDLOptions::kJava) {} }; +// This encapsulates where the parser is in the current source file. +struct ParserState { + ParserState() : cursor_(nullptr), line_(1), token_(-1) {} + + protected: + const char *cursor_; + int line_; // the current line being parsed + int token_; + + std::string attribute_; + std::vector<std::string> doc_comment_; +}; + // A way to make error propagation less error prone by requiring values to be // checked. // Once you create a value of this type you must either: @@ -400,14 +425,12 @@ class CheckedError { #define FLATBUFFERS_CHECKED_ERROR CheckedError #endif -class Parser { +class Parser : public ParserState { public: explicit Parser(const IDLOptions &options = IDLOptions()) : root_struct_def_(nullptr), opts(options), source_(nullptr), - cursor_(nullptr), - line_(1), anonymous_counter(0) { // Just in case none are declared: namespaces_.push_back(new Namespace()); @@ -421,7 +444,7 @@ class Parser { known_attributes_["original_order"] = true; known_attributes_["nested_flatbuffer"] = true; known_attributes_["csharp_partial"] = true; - known_attributes_["stream"] = true; + known_attributes_["streaming"] = true; known_attributes_["idempotent"] = true; } @@ -458,6 +481,10 @@ class Parser { // See reflection/reflection.fbs void Serialize(); + // Checks that the schema represented by this parser is a safe evolution + // of the schema provided. Returns non-empty error on any problems. + std::string ConformTo(const Parser &base); + FLATBUFFERS_CHECKED_ERROR CheckBitsFit(int64_t val, size_t bits); private: @@ -478,7 +505,8 @@ private: FieldDef **dest); FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def); FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field, - size_t parent_fieldn); + size_t parent_fieldn, + const StructDef *parent_struct_def); FLATBUFFERS_CHECKED_ERROR ParseTable(const StructDef &struct_def, std::string *value, uoffset_t *ovalue); void SerializeStruct(const StructDef &struct_def, const Value &val); @@ -538,13 +566,9 @@ private: IDLOptions opts; private: - const char *source_, *cursor_; - int line_; // the current line being parsed - int token_; - std::string file_being_parsed_; + const char *source_; - std::string attribute_; - std::vector<std::string> doc_comment_; + std::string file_being_parsed_; std::vector<std::pair<Value, FieldDef *>> field_stack_; @@ -671,6 +695,12 @@ extern std::string BinaryMakeRule(const Parser &parser, const std::string &path, const std::string &file_name); +// Generate GRPC interfaces. +// See idl_gen_grpc.cpp. +bool GenerateGRPC(const Parser &parser, + const std::string &path, + const std::string &file_name); + } // namespace flatbuffers #endif // FLATBUFFERS_IDL_H_ diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h index ababe6ad..ae331ce2 100644 --- a/include/flatbuffers/reflection.h +++ b/include/flatbuffers/reflection.h @@ -335,6 +335,8 @@ template<typename T, typename U> pointer_inside_vector<T, U> piv(T *ptr, return pointer_inside_vector<T, U>(ptr, vec); } +inline const char *UnionTypeFieldSuffix() { return "_type"; } + // Helper to figure out the actual table type a union refers to. inline const reflection::Object &GetUnionType( const reflection::Schema &schema, const reflection::Object &parent, @@ -342,7 +344,7 @@ inline const reflection::Object &GetUnionType( auto enumdef = schema.enums()->Get(unionfield.type()->index()); // TODO: this is clumsy and slow, but no other way to find it? auto type_field = parent.fields()->LookupByKey( - (unionfield.name()->str() + "_type").c_str()); + (unionfield.name()->str() + UnionTypeFieldSuffix()).c_str()); assert(type_field); auto union_type = GetFieldI<uint8_t>(table, *type_field); auto enumval = enumdef->values()->LookupByKey(union_type); @@ -368,6 +370,7 @@ uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize, uoffset_t elem_size, std::vector<uint8_t> *flatbuf, const reflection::Object *root_table = nullptr); +#ifndef FLATBUFFERS_CPP98_STL template <typename T> void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, const Vector<T> *vec, std::vector<uint8_t> *flatbuf, @@ -389,6 +392,7 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, } } } +#endif // Adds any new data (in the form of a new FlatBuffer) to an existing // FlatBuffer. This can be used when any of the above methods are not diff --git a/include/flatbuffers/reflection_generated.h b/include/flatbuffers/reflection_generated.h index da112493..eb981857 100644 --- a/include/flatbuffers/reflection_generated.h +++ b/include/flatbuffers/reflection_generated.h @@ -478,12 +478,12 @@ inline flatbuffers::Offset<Schema> CreateSchema(flatbuffers::FlatBufferBuilder & inline const reflection::Schema *GetSchema(const void *buf) { return flatbuffers::GetRoot<reflection::Schema>(buf); } -inline bool VerifySchemaBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<reflection::Schema>(); } - inline const char *SchemaIdentifier() { return "BFBS"; } inline bool SchemaBufferHasIdentifier(const void *buf) { return flatbuffers::BufferHasIdentifier(buf, SchemaIdentifier()); } +inline bool VerifySchemaBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<reflection::Schema>(SchemaIdentifier()); } + inline const char *SchemaExtension() { return "bfbs"; } inline void FinishSchemaBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<reflection::Schema> root) { fbb.Finish(root, SchemaIdentifier()); } diff --git a/java/com/google/flatbuffers/Constants.java b/java/com/google/flatbuffers/Constants.java index ac0593ae..f5906314 100644 --- a/java/com/google/flatbuffers/Constants.java +++ b/java/com/google/flatbuffers/Constants.java @@ -23,10 +23,18 @@ package com.google.flatbuffers; */ public class Constants { // Java doesn't seem to have these. + /** The number of bytes in an `byte`. */ + static final int SIZEOF_BYTE = 1; /** The number of bytes in a `short`. */ static final int SIZEOF_SHORT = 2; /** The number of bytes in an `int`. */ static final int SIZEOF_INT = 4; + /** The number of bytes in an `float`. */ + static final int SIZEOF_FLOAT = 4; + /** The number of bytes in an `long`. */ + static final int SIZEOF_LONG = 8; + /** The number of bytes in an `double`. */ + static final int SIZEOF_DOUBLE = 8; /** The number of bytes in a file identifier. */ static final int FILE_IDENTIFIER_LENGTH = 4; } diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java index fecb213f..c2186fa7 100644 --- a/java/com/google/flatbuffers/FlatBufferBuilder.java +++ b/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -188,7 +188,7 @@ public class FlatBufferBuilder { * * @param x A `boolean` to put into the buffer. */ - public void putBoolean(boolean x) { bb.put (space -= 1, (byte)(x ? 1 : 0)); } + public void putBoolean(boolean x) { bb.put (space -= Constants.SIZEOF_BYTE, (byte)(x ? 1 : 0)); } /** * Add a `byte` to the buffer, backwards from the current location. Doesn't align nor @@ -196,7 +196,7 @@ public class FlatBufferBuilder { * * @param x A `byte` to put into the buffer. */ - public void putByte (byte x) { bb.put (space -= 1, x); } + public void putByte (byte x) { bb.put (space -= Constants.SIZEOF_BYTE, x); } /** * Add a `short` to the buffer, backwards from the current location. Doesn't align nor @@ -204,7 +204,7 @@ public class FlatBufferBuilder { * * @param x A `short` to put into the buffer. */ - public void putShort (short x) { bb.putShort (space -= 2, x); } + public void putShort (short x) { bb.putShort (space -= Constants.SIZEOF_SHORT, x); } /** * Add an `int` to the buffer, backwards from the current location. Doesn't align nor @@ -212,7 +212,7 @@ public class FlatBufferBuilder { * * @param x An `int` to put into the buffer. */ - public void putInt (int x) { bb.putInt (space -= 4, x); } + public void putInt (int x) { bb.putInt (space -= Constants.SIZEOF_INT, x); } /** * Add a `long` to the buffer, backwards from the current location. Doesn't align nor @@ -220,7 +220,7 @@ public class FlatBufferBuilder { * * @param x A `long` to put into the buffer. */ - public void putLong (long x) { bb.putLong (space -= 8, x); } + public void putLong (long x) { bb.putLong (space -= Constants.SIZEOF_LONG, x); } /** * Add a `float` to the buffer, backwards from the current location. Doesn't align nor @@ -228,7 +228,7 @@ public class FlatBufferBuilder { * * @param x A `float` to put into the buffer. */ - public void putFloat (float x) { bb.putFloat (space -= 4, x); } + public void putFloat (float x) { bb.putFloat (space -= Constants.SIZEOF_FLOAT, x); } /** * Add a `double` to the buffer, backwards from the current location. Doesn't align nor @@ -236,7 +236,7 @@ public class FlatBufferBuilder { * * @param x A `double` to put into the buffer. */ - public void putDouble (double x) { bb.putDouble(space -= 8, x); } + public void putDouble (double x) { bb.putDouble(space -= Constants.SIZEOF_DOUBLE, x); } /// @endcond /** @@ -244,49 +244,49 @@ public class FlatBufferBuilder { * * @param x A `boolean` to put into the buffer. */ - public void addBoolean(boolean x) { prep(1, 0); putBoolean(x); } + public void addBoolean(boolean x) { prep(Constants.SIZEOF_BYTE, 0); putBoolean(x); } /** * Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary). * * @param x A `byte` to put into the buffer. */ - public void addByte (byte x) { prep(1, 0); putByte (x); } + public void addByte (byte x) { prep(Constants.SIZEOF_BYTE, 0); putByte (x); } /** * Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary). * * @param x A `short` to put into the buffer. */ - public void addShort (short x) { prep(2, 0); putShort (x); } + public void addShort (short x) { prep(Constants.SIZEOF_SHORT, 0); putShort (x); } /** * Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary). * * @param x An `int` to put into the buffer. */ - public void addInt (int x) { prep(4, 0); putInt (x); } + public void addInt (int x) { prep(Constants.SIZEOF_INT, 0); putInt (x); } /** * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary). * * @param x A `long` to put into the buffer. */ - public void addLong (long x) { prep(8, 0); putLong (x); } + public void addLong (long x) { prep(Constants.SIZEOF_LONG, 0); putLong (x); } /** * Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary). * * @param x A `float` to put into the buffer. */ - public void addFloat (float x) { prep(4, 0); putFloat (x); } + public void addFloat (float x) { prep(Constants.SIZEOF_FLOAT, 0); putFloat (x); } /** * Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary). * * @param x A `double` to put into the buffer. */ - public void addDouble (double x) { prep(8, 0); putDouble (x); } + public void addDouble (double x) { prep(Constants.SIZEOF_DOUBLE, 0); putDouble (x); } /** * Adds on offset, relative to where it will be written. diff --git a/php/FlatbufferBuilder.php b/php/FlatbufferBuilder.php index 3738582e..6f0ee483 100644 --- a/php/FlatbufferBuilder.php +++ b/php/FlatbufferBuilder.php @@ -233,7 +233,7 @@ class FlatbufferBuilder public function putUint($x) { if ($x > PHP_INT_MAX) { - throw new \InvalidArgumentException("your platform can't handling uint correctly. use 64bit machine."); + throw new \InvalidArgumentException("your platform can't handle uint correctly. use 64bit machine."); } $this->bb->putUint($this->space -= 4, $x); @@ -245,7 +245,7 @@ class FlatbufferBuilder public function putLong($x) { if ($x > PHP_INT_MAX) { - throw new \InvalidArgumentException("your platform can't handling long correctly. use 64bit machine."); + throw new \InvalidArgumentException("Your platform can't handle long correctly. Use a 64bit machine."); } $this->bb->putLong($this->space -= 8, $x); @@ -257,7 +257,7 @@ class FlatbufferBuilder public function putUlong($x) { if ($x > PHP_INT_MAX) { - throw new \InvalidArgumentException("your platform can't handling ulong correctly. this is php limitations. please wait extension release."); + throw new \InvalidArgumentException("Your platform can't handle ulong correctly. This is a php limitation. Please wait for the extension release."); } $this->bb->putUlong($this->space -= 8, $x); @@ -6,7 +6,7 @@ <groupId>com.google.flatbuffers</groupId> <artifactId>flatbuffers-java</artifactId> <version>1.3.0-SNAPSHOT</version> - <packaging>jar</packaging> + <packaging>bundle</packaging> <name>FlatBuffers Java API</name> <description> Memory Efficient Serialization Library @@ -33,7 +33,7 @@ <dependencies> </dependencies> <build> - <sourceDirectory>./</sourceDirectory> + <sourceDirectory>java</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> @@ -78,6 +78,12 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>3.0.1</version> + <extensions>true</extensions> + </plugin> </plugins> </build> </project> @@ -46,7 +46,7 @@ you would leave it in. <br> - [CONTRIBUTING]: http://github.com/google/flatbuffers/blob/master/CONTRIBUTING + [CONTRIBUTING]: http://github.com/google/flatbuffers/blob/master/CONTRIBUTING.md [`flatbuffers` tag]: https://stackoverflow.com/questions/tagged/flatbuffers [FlatBuffers Google Group]: https://groups.google.com/forum/#!forum/flatbuffers [FlatBuffers Issues Tracker]: http://github.com/google/flatbuffers/issues diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 1a16126a..636aa377 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -11,8 +11,10 @@ namespace Sample { struct Vec3; struct Monster; +struct MonsterT; struct Weapon; +struct WeaponT; enum Color { Color_Red = 0, @@ -36,6 +38,21 @@ enum Equipment { Equipment_MAX = Equipment_Weapon }; +struct EquipmentUnion { + Equipment type; + + flatbuffers::NativeTable *table; + EquipmentUnion() : type(Equipment_NONE), table(nullptr) {} + EquipmentUnion(const EquipmentUnion &); + EquipmentUnion &operator=(const EquipmentUnion &); + ~EquipmentUnion(); + + static flatbuffers::NativeTable *UnPack(const void *union_obj, Equipment type); + flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb) const; + + WeaponT *AsWeapon() { return type == Equipment_Weapon ? reinterpret_cast<WeaponT *>(table) : nullptr; } +}; + inline const char **EnumNamesEquipment() { static const char *names[] = { "NONE", "Weapon", nullptr }; return names; @@ -52,6 +69,8 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS { float z_; public: + Vec3() { memset(this, 0, sizeof(Vec3)); } + Vec3(const Vec3 &_o) { memcpy(this, &_o, sizeof(Vec3)); } Vec3(float _x, float _y, float _z) : x_(flatbuffers::EndianScalar(_x)), y_(flatbuffers::EndianScalar(_y)), z_(flatbuffers::EndianScalar(_z)) { } @@ -64,6 +83,17 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS { }; STRUCT_END(Vec3, 12); +struct MonsterT : public flatbuffers::NativeTable { + std::unique_ptr<Vec3> pos; + int16_t mana; + int16_t hp; + std::string name; + std::vector<uint8_t> inventory; + Color color; + std::vector<std::unique_ptr<WeaponT>> weapons; + EquipmentUnion equipped; +}; + struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_POS = 4, @@ -112,6 +142,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyEquipment(verifier, equipped(), equipped_type()) && verifier.EndTable(); } + std::unique_ptr<MonsterT> UnPack() const; }; struct MonsterBuilder { @@ -135,15 +166,15 @@ struct MonsterBuilder { }; inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, - const Vec3 *pos = 0, - int16_t mana = 150, - int16_t hp = 100, - flatbuffers::Offset<flatbuffers::String> name = 0, - flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory = 0, - Color color = Color_Blue, - flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Weapon>>> weapons = 0, - Equipment equipped_type = Equipment_NONE, - flatbuffers::Offset<void> equipped = 0) { + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + flatbuffers::Offset<flatbuffers::String> name = 0, + flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory = 0, + Color color = Color_Blue, + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Weapon>>> weapons = 0, + Equipment equipped_type = Equipment_NONE, + flatbuffers::Offset<void> equipped = 0) { MonsterBuilder builder_(_fbb); builder_.add_equipped(equipped); builder_.add_weapons(weapons); @@ -157,6 +188,26 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder return builder_.Finish(); } +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + const char *name = nullptr, + const std::vector<uint8_t> *inventory = nullptr, + Color color = Color_Blue, + const std::vector<flatbuffers::Offset<Weapon>> *weapons = nullptr, + Equipment equipped_type = Equipment_NONE, + flatbuffers::Offset<void> equipped = 0) { + return CreateMonster(_fbb, pos, mana, hp, name ? 0 : _fbb.CreateString(name), inventory ? 0 : _fbb.CreateVector<uint8_t>(*inventory), color, weapons ? 0 : _fbb.CreateVector<flatbuffers::Offset<Weapon>>(*weapons), equipped_type, equipped); +} + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o); + +struct WeaponT : public flatbuffers::NativeTable { + std::string name; + int16_t damage; +}; + struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_NAME = 4, @@ -173,6 +224,7 @@ struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyField<int16_t>(verifier, VT_DAMAGE) && verifier.EndTable(); } + std::unique_ptr<WeaponT> UnPack() const; }; struct WeaponBuilder { @@ -189,14 +241,62 @@ struct WeaponBuilder { }; inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset<flatbuffers::String> name = 0, - int16_t damage = 0) { + flatbuffers::Offset<flatbuffers::String> name = 0, + int16_t damage = 0) { WeaponBuilder builder_(_fbb); builder_.add_name(name); builder_.add_damage(damage); return builder_.Finish(); } +inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + int16_t damage = 0) { + return CreateWeapon(_fbb, name ? 0 : _fbb.CreateString(name), damage); +} + +inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, const WeaponT *_o); + +inline std::unique_ptr<MonsterT> Monster::UnPack() const { + auto _o = new MonsterT(); + { auto _e = pos(); if (_e) _o->pos = std::unique_ptr<Vec3>(new Vec3(*_e)); }; + { auto _e = mana(); _o->mana = _e; }; + { auto _e = hp(); _o->hp = _e; }; + { auto _e = name(); if (_e) _o->name = _e->str(); }; + { auto _e = inventory(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->inventory.push_back(_e->Get(_i)); } } }; + { auto _e = color(); _o->color = _e; }; + { auto _e = weapons(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->weapons.push_back(_e->Get(_i)->UnPack()); } } }; + { auto _e = equipped_type(); _o->equipped.type = _e; }; + { auto _e = equipped(); if (_e) _o->equipped.table = EquipmentUnion::UnPack(_e, equipped_type()); }; + return std::unique_ptr<MonsterT>(_o); +} + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) { + return CreateMonster(_fbb, + _o->pos ? _o->pos.get() : 0, + _o->mana, + _o->hp, + _o->name.size() ? _fbb.CreateString(_o->name) : 0, + _o->inventory.size() ? _fbb.CreateVector(_o->inventory) : 0, + _o->color, + _o->weapons.size() ? _fbb.CreateVector<flatbuffers::Offset<Weapon>>(_o->weapons.size(), [&](size_t i) { return CreateWeapon(_fbb, _o->weapons[i].get()); }) : 0, + _o->equipped.type, + _o->equipped.Pack(_fbb)); +} + +inline std::unique_ptr<WeaponT> Weapon::UnPack() const { + auto _o = new WeaponT(); + { auto _e = name(); if (_e) _o->name = _e->str(); }; + { auto _e = damage(); _o->damage = _e; }; + return std::unique_ptr<WeaponT>(_o); +} + +inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, const WeaponT *_o) { + return CreateWeapon(_fbb, + _o->name.size() ? _fbb.CreateString(_o->name) : 0, + _o->damage); +} + inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *union_obj, Equipment type) { switch (type) { case Equipment_NONE: return true; @@ -205,11 +305,34 @@ inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *union_o } } +inline flatbuffers::NativeTable *EquipmentUnion::UnPack(const void *union_obj, Equipment type) { + switch (type) { + case Equipment_NONE: return nullptr; + case Equipment_Weapon: return reinterpret_cast<const Weapon *>(union_obj)->UnPack().release(); + default: return nullptr; + } +} + +inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb) const { + switch (type) { + case Equipment_NONE: return 0; + case Equipment_Weapon: return CreateWeapon(_fbb, reinterpret_cast<const WeaponT *>(table)).Union(); + default: return 0; + } +} + +inline EquipmentUnion::~EquipmentUnion() { + switch (type) { + case Equipment_Weapon: delete reinterpret_cast<WeaponT *>(table); break; + default:; + } +} + inline const MyGame::Sample::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<MyGame::Sample::Monster>(buf); } inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot<Monster>(buf); } -inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<MyGame::Sample::Monster>(); } +inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<MyGame::Sample::Monster>(nullptr); } inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<MyGame::Sample::Monster> root) { fbb.Finish(root); } diff --git a/src/flatc.cpp b/src/flatc.cpp index d4be151c..a568bbb2 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -74,20 +74,24 @@ const Generator generators[] = { flatbuffers::IDLOptions::kMAX, "Generate Python files for tables/structs", flatbuffers::GeneralMakeRule }, - { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", + { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", flatbuffers::IDLOptions::kMAX, "Generate PHP files for tables/structs", flatbuffers::GeneralMakeRule }, + { flatbuffers::GenerateGRPC, nullptr, "--grpc", "GRPC", + flatbuffers::IDLOptions::kMAX, + "Generate GRPC interfaces", + flatbuffers::CPPMakeRule }, }; -const char *program_name = nullptr; -flatbuffers::Parser *parser = nullptr; +const char *g_program_name = nullptr; +flatbuffers::Parser *g_parser = nullptr; static void Error(const std::string &err, bool usage, bool show_exe_name) { - if (show_exe_name) printf("%s: ", program_name); + if (show_exe_name) printf("%s: ", g_program_name); printf("%s\n", err.c_str()); if (usage) { - printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", program_name); + printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", g_program_name); for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i) printf(" %-12s %s %s.\n", generators[i].generator_opt_long, @@ -116,25 +120,42 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) { " --no-includes Don\'t generate include statements for included\n" " schemas the generated file depends on (C++).\n" " --gen-mutable Generate accessors that can mutate buffers in-place.\n" - " --gen-onefile Generate single output file for C#\n" + " --gen-onefile Generate single output file for C#.\n" " --gen-name-strings Generate type name functions for C++.\n" + " --escape-proto-ids Disable appending '_' in namespaces names.\n" + " --gen-object-api Generate an additional object-based API\n" " --raw-binary Allow binaries without file_indentifier to be read.\n" " This may crash flatc given a mismatched schema.\n" " --proto Input is a .proto, translate to .fbs.\n" " --schema Serialize schemas instead of JSON (use with -b)\n" + " --conform FILE Specify a schema the following schemas should be\n" + " an evolution of. Gives errors if not.\n" "FILEs may be schemas, or JSON files (conforming to preceding schema)\n" "FILEs after the -- must be binary flatbuffer format files.\n" "Output files are named using the base file name of the input,\n" "and written to the current directory or the path given by -o.\n" "example: %s -c -b schema1.fbs schema2.fbs data.json\n", - program_name); + g_program_name); } - if (parser) delete parser; + if (g_parser) delete g_parser; exit(1); } +static void ParseFile(flatbuffers::Parser &parser, const std::string &filename, + const std::string &contents, + std::vector<const char *> &include_directories) { + auto local_include_directory = flatbuffers::StripFileName(filename); + include_directories.push_back(local_include_directory.c_str()); + include_directories.push_back(nullptr); + if (!parser.Parse(contents.c_str(), &include_directories[0], + filename.c_str())) + Error(parser.error_, false, false); + include_directories.pop_back(); + include_directories.pop_back(); +} + int main(int argc, const char *argv[]) { - program_name = argv[0]; + g_program_name = argv[0]; flatbuffers::IDLOptions opts; std::string output_path; const size_t num_generators = sizeof(generators) / sizeof(generators[0]); @@ -146,6 +167,7 @@ int main(int argc, const char *argv[]) { std::vector<std::string> filenames; std::vector<const char *> include_directories; size_t binary_files_from = std::numeric_limits<size_t>::max(); + std::string conform_to_schema; for (int argi = 1; argi < argc; argi++) { std::string arg = argv[argi]; if (arg[0] == '-') { @@ -157,6 +179,9 @@ int main(int argc, const char *argv[]) { } else if(arg == "-I") { if (++argi >= argc) Error("missing path following" + arg, true); include_directories.push_back(argv[argi]); + } else if(arg == "--conform") { + if (++argi >= argc) Error("missing path following" + arg, true); + conform_to_schema = argv[argi]; } else if(arg == "--strict-json") { opts.strict_json = true; } else if(arg == "--no-js-exports") { @@ -174,6 +199,8 @@ int main(int argc, const char *argv[]) { opts.mutable_buffer = true; } else if(arg == "--gen-name-strings") { opts.generate_name_strings = true; + } else if(arg == "--gen-object-api") { + opts.generate_object_based_api = true; } else if(arg == "--gen-all") { opts.generate_all = true; opts.include_dependence_headers = false; @@ -190,6 +217,8 @@ int main(int argc, const char *argv[]) { binary_files_from = filenames.size(); } else if(arg == "--proto") { opts.proto_mode = true; + } else if(arg == "--escape-proto-ids") { + opts.escape_proto_identifiers = true; } else if(arg == "--schema") { schema_binary = true; } else if(arg == "-M") { @@ -220,12 +249,20 @@ int main(int argc, const char *argv[]) { if (opts.proto_mode) { if (any_generator) Error("cannot generate code directly from .proto files", true); - } else if (!any_generator) { + } else if (!any_generator && conform_to_schema.empty()) { Error("no options: specify at least one generator.", true); } + flatbuffers::Parser conform_parser; + if (!conform_to_schema.empty()) { + std::string contents; + if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents)) + Error("unable to load schema: " + conform_to_schema); + ParseFile(conform_parser, conform_to_schema, contents, include_directories); + } + // Now process the files: - parser = new flatbuffers::Parser(opts); + g_parser = new flatbuffers::Parser(opts); for (auto file_it = filenames.begin(); file_it != filenames.end(); ++file_it) { @@ -236,8 +273,8 @@ int main(int argc, const char *argv[]) { bool is_binary = static_cast<size_t>(file_it - filenames.begin()) >= binary_files_from; if (is_binary) { - parser->builder_.Clear(); - parser->builder_.PushFlatBuffer( + g_parser->builder_.Clear(); + g_parser->builder_.PushFlatBuffer( reinterpret_cast<const uint8_t *>(contents.c_str()), contents.length()); if (!raw_binary) { @@ -246,17 +283,17 @@ int main(int argc, const char *argv[]) { // does not contain a file identifier. // We'd expect that typically any binary used as a file would have // such an identifier, so by default we require them to match. - if (!parser->file_identifier_.length()) { + if (!g_parser->file_identifier_.length()) { Error("current schema has no file_identifier: cannot test if \"" + *file_it + "\" matches the schema, use --raw-binary to read this file" " anyway."); } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(), - parser->file_identifier_.c_str())) { + g_parser->file_identifier_.c_str())) { Error("binary \"" + *file_it + "\" does not have expected file_identifier \"" + - parser->file_identifier_ + + g_parser->file_identifier_ + "\", use --raw-binary to read this file anyway."); } } @@ -265,36 +302,34 @@ int main(int argc, const char *argv[]) { if (contents.length() != strlen(contents.c_str())) { Error("input file appears to be binary: " + *file_it, true); } - if (flatbuffers::GetExtension(*file_it) == "fbs") { + auto is_schema = flatbuffers::GetExtension(*file_it) == "fbs"; + if (is_schema) { // If we're processing multiple schemas, make sure to start each // one from scratch. If it depends on previous schemas it must do // so explicitly using an include. - delete parser; - parser = new flatbuffers::Parser(opts); + delete g_parser; + g_parser = new flatbuffers::Parser(opts); + } + ParseFile(*g_parser, *file_it, contents, include_directories); + if (is_schema && !conform_to_schema.empty()) { + auto err = g_parser->ConformTo(conform_parser); + if (!err.empty()) Error("schemas don\'t conform: " + err); } - auto local_include_directory = flatbuffers::StripFileName(*file_it); - include_directories.push_back(local_include_directory.c_str()); - include_directories.push_back(nullptr); - if (!parser->Parse(contents.c_str(), &include_directories[0], - file_it->c_str())) - Error(parser->error_, false, false); if (schema_binary) { - parser->Serialize(); - parser->file_extension_ = reflection::SchemaExtension(); + g_parser->Serialize(); + g_parser->file_extension_ = reflection::SchemaExtension(); } - include_directories.pop_back(); - include_directories.pop_back(); } std::string filebase = flatbuffers::StripPath( flatbuffers::StripExtension(*file_it)); for (size_t i = 0; i < num_generators; ++i) { - parser->opts.lang = generators[i].lang; + g_parser->opts.lang = generators[i].lang; if (generator_enabled[i]) { if (!print_make_rules) { flatbuffers::EnsureDirExists(output_path); - if (!generators[i].generate(*parser, output_path, filebase)) { + if (!generators[i].generate(*g_parser, output_path, filebase)) { Error(std::string("Unable to generate ") + generators[i].lang_name + " for " + @@ -302,7 +337,7 @@ int main(int argc, const char *argv[]) { } } else { std::string make_rule = generators[i].make_rule( - *parser, output_path, *file_it); + *g_parser, output_path, *file_it); if (!make_rule.empty()) printf("%s\n", flatbuffers::WordWrap( make_rule, 80, " ", " \\").c_str()); @@ -310,13 +345,13 @@ int main(int argc, const char *argv[]) { } } - if (opts.proto_mode) GenerateFBS(*parser, output_path, filebase); + if (opts.proto_mode) GenerateFBS(*g_parser, output_path, filebase); // We do not want to generate code for the definitions in this file // in any files coming up next. - parser->MarkGenerated(); + g_parser->MarkGenerated(); } - delete parser; + delete g_parser; return 0; } diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index b1402d8e..ad20b36c 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -24,9 +24,7 @@ namespace flatbuffers { struct IsAlnum { - bool operator()(char c) { - return !isalnum(c); - } + bool operator()(char c) { return !isalnum(c); } }; static std::string GeneratedFileName(const std::string &path, @@ -39,7 +37,8 @@ class CppGenerator : public BaseGenerator { public: CppGenerator(const Parser &parser, const std::string &path, const std::string &file_name) - : BaseGenerator(parser, path, file_name){}; + : BaseGenerator(parser, path, file_name, "", "::"), + cur_name_space_(nullptr){}; // Iterate through all definitions we haven't generate code for (enums, // structs, // and tables) and output them to a single file. @@ -95,7 +94,11 @@ class CppGenerator : public BaseGenerator { auto &struct_def = **it; if (!struct_def.generated) { SetNameSpace(struct_def.defined_namespace, &code); - code += "struct " + struct_def.name + ";\n\n"; + code += "struct " + struct_def.name + ";\n"; + if (parser_.opts.generate_object_based_api && !struct_def.fixed) { + code += "struct " + NativeName(struct_def.name) + ";\n"; + } + code += "\n"; } } @@ -126,6 +129,14 @@ class CppGenerator : public BaseGenerator { GenTable(struct_def, &code); } } + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + if (!struct_def.fixed && !struct_def.generated) { + SetNameSpace(struct_def.defined_namespace, &code); + GenTablePost(struct_def, &code); + } + } // Generate code for union verifiers. for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); @@ -133,7 +144,7 @@ class CppGenerator : public BaseGenerator { auto &enum_def = **it; if (enum_def.is_union && !enum_def.generated) { SetNameSpace(enum_def.defined_namespace, &code); - GenEnumPost(enum_def, &code); + GenUnionPost(enum_def, &code); } } @@ -157,14 +168,6 @@ class CppGenerator : public BaseGenerator { code += name + ">(buf); }\n\n"; } - // The root verifier: - code += "inline bool Verify"; - code += name; - code += - "Buffer(flatbuffers::Verifier &verifier) { " - "return verifier.VerifyBuffer<"; - code += cpp_qualified_name + ">(); }\n\n"; - if (parser_.file_identifier_.length()) { // Return the identifier code += "inline const char *" + name; @@ -178,6 +181,19 @@ class CppGenerator : public BaseGenerator { code += name + "Identifier()); }\n\n"; } + // The root verifier: + code += "inline bool Verify"; + code += name; + code += + "Buffer(flatbuffers::Verifier &verifier) { " + "return verifier.VerifyBuffer<"; + code += cpp_qualified_name + ">("; + if (parser_.file_identifier_.length()) + code += name + "Identifier()"; + else + code += "nullptr"; + code += "); }\n\n"; + if (parser_.file_extension_.length()) { // Return the extension code += "inline const char *" + name; @@ -206,25 +222,9 @@ class CppGenerator : public BaseGenerator { private: // This tracks the current namespace so we can insert namespace declarations. - const Namespace *cur_name_space_ = nullptr; + const Namespace *cur_name_space_; - // Ensure that a type is prefixed with its namespace whenever it is used - // outside of its namespace. - std::string WrapInNameSpace(const Namespace *ns, const std::string &name) { - if (cur_name_space_ != ns) { - std::string qualified_name; - for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { - qualified_name += *it + "::"; - } - return qualified_name + name; - } else { - return name; - } - } - - std::string WrapInNameSpace(const Definition &def) { - return WrapInNameSpace(def.defined_namespace, def.name); - } + const Namespace *CurrentNameSpace() { return cur_name_space_; } // Translates a qualified name in flatbuffer text format to the same name in // the equivalent C++ namespace. @@ -241,9 +241,10 @@ class CppGenerator : public BaseGenerator { // Return a C++ type from the table in idl.h std::string GenTypeBasic(const Type &type, bool user_facing_type) { static const char *ctypename[] = { -#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) #CTYPE, + #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + #CTYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) -#undef FLATBUFFERS_TD + #undef FLATBUFFERS_TD }; if (user_facing_type) { if (type.enum_def) return WrapInNameSpace(*type.enum_def); @@ -261,9 +262,8 @@ class CppGenerator : public BaseGenerator { case BASE_TYPE_VECTOR: return "flatbuffers::Vector<" + GenTypeWire(type.VectorType(), "", false) + ">"; - case BASE_TYPE_STRUCT: { + case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def); - } case BASE_TYPE_UNION: // fall through default: @@ -273,30 +273,56 @@ class CppGenerator : public BaseGenerator { // Return a C++ type for any type (scalar/pointer) specifically for // building a flatbuffer. - std::string GenTypeWire(const Type &type, - const char *postfix, bool user_facing_type) { + std::string GenTypeWire(const Type &type, const char *postfix, + bool user_facing_type) { return IsScalar(type.base_type) ? GenTypeBasic(type, user_facing_type) + postfix - : IsStruct(type) - ? "const " + GenTypePointer(type) + " *" - : "flatbuffers::Offset<" + GenTypePointer(type) + - ">" + postfix; + : IsStruct(type) ? "const " + GenTypePointer(type) + " *" + : "flatbuffers::Offset<" + + GenTypePointer(type) + ">" + postfix; } // Return a C++ type for any type (scalar/pointer) that reflects its // serialized size. std::string GenTypeSize(const Type &type) { - return IsScalar(type.base_type) - ? GenTypeBasic(type, false) - : IsStruct(type) ? GenTypePointer(type) - : "flatbuffers::uoffset_t"; + return IsScalar(type.base_type) ? GenTypeBasic(type, false) + : IsStruct(type) ? GenTypePointer(type) + : "flatbuffers::uoffset_t"; + } + + // TODO(wvo): make this configurable. + std::string NativeName(const std::string &name) { return name + "T"; } + + std::string GenTypeNative(const Type &type, bool invector) { + switch (type.base_type) { + case BASE_TYPE_STRING: + return "std::string"; + case BASE_TYPE_VECTOR: + return "std::vector<" + GenTypeNative(type.VectorType(), true) + ">"; + case BASE_TYPE_STRUCT: + if (IsStruct(type)) { + if (invector) { + return WrapInNameSpace(*type.struct_def); + } else { + return "std::unique_ptr<" + + WrapInNameSpace(*type.struct_def) + ">"; + } + } else { + return "std::unique_ptr<" + + NativeName(WrapInNameSpace(*type.struct_def)) + ">"; + } + case BASE_TYPE_UNION: + return type.enum_def->name + "Union"; + default: + return GenTypeBasic(type, true); + } } // Return a C++ type for any type (scalar/pointer) specifically for // using a flatbuffer. - std::string GenTypeGet(const Type &type, - const char *afterbasic, const char *beforeptr, - const char *afterptr, bool user_facing_type) { + std::string GenTypeGet(const Type &type, const char *afterbasic, + const char *beforeptr, const char *afterptr, + bool user_facing_type) { return IsScalar(type.base_type) ? GenTypeBasic(type, user_facing_type) + afterbasic : beforeptr + GenTypePointer(type) + afterptr; @@ -325,12 +351,37 @@ class CppGenerator : public BaseGenerator { } } - std::string EnumSignature(EnumDef &enum_def) { + std::string UnionVerifySignature(EnumDef &enum_def) { return "inline bool Verify" + enum_def.name + - "(flatbuffers::Verifier &verifier, " + "const void *union_obj, " + + "(flatbuffers::Verifier &verifier, const void *union_obj, " + enum_def.name + " type)"; } + std::string UnionUnPackSignature(EnumDef &enum_def, bool inclass) { + return (inclass ? "static " : "") + + std::string("flatbuffers::NativeTable *") + + (inclass ? "" : enum_def.name + "Union::") + + "UnPack(const void *union_obj, " + enum_def.name + " type)"; + } + + std::string UnionPackSignature(EnumDef &enum_def, bool inclass) { + return "flatbuffers::Offset<void> " + + (inclass ? "" : enum_def.name + "Union::") + + "Pack(flatbuffers::FlatBufferBuilder &_fbb) const"; + } + + std::string TableCreateSignature(StructDef &struct_def) { + return "inline flatbuffers::Offset<" + struct_def.name + "> Create" + + struct_def.name + + "(flatbuffers::FlatBufferBuilder &_fbb, const " + + NativeName(struct_def.name) + " *_o)"; + } + + std::string TableUnPackSignature(StructDef &struct_def, bool inclass) { + return "std::unique_ptr<" + NativeName(struct_def.name) + "> " + + (inclass ? "" : struct_def.name + "::") + "UnPack() const"; + } + // Generate an enum declaration and an enum string lookup table. void GenEnum(EnumDef &enum_def, std::string *code_ptr) { std::string &code = *code_ptr; @@ -372,6 +423,36 @@ class CppGenerator : public BaseGenerator { GenTypeBasic(enum_def.underlying_type, false) + ")\n"; code += "\n"; + if (parser_.opts.generate_object_based_api && enum_def.is_union) { + // Generate a union type + code += "struct " + enum_def.name + "Union {\n"; + code += " " + enum_def.name + " type;\n\n"; + code += " flatbuffers::NativeTable *table;\n"; + code += " " + enum_def.name + "Union() : type("; + code += GenEnumVal(enum_def, "NONE", parser_.opts); + code += "), table(nullptr) {}\n"; + code += " " + enum_def.name + "Union(const "; + code += enum_def.name + "Union &);\n"; + code += " " + enum_def.name + "Union &operator=(const "; + code += enum_def.name + "Union &);\n"; + code += " ~" + enum_def.name + "Union();\n\n"; + code += " " + UnionUnPackSignature(enum_def, true) + ";\n"; + code += " " + UnionPackSignature(enum_def, true) + ";\n\n"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + if (ev.value) { + auto native_name = NativeName(WrapInNameSpace(*ev.struct_def)); + code += " " + native_name + " *As"; + code += ev.name + "() { return type == "; + code += GetEnumVal(enum_def, ev, parser_.opts); + code += " ? reinterpret_cast<" + native_name; + code += " *>(table) : nullptr; }\n"; + } + } + code += "};\n\n"; + } + // Generate a generate string table for enum values. // Problem is, if values are very sparse that could generate really big // tables. Ideally in that case we generate a map lookup instead, but for @@ -397,38 +478,89 @@ class CppGenerator : public BaseGenerator { code += "()[static_cast<int>(e)"; if (enum_def.vals.vec.front()->value) { code += " - static_cast<int>("; - code += - GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser_.opts) + ")"; + code += GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser_.opts) + + ")"; } code += "]; }\n\n"; } if (enum_def.is_union) { - code += EnumSignature(enum_def) + ";\n\n"; + code += UnionVerifySignature(enum_def) + ";\n\n"; } } - void GenEnumPost(EnumDef &enum_def, std::string *code_ptr_post) { + void GenUnionPost(EnumDef &enum_def, std::string *code_ptr) { // Generate a verifier function for this union that can be called by the // table verifier functions. It uses a switch case to select a specific // verifier function to call, this should be safe even if the union type // has been corrupted, since the verifiers will simply fail when called // on the wrong type. - std::string &code_post = *code_ptr_post; - code_post += EnumSignature(enum_def) + " {\n switch (type) {\n"; + std::string &code = *code_ptr; + code += UnionVerifySignature(enum_def) + " {\n switch (type) {\n"; for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); ++it) { auto &ev = **it; - code_post += " case " + GetEnumVal(enum_def, ev, parser_.opts); + code += " case " + GetEnumVal(enum_def, ev, parser_.opts); if (!ev.value) { - code_post += ": return true;\n"; // "NONE" enum value. + code += ": return true;\n"; // "NONE" enum value. } else { - code_post += ": return verifier.VerifyTable(reinterpret_cast<const "; - code_post += WrapInNameSpace(*ev.struct_def); - code_post += " *>(union_obj));\n"; + code += ": return verifier.VerifyTable(reinterpret_cast<const "; + code += WrapInNameSpace(*ev.struct_def); + code += " *>(union_obj));\n"; } } - code_post += " default: return false;\n }\n}\n\n"; + code += " default: return false;\n }\n}\n\n"; + + if (parser_.opts.generate_object_based_api) { + // Generate a union pack & unpack function. + code += "inline " + UnionUnPackSignature(enum_def, false); + code += " {\n switch (type) {\n"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + code += " case " + GetEnumVal(enum_def, ev, parser_.opts); + if (!ev.value) { + code += ": return nullptr;\n"; // "NONE" enum value. + } else { + code += ": return reinterpret_cast<const "; + code += WrapInNameSpace(*ev.struct_def); + code += " *>(union_obj)->UnPack().release();\n"; + } + } + code += " default: return nullptr;\n }\n}\n\n"; + code += "inline " + UnionPackSignature(enum_def, false); + code += " {\n switch (type) {\n"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + code += " case " + GetEnumVal(enum_def, ev, parser_.opts); + if (!ev.value) { + code += ": return 0;\n"; // "NONE" enum value. + } else { + code += ": return Create" + ev.struct_def->name; + code += "(_fbb, reinterpret_cast<const "; + code += NativeName(WrapInNameSpace(*ev.struct_def)); + code += " *>(table)).Union();\n"; + } + } + code += " default: return 0;\n }\n}\n\n"; + + // Generate a union destructor. + code += "inline " + enum_def.name + "Union::~"; + code += enum_def.name + "Union() {\n"; + code += " switch (type) {\n"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + if (ev.value) { + code += " case " + GenEnumVal(enum_def, ev.name, parser_.opts); + code += ": delete reinterpret_cast<"; + code += NativeName(WrapInNameSpace(*ev.struct_def)); + code += " *>(table); break;\n"; + } + } + code += " default:;\n }\n}\n\n"; + } } // Generates a value with optionally a cast applied if the field has a @@ -455,8 +587,7 @@ class CppGenerator : public BaseGenerator { return "VT_" + uname; } - void GenFullyQualifiedNameGetter(const std::string &name, - std::string &code) { + void GenFullyQualifiedNameGetter(const std::string &name, std::string &code) { if (parser_.opts.generate_name_strings) { code += " static FLATBUFFERS_CONSTEXPR const char *GetFullyQualifiedName() " @@ -473,9 +604,47 @@ class CppGenerator : public BaseGenerator { : field.value.constant; } + void GenSimpleParam(std::string &code, FieldDef &field) { + code += ",\n " + GenTypeWire(field.value.type, " ", true); + code += field.name + " = "; + if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) { + auto ev = field.value.type.enum_def->ReverseLookup( + static_cast<int>(StringToInt(field.value.constant.c_str())), false); + if (ev) { + code += WrapInNameSpace( + field.value.type.enum_def->defined_namespace, + GetEnumVal(*field.value.type.enum_def, *ev, parser_.opts)); + } else { + code += GenUnderlyingCast(field, true, field.value.constant); + } + } else if (field.value.type.base_type == BASE_TYPE_BOOL) { + code += field.value.constant == "0" ? "false" : "true"; + } else { + code += GenDefaultConstant(field); + } + } + // Generate an accessor struct, builder structs & function for a table. void GenTable(StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; + + if (parser_.opts.generate_object_based_api) { + // Generate a C++ object that can hold an unpacked version of this + // table. + code += "struct " + NativeName(struct_def.name); + code += " : public flatbuffers::NativeTable {\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated && // Deprecated fields won't be accessible. + field.value.type.base_type != BASE_TYPE_UTYPE) { + code += " " + GenTypeNative(field.value.type, false) + " "; + code += field.name + ";\n"; + } + } + code += "};\n\n"; + } + // Generate an accessor struct, with methods of the form: // type name() const { return GetField<type>(offset, defaultval); } GenComment(struct_def.doc_comment, code_ptr, nullptr); @@ -514,8 +683,7 @@ class CppGenerator : public BaseGenerator { if (!field.deprecated) { // Deprecated fields won't be accessible. auto is_scalar = IsScalar(field.value.type.base_type); GenComment(field.doc_comment, code_ptr, nullptr, " "); - code += " " + - GenTypeGet(field.value.type, " ", "const ", " *", true); + code += " " + GenTypeGet(field.value.type, " ", "const ", " *", true); code += field.name + "() const { return "; // Call a different accessor for pointers, that indirects. auto accessor = @@ -523,8 +691,8 @@ class CppGenerator : public BaseGenerator { ? "GetField<" : (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<"); auto offsetstr = GenFieldOffsetName(field); - auto call = accessor + GenTypeGet(field.value.type, "", - "const ", " *", false) + + auto call = accessor + + GenTypeGet(field.value.type, "", "const ", " *", false) + ">(" + offsetstr; // Default value as second arg for non-pointer types. if (IsScalar(field.value.type.base_type)) @@ -541,8 +709,7 @@ class CppGenerator : public BaseGenerator { code += GenUnderlyingCast(field, false, "_" + field.name); code += "); }\n"; } else { - auto type = - GenTypeGet(field.value.type, " ", "", " *", true); + auto type = GenTypeGet(field.value.type, " ", "", " *", true); code += " " + type + "mutable_" + field.name + "() { return "; code += GenUnderlyingCast(field, true, accessor + type + ">(" + offsetstr + ")"); @@ -604,8 +771,8 @@ class CppGenerator : public BaseGenerator { switch (field.value.type.base_type) { case BASE_TYPE_UNION: code += prefix + "Verify" + field.value.type.enum_def->name; - code += - "(verifier, " + field.name + "(), " + field.name + "_type())"; + code += "(verifier, " + field.name + "(), " + field.name + + UnionTypeFieldSuffix() + "())"; break; case BASE_TYPE_STRUCT: if (!field.value.type.struct_def->fixed) { @@ -643,7 +810,13 @@ class CppGenerator : public BaseGenerator { } code += prefix + "verifier.EndTable()"; code += ";\n }\n"; - code += "};\n\n"; + + if (parser_.opts.generate_object_based_api) { + // Generate the UnPack() pre declaration. + code += " " + TableUnPackSignature(struct_def, true) + ";\n"; + } + + code += "};\n\n"; // End of table. // Generate a builder struct, with methods of the form: // void add_name(type name) { fbb_.AddElement<type>(offset, name, default); @@ -695,6 +868,7 @@ class CppGenerator : public BaseGenerator { // Generate a convenient CreateX function that uses the above builder // to create a table in one go. + bool gen_vector_pars = false; code += "inline flatbuffers::Offset<" + struct_def.name + "> Create"; code += struct_def.name; code += "(flatbuffers::FlatBufferBuilder &_fbb"; @@ -702,24 +876,11 @@ class CppGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (!field.deprecated) { - code += ",\n " + GenTypeWire(field.value.type, " ", true); - code += field.name + " = "; - if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) { - auto ev = field.value.type.enum_def->ReverseLookup( - static_cast<int>(StringToInt(field.value.constant.c_str())), - false); - if (ev) { - code += WrapInNameSpace( - field.value.type.enum_def->defined_namespace, - GetEnumVal(*field.value.type.enum_def, *ev, parser_.opts)); - } else { - code += GenUnderlyingCast(field, true, field.value.constant); - } - } else if (field.value.type.base_type == BASE_TYPE_BOOL) { - code += field.value.constant == "0" ? "false" : "true"; - } else { - code += GenDefaultConstant(field); + if (field.value.type.base_type == BASE_TYPE_STRING || + field.value.type.base_type == BASE_TYPE_VECTOR) { + gen_vector_pars = true; } + GenSimpleParam(code, field); } } code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n"; @@ -735,6 +896,212 @@ class CppGenerator : public BaseGenerator { } } code += " return builder_.Finish();\n}\n\n"; + + // Generate a CreateX function with vector types as parameters + if (gen_vector_pars) { + code += "inline flatbuffers::Offset<" + struct_def.name + "> Create"; + code += struct_def.name; + code += "(flatbuffers::FlatBufferBuilder &_fbb"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + if (field.value.type.base_type == BASE_TYPE_STRING) { + code += ",\n const char *"; + code += field.name + " = nullptr"; + } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += ",\n const std::vector<"; + code += GenTypeWire(field.value.type.VectorType(), "", false); + code += "> *" + field.name + " = nullptr"; + } else { + GenSimpleParam(code, field); + } + } + } + code += ") {\n "; + code += "return Create"; + code += struct_def.name; + code += "(_fbb"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + if (field.value.type.base_type == BASE_TYPE_STRING) { + code += ", " + field.name + " ? 0 : "; + code += "_fbb.CreateString(" + field.name + ")"; + } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += ", " + field.name + " ? 0 : "; + code += "_fbb.CreateVector<"; + code += GenTypeWire(field.value.type.VectorType(), "", false); + code += ">(*" + field.name + ")"; + } else + code += ", " + field.name; + } + } + code += ");\n}\n\n"; + } + + if (parser_.opts.generate_object_based_api) { + // Generate a pre-declaration for a CreateX method that works with an + // unpacked C++ object. + code += TableCreateSignature(struct_def) + ";\n\n"; + } + } + + // Generate code for tables that needs to come after the regular definition. + void GenTablePost(StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + + if (parser_.opts.generate_object_based_api) { + // Generate the UnPack() method. + code += "inline " + TableUnPackSignature(struct_def, false) + " {\n"; + code += " auto _o = new " + NativeName(struct_def.name) + "();\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + auto prefix = " { auto _e = " + field.name + "(); "; + if (!IsScalar(field.value.type.base_type)) prefix += "if (_e) "; + auto deref = "_o->"; + auto dest = deref + field.name; + auto assign = prefix + dest + " = "; + auto gen_unpack_val = [&](const Type &type, const std::string &val, + bool invector) -> std::string { + switch (type.base_type) { + case BASE_TYPE_STRING: + return val + "->str()"; + case BASE_TYPE_STRUCT: + if (IsStruct(type)) { + if (invector) { + return "*" + val; + } else { + return "std::unique_ptr<" + type.struct_def->name + + ">(new " + type.struct_def->name + "(*" + val + "))"; + } + } else { + return val + "->UnPack()"; + } + default: + return val; + break; + } + }; + switch (field.value.type.base_type) { + case BASE_TYPE_VECTOR: { + code += prefix; + code += "{ for (flatbuffers::uoffset_t _i = 0;"; + code += " _i < _e->size(); _i++) { "; + code += dest + ".push_back("; + std::string indexing = "_e->Get(_i)"; + if (field.value.type.element == BASE_TYPE_BOOL) + indexing += "!=0"; + code += gen_unpack_val(field.value.type.VectorType(), + indexing, true); + code += "); } }"; + break; + } + case BASE_TYPE_UTYPE: { + auto &union_field = **(it + 1); + assert(union_field.value.type.base_type == BASE_TYPE_UNION); + code += prefix + deref + union_field.name + ".type = _e;"; + break; + } + case BASE_TYPE_UNION: + code += prefix + dest + ".table = "; + code += field.value.type.enum_def->name; + code += "Union::UnPack(_e, "; + code += field.name + UnionTypeFieldSuffix() + "());"; + break; + default: + code += assign + gen_unpack_val(field.value.type, "_e", false); + code += ";"; + break; + } + code += " };\n"; + } + } + code += " return std::unique_ptr<" + NativeName(struct_def.name); + code += ">(_o);\n}\n\n"; + + // Generate a CreateX method that works with an unpacked C++ object. + code += TableCreateSignature(struct_def) + " {\n"; + auto before_return_statement = code.size(); + code += " return Create"; + code += struct_def.name + "(_fbb"; + bool any_fields = false; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + any_fields = true; + auto field_name = field.name; + if (field.value.type.base_type == BASE_TYPE_UTYPE) { + field_name = field_name.substr(0, field_name.size() - + strlen(UnionTypeFieldSuffix())); + field_name += ".type"; + } + auto accessor = "_o->" + field_name; + auto ptrprefix = accessor + " ? "; + auto stlprefix = accessor + ".size() ? "; + auto postfix = " : 0"; + if (field.required && + (field.value.type.base_type == BASE_TYPE_STRING || + field.value.type.base_type == BASE_TYPE_VECTOR)) { + stlprefix = ""; + postfix = ""; + } + code += ",\n "; + switch (field.value.type.base_type) { + case BASE_TYPE_STRING: + code += stlprefix + "_fbb.CreateString(" + accessor + ")"; + code += postfix; + break; + case BASE_TYPE_VECTOR: { + auto vector_type = field.value.type.VectorType(); + code += stlprefix; + switch (vector_type.base_type) { + case BASE_TYPE_STRING: + code += "_fbb.CreateVectorOfStrings(" + accessor + ")"; + break; + case BASE_TYPE_STRUCT: + if (IsStruct(vector_type)) { + code += "_fbb.CreateVectorOfStructs(" + accessor + ")"; + } else { + code += "_fbb.CreateVector<flatbuffers::Offset<"; + code += vector_type.struct_def->name + ">>(" + accessor; + code += ".size(), [&](size_t i) { return Create"; + code += vector_type.struct_def->name + "(_fbb, " + accessor; + code += "[i].get()); })"; + } + break; + default: + code += "_fbb.CreateVector(" + accessor + ")"; + break; + } + code += postfix; + break; + } + case BASE_TYPE_UNION: + code += accessor + ".Pack(_fbb)"; + break; + case BASE_TYPE_STRUCT: + if (IsStruct(field.value.type)) { + code += ptrprefix + accessor + ".get()" + postfix; + } else { + code += ptrprefix + "Create"; + code += field.value.type.struct_def->name; + code += "(_fbb, " + accessor + ".get())" + postfix; + } + break; + default: + code += accessor; + break; + } + } + } + code += ");\n}\n\n"; + if (!any_fields) code.insert(before_return_statement, " (void)_o;\n"); + } } static void GenPadding(const FieldDef &field, std::string &code, @@ -791,6 +1158,15 @@ class CppGenerator : public BaseGenerator { code += "\n public:\n"; GenFullyQualifiedNameGetter(struct_def.name, code); + // Generate a default constructor. + code += " " + struct_def.name + "() { memset(this, 0, sizeof("; + code += struct_def.name + ")); }\n"; + + // Generate a copy constructor. + code += " " + struct_def.name + "(const " + struct_def.name; + code += " &_o) { memcpy(this, &_o, sizeof("; + code += struct_def.name + ")); }\n"; + // Generate a constructor that takes all fields as arguments. code += " " + struct_def.name + "("; for (auto it = struct_def.fields.vec.begin(); @@ -833,8 +1209,7 @@ class CppGenerator : public BaseGenerator { auto &field = **it; GenComment(field.doc_comment, code_ptr, nullptr, " "); auto is_scalar = IsScalar(field.value.type.base_type); - code += " " + - GenTypeGet(field.value.type, " ", "const ", " &", true); + code += " " + GenTypeGet(field.value.type, " ", "const ", " &", true); code += field.name + "() const { return "; code += GenUnderlyingCast( field, true, is_scalar @@ -868,8 +1243,8 @@ class CppGenerator : public BaseGenerator { // so that namespaces are properly opened and closed void SetNameSpace(const Namespace *ns, std::string *code_ptr) { if (cur_name_space_ == ns) return; - // compute the size of the longest common namespace prefix. - // if cur_name_space is A::B::C::D and ns is A::B::E::F::G, + // compute the size of the longest common namespace prefix. + // if cur_name_space is A::B::C::D and ns is A::B::E::F::G, // the common prefix is A::B:: and we have old_size = 4, new_size = 5 // and common_prefix_size = 2 auto old_size = @@ -881,19 +1256,20 @@ class CppGenerator : public BaseGenerator { cur_name_space_->components[common_prefix_size]) common_prefix_size++; // close cur_name_space in reverse order to reach the common prefix - // in the previous example, D then C are closed + // in the previous example, D then C are closed for (auto j = old_size; j > common_prefix_size; --j) *code_ptr += "} // namespace " + cur_name_space_->components[j - 1] + "\n"; if (old_size != common_prefix_size) *code_ptr += "\n"; // open namespace parts to reach the ns namespace - // in the previous example, E, then F, then G are opened + // in the previous example, E, then F, then G are opened for (auto j = common_prefix_size; j != new_size; ++j) *code_ptr += "namespace " + ns->components[j] + " {\n"; if (new_size != common_prefix_size) *code_ptr += "\n"; cur_name_space_ = ns; } }; + } // namespace cpp bool GenerateCPP(const Parser &parser, const std::string &path, @@ -902,19 +1278,16 @@ bool GenerateCPP(const Parser &parser, const std::string &path, return generator.generate(); } -std::string CPPMakeRule(const Parser &parser, - const std::string &path, +std::string CPPMakeRule(const Parser &parser, const std::string &path, const std::string &file_name) { - std::string filebase = flatbuffers::StripPath( - flatbuffers::StripExtension(file_name)); + std::string filebase = + flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); std::string make_rule = GeneratedFileName(path, filebase) + ": "; auto included_files = parser.GetIncludedFilesRecursive(file_name); - for (auto it = included_files.begin(); - it != included_files.end(); ++it) { + for (auto it = included_files.begin(); it != included_files.end(); ++it) { make_rule += " " + *it; } return make_rule; } } // namespace flatbuffers - diff --git a/src/idl_gen_fbs.cpp b/src/idl_gen_fbs.cpp index 00afe25d..237230e8 100644 --- a/src/idl_gen_fbs.cpp +++ b/src/idl_gen_fbs.cpp @@ -53,12 +53,14 @@ static void GenNameSpace(const Namespace &name_space, std::string *_schema, // Generate a flatbuffer schema from the Parser's internal representation. std::string GenerateFBS(const Parser &parser, const std::string &file_name) { - // Proto namespaces may clash with table names, so we have to prefix all: - for (auto it = parser.namespaces_.begin(); it != parser.namespaces_.end(); - ++it) { - for (auto comp = (*it)->components.begin(); comp != (*it)->components.end(); - ++comp) { - (*comp) = "_" + (*comp); + // Proto namespaces may clash with table names, so we have to prefix all: + if (!parser.opts.escape_proto_identifiers) { + for (auto it = parser.namespaces_.begin(); it != parser.namespaces_.end(); + ++it) { + for (auto comp = (*it)->components.begin(); comp != (*it)->components.end(); + ++comp) { + (*comp) = "_" + (*comp); + } } } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index a0b837a2..7e8e8c57 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -188,41 +188,85 @@ static_assert(sizeof(language_parameters) / sizeof(LanguageParameters) == IDLOptions::kMAX, "Please add extra elements to the arrays above."); -static std::string FunctionStart(const LanguageParameters &lang, char upper) { - return std::string() + - (lang.language == IDLOptions::kJava - ? static_cast<char>(tolower(upper)) - : upper); -} +namespace general { +class GeneralGenerator : public BaseGenerator { + public: + GeneralGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "", "."), + lang_(language_parameters[parser_.opts.lang]) { + assert(parser_.opts.lang <= IDLOptions::kMAX); + }; + GeneralGenerator &operator=(const GeneralGenerator &); + bool generate() { + std::string one_file_code; -static bool IsEnum(const Type& type) { - return type.enum_def != nullptr && IsInteger(type.base_type); -} + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + std::string enumcode; + auto &enum_def = **it; + GenEnum(enum_def, &enumcode); + if (parser_.opts.one_file) { + one_file_code += enumcode; + } else { + if (!SaveType(enum_def.name, *enum_def.defined_namespace, + enumcode, false)) return false; + } + } -// Ensure that a type is prefixed with its namespace whenever it is used -// outside of its namespace. -static std::string WrapInNameSpace(const Parser &parser, const Namespace *ns, - const std::string &name) { - if (parser.namespaces_.back() != ns) { - std::string qualified_name; - for (auto it = ns->components.begin(); - it != ns->components.end(); ++it) { - qualified_name += *it + "."; - } - return qualified_name + name; - } else { - return name; - } + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + std::string declcode; + auto &struct_def = **it; + GenStruct(struct_def, &declcode); + if (parser_.opts.one_file) { + one_file_code += declcode; + } else { + if (!SaveType(struct_def.name, *struct_def.defined_namespace, + declcode, true)) return false; + } + } + + if (parser_.opts.one_file) { + return SaveType(file_name_, *parser_.namespaces_.back(), + one_file_code, true); + } + return true; + } + + // Save out the generated code for a single class while adding + // declaration boilerplate. + bool SaveType(const std::string &defname, const Namespace &ns, + const std::string &classcode, bool needs_includes) { + if (!classcode.length()) return true; + + std::string code; + code = code + "// " + FlatBuffersGeneratedWarning(); + std::string namespace_name = FullNamespace(".", ns); + if (!namespace_name.empty()) { + code += lang_.namespace_ident + namespace_name + lang_.namespace_begin; + code += "\n\n"; + } + if (needs_includes) code += lang_.includes; + code += classcode; + if (!namespace_name.empty()) code += lang_.namespace_end; + auto filename = NamespaceDir(ns) + defname + lang_.file_extension; + return SaveFile(filename.c_str(), code, false); + } + + const Namespace *CurrentNameSpace() { return parser_.namespaces_.back(); } + + std::string FunctionStart(char upper) { + return std::string() + (lang_.language == IDLOptions::kJava + ? static_cast<char>(tolower(upper)) + : upper); } -static std::string WrapInNameSpace(const Parser &parser, - const Definition &def) { - return WrapInNameSpace(parser, def.defined_namespace, def.name); +static bool IsEnum(const Type& type) { + return type.enum_def != nullptr && IsInteger(type.base_type); } -static std::string GenTypeBasic(const LanguageParameters &lang, const Parser &parser, - const Type &type, - bool enableLangOverrides) { +std::string GenTypeBasic(const Type &type, bool enableLangOverrides) { static const char *gtypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ #JTYPE, #NTYPE, #GTYPE, @@ -231,55 +275,48 @@ static std::string GenTypeBasic(const LanguageParameters &lang, const Parser &pa }; if (enableLangOverrides) { - if (lang.language == IDLOptions::kCSharp) { - if (IsEnum(type)) return WrapInNameSpace(parser, *type.enum_def); + if (lang_.language == IDLOptions::kCSharp) { + if (IsEnum(type)) return WrapInNameSpace(*type.enum_def); if (type.base_type == BASE_TYPE_STRUCT) { - return "Offset<" + WrapInNameSpace(parser, *type.struct_def) + ">"; + return "Offset<" + WrapInNameSpace(*type.struct_def) + ">"; } } } - return gtypename[type.base_type * IDLOptions::kMAX + lang.language]; + return gtypename[type.base_type * IDLOptions::kMAX + lang_.language]; } -static std::string GenTypeBasic(const LanguageParameters &lang, const Parser &parser, - const Type &type) { - return GenTypeBasic(lang, parser, type, true); +std::string GenTypeBasic(const Type &type) { + return GenTypeBasic(type, true); } -static std::string GenTypeGet(const LanguageParameters &lang, const Parser &parser, - const Type &type); - -static std::string GenTypePointer(const LanguageParameters &lang, const Parser &parser, - const Type &type) { +std::string GenTypePointer(const Type &type) { switch (type.base_type) { case BASE_TYPE_STRING: - return lang.string_type; + return lang_.string_type; case BASE_TYPE_VECTOR: - return GenTypeGet(lang, parser, type.VectorType()); + return GenTypeGet(type.VectorType()); case BASE_TYPE_STRUCT: - return WrapInNameSpace(parser, *type.struct_def); + return WrapInNameSpace(*type.struct_def); case BASE_TYPE_UNION: // Unions in C# use a generic Table-derived type for better type safety - if (lang.language == IDLOptions::kCSharp) return "TTable"; + if (lang_.language == IDLOptions::kCSharp) return "TTable"; // fall through default: return "Table"; } } -static std::string GenTypeGet(const LanguageParameters &lang, const Parser &parser, - const Type &type) { +std::string GenTypeGet(const Type &type) { return IsScalar(type.base_type) - ? GenTypeBasic(lang, parser, type) - : GenTypePointer(lang, parser, type); + ? GenTypeBasic(type) + : GenTypePointer(type); } // Find the destination type the user wants to receive the value in (e.g. // one size higher signed types for unsigned serialized values in Java). -static Type DestinationType(const LanguageParameters &lang, const Parser &parser, const Type &type, - bool vectorelem) { - if (lang.language != IDLOptions::kJava) return type; +Type DestinationType(const Type &type, bool vectorelem) { + if (lang_.language != IDLOptions::kJava) return type; switch (type.base_type) { // We use int for both uchar/ushort, since that generally means less casting // than using short for uchar. @@ -288,33 +325,31 @@ static Type DestinationType(const LanguageParameters &lang, const Parser &parser case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG); case BASE_TYPE_VECTOR: if (vectorelem) - return DestinationType(lang, parser, type.VectorType(), vectorelem); + return DestinationType(type.VectorType(), vectorelem); // else fall thru: default: return type; } } -static std::string GenOffsetType(const LanguageParameters &lang, const Parser &parser, - const StructDef &struct_def) { - if(lang.language == IDLOptions::kCSharp) { - return "Offset<" + WrapInNameSpace(parser, struct_def) + ">"; +std::string GenOffsetType(const StructDef &struct_def) { + if(lang_.language == IDLOptions::kCSharp) { + return "Offset<" + WrapInNameSpace(struct_def) + ">"; } else { return "int"; } } -static std::string GenOffsetConstruct(const LanguageParameters &lang, const Parser &parser, - const StructDef &struct_def, +std::string GenOffsetConstruct(const StructDef &struct_def, const std::string &variable_name) { - if(lang.language == IDLOptions::kCSharp) { - return "new Offset<" + WrapInNameSpace(parser, struct_def) + ">(" + variable_name + ")"; + if(lang_.language == IDLOptions::kCSharp) { + return "new Offset<" + WrapInNameSpace(struct_def) + ">(" + variable_name + ")"; } return variable_name; } -static std::string GenVectorOffsetType(const LanguageParameters &lang) { - if(lang.language == IDLOptions::kCSharp) { +std::string GenVectorOffsetType() { + if(lang_.language == IDLOptions::kCSharp) { return "VectorOffset"; } else { return "int"; @@ -322,34 +357,32 @@ static std::string GenVectorOffsetType(const LanguageParameters &lang) { } // Generate destination type name -static std::string GenTypeNameDest(const LanguageParameters &lang, const Parser &parser, const Type &type) +std::string GenTypeNameDest(const Type &type) { - return GenTypeGet(lang, parser, DestinationType(lang, parser, type, true)); + return GenTypeGet(DestinationType(type, true)); } // Mask to turn serialized value into destination type value. -static std::string DestinationMask(const LanguageParameters &lang, - const Type &type, bool vectorelem) { - if (lang.language != IDLOptions::kJava) return ""; +std::string DestinationMask(const Type &type, bool vectorelem) { + if (lang_.language != IDLOptions::kJava) return ""; switch (type.base_type) { case BASE_TYPE_UCHAR: return " & 0xFF"; case BASE_TYPE_USHORT: return " & 0xFFFF"; case BASE_TYPE_UINT: return " & 0xFFFFFFFFL"; case BASE_TYPE_VECTOR: if (vectorelem) - return DestinationMask(lang, type.VectorType(), vectorelem); + return DestinationMask(type.VectorType(), vectorelem); // else fall thru: default: return ""; } } // Casts necessary to correctly read serialized data -static std::string DestinationCast(const LanguageParameters &lang, const Parser &parser, - const Type &type) { +std::string DestinationCast(const Type &type) { if (type.base_type == BASE_TYPE_VECTOR) { - return DestinationCast(lang, parser, type.VectorType()); + return DestinationCast(type.VectorType()); } else { - switch (lang.language) { + switch (lang_.language) { case IDLOptions::kJava: // Cast necessary to correctly read serialized unsigned values. if (type.base_type == BASE_TYPE_UINT) return "(long)"; @@ -357,7 +390,7 @@ static std::string DestinationCast(const LanguageParameters &lang, const Parser case IDLOptions::kCSharp: // Cast from raw integral types to enum. - if (IsEnum(type)) return "(" + WrapInNameSpace(parser, *type.enum_def) + ")"; + if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")"; break; default: @@ -371,13 +404,11 @@ static std::string DestinationCast(const LanguageParameters &lang, const Parser // In Java, parameters representing unsigned numbers need to be cast down to their respective type. // For example, a long holding an unsigned int value would be cast down to int before being put onto the buffer. // In C#, one cast directly cast an Enum to its underlying type, which is essential before putting it onto the buffer. -static std::string SourceCast(const LanguageParameters &lang, const Parser &parser, - const Type &type, - bool castFromDest) { +std::string SourceCast(const Type &type, bool castFromDest) { if (type.base_type == BASE_TYPE_VECTOR) { - return SourceCast(lang, parser, type.VectorType(), castFromDest); + return SourceCast(type.VectorType(), castFromDest); } else { - switch (lang.language) { + switch (lang_.language) { case IDLOptions::kJava: if (castFromDest) { if (type.base_type == BASE_TYPE_UINT) return "(int)"; @@ -386,7 +417,7 @@ static std::string SourceCast(const LanguageParameters &lang, const Parser &pars } break; case IDLOptions::kCSharp: - if (IsEnum(type)) return "(" + GenTypeBasic(lang, parser, type, false) + ")"; + if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")"; break; default: break; @@ -395,24 +426,20 @@ static std::string SourceCast(const LanguageParameters &lang, const Parser &pars return ""; } -static std::string SourceCast(const LanguageParameters &lang, const Parser &parser, - const Type &type) { - return SourceCast(lang, parser, type, true); +std::string SourceCast(const Type &type) { + return SourceCast(type, true); } -static std::string SourceCastBasic(const LanguageParameters &lang, const Parser &parser, - const Type &type, - bool castFromDest) { - return IsScalar(type.base_type) ? SourceCast(lang, parser, type, castFromDest) : ""; +std::string SourceCastBasic(const Type &type, bool castFromDest) { + return IsScalar(type.base_type) ? SourceCast(type, castFromDest) : ""; } -static std::string SourceCastBasic(const LanguageParameters &lang, const Parser &parser, - const Type &type) { - return SourceCastBasic(lang, parser, type, true); +std::string SourceCastBasic(const Type &type) { + return SourceCastBasic(type, true); } -static std::string GenEnumDefaultValue(const Parser &parser, const Value &value) { +std::string GenEnumDefaultValue(const Value &value) { auto enum_def = value.type.enum_def; auto vec = enum_def->vals.vec; auto default_value = StringToInt(value.constant.c_str()); @@ -421,7 +448,7 @@ static std::string GenEnumDefaultValue(const Parser &parser, const Value &value) for (auto it = vec.begin(); it != vec.end(); ++it) { auto enum_val = **it; if (enum_val.value == default_value) { - result = WrapInNameSpace(parser, *enum_def) + "." + enum_val.name; + result = WrapInNameSpace(*enum_def) + "." + enum_val.name; break; } } @@ -429,14 +456,13 @@ static std::string GenEnumDefaultValue(const Parser &parser, const Value &value) return result; } -static std::string GenDefaultValue(const LanguageParameters &lang, const Parser &parser, - const Value &value, bool enableLangOverrides) { +std::string GenDefaultValue(const Value &value, bool enableLangOverrides) { if (enableLangOverrides) { // handles both enum case and vector of enum case - if (lang.language == IDLOptions::kCSharp && + if (lang_.language == IDLOptions::kCSharp && value.type.enum_def != nullptr && value.type.base_type != BASE_TYPE_UNION) { - return GenEnumDefaultValue(parser, value); + return GenEnumDefaultValue(value); } } switch (value.type.base_type) { @@ -446,21 +472,19 @@ static std::string GenDefaultValue(const LanguageParameters &lang, const Parser } } -static std::string GenDefaultValue(const LanguageParameters &lang, const Parser &parser, - const Value &value) { - return GenDefaultValue(lang, parser, value, true); +std::string GenDefaultValue(const Value &value) { + return GenDefaultValue(value, true); } -static std::string GenDefaultValueBasic(const LanguageParameters &lang, const Parser &parser, - const Value &value, bool enableLangOverrides) { +std::string GenDefaultValueBasic(const Value &value, bool enableLangOverrides) { if (!IsScalar(value.type.base_type)) { if (enableLangOverrides) { - if (lang.language == IDLOptions::kCSharp) { + if (lang_.language == IDLOptions::kCSharp) { switch (value.type.base_type) { case BASE_TYPE_STRING: return "default(StringOffset)"; case BASE_TYPE_STRUCT: - return "default(Offset<" + WrapInNameSpace(parser, *value.type.struct_def) + ">)"; + return "default(Offset<" + WrapInNameSpace(*value.type.struct_def) + ">)"; case BASE_TYPE_VECTOR: return "default(VectorOffset)"; default: @@ -470,16 +494,14 @@ static std::string GenDefaultValueBasic(const LanguageParameters &lang, const Pa } return "0"; } - return GenDefaultValue(lang, parser, value, enableLangOverrides); + return GenDefaultValue(value, enableLangOverrides); } -static std::string GenDefaultValueBasic(const LanguageParameters &lang, const Parser &parser, - const Value &value) { - return GenDefaultValueBasic(lang, parser, value, true); +std::string GenDefaultValueBasic(const Value &value) { + return GenDefaultValueBasic(value, true); } -static void GenEnum(const LanguageParameters &lang, const Parser &parser, EnumDef &enum_def, - std::string *code_ptr) { +void GenEnum(EnumDef &enum_def, std::string *code_ptr) { std::string &code = *code_ptr; if (enum_def.generated) return; @@ -488,33 +510,33 @@ static void GenEnum(const LanguageParameters &lang, const Parser &parser, EnumDe // In Java, we use ints rather than the Enum feature, because we want them // to map directly to how they're used in C/C++ and file formats. // That, and Java Enums are expensive, and not universally liked. - GenComment(enum_def.doc_comment, code_ptr, &lang.comment_config); - code += std::string("public ") + lang.enum_decl + enum_def.name; - if (lang.language == IDLOptions::kCSharp) { - code += lang.inheritance_marker + GenTypeBasic(lang, parser, enum_def.underlying_type, false); + GenComment(enum_def.doc_comment, code_ptr, &lang_.comment_config); + code += std::string("public ") + lang_.enum_decl + enum_def.name; + if (lang_.language == IDLOptions::kCSharp) { + code += lang_.inheritance_marker + GenTypeBasic(enum_def.underlying_type, false); } - code += lang.open_curly; - if (lang.language == IDLOptions::kJava) { + code += lang_.open_curly; + if (lang_.language == IDLOptions::kJava) { code += " private " + enum_def.name + "() { }\n"; } for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); ++it) { auto &ev = **it; - GenComment(ev.doc_comment, code_ptr, &lang.comment_config, " "); - if (lang.language != IDLOptions::kCSharp) { + GenComment(ev.doc_comment, code_ptr, &lang_.comment_config, " "); + if (lang_.language != IDLOptions::kCSharp) { code += " public static"; - code += lang.const_decl; - code += GenTypeBasic(lang, parser, enum_def.underlying_type, false); + code += lang_.const_decl; + code += GenTypeBasic(enum_def.underlying_type, false); } code += " " + ev.name + " = "; code += NumToString(ev.value); - code += lang.enum_separator; + code += lang_.enum_separator; } // Generate a generate string table for enum values. // We do not do that for C# where this functionality is native. - if (lang.language != IDLOptions::kCSharp) { + if (lang_.language != IDLOptions::kCSharp) { // Problem is, if values are very sparse that could generate really big // tables. Ideally in that case we generate a map lookup instead, but for // the moment we simply don't output a table at all. @@ -524,9 +546,9 @@ static void GenEnum(const LanguageParameters &lang, const Parser &parser, EnumDe // "too sparse". Change at will. static const int kMaxSparseness = 5; if (range / static_cast<int64_t>(enum_def.vals.vec.size()) < kMaxSparseness) { - code += "\n private static"; - code += lang.const_decl; - code += lang.string_type; + code += "\n public static"; + code += lang_.const_decl; + code += lang_.string_type; code += "[] names = { "; auto val = enum_def.vals.vec.front()->value; for (auto it = enum_def.vals.vec.begin(); @@ -537,8 +559,8 @@ static void GenEnum(const LanguageParameters &lang, const Parser &parser, EnumDe } code += "};\n\n"; code += " public static "; - code += lang.string_type; - code += " " + MakeCamel("name", lang.first_camel_upper); + code += lang_.string_type; + code += " " + MakeCamel("name", lang_.first_camel_upper); code += "(int e) { return names[e"; if (enum_def.vals.vec.front()->value) code += " - " + enum_def.vals.vec.front()->name; @@ -547,23 +569,25 @@ static void GenEnum(const LanguageParameters &lang, const Parser &parser, EnumDe } // Close the class - code += "};\n\n"; + code += "}"; + // Java does not need the closing semi-colon on class definitions. + code += (lang_.language != IDLOptions::kJava) ? ";" : ""; + code += "\n\n"; } // Returns the function name that is able to read a value of the given type. -static std::string GenGetter(const LanguageParameters &lang, const Parser &parser, - const Type &type) { +std::string GenGetter(const Type &type) { switch (type.base_type) { case BASE_TYPE_STRING: return "__string"; case BASE_TYPE_STRUCT: return "__struct"; case BASE_TYPE_UNION: return "__union"; - case BASE_TYPE_VECTOR: return GenGetter(lang, parser, type.VectorType()); + case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); default: { - std::string getter = "bb." + FunctionStart(lang, 'G') + "et"; + std::string getter = "bb." + FunctionStart('G') + "et"; if (type.base_type == BASE_TYPE_BOOL) { getter = "0!=" + getter; - } else if (GenTypeBasic(lang, parser, type, false) != "byte") { - getter += MakeCamel(GenTypeBasic(lang, parser, type, false)); + } else if (GenTypeBasic(type, false) != "byte") { + getter += MakeCamel(GenTypeBasic(type, false)); } return getter; } @@ -572,13 +596,12 @@ static std::string GenGetter(const LanguageParameters &lang, const Parser &parse // Direct mutation is only allowed for scalar fields. // Hence a setter method will only be generated for such fields. -static std::string GenSetter(const LanguageParameters &lang, const Parser &parser, - const Type &type) { +std::string GenSetter(const Type &type) { if (IsScalar(type.base_type)) { - std::string setter = "bb." + FunctionStart(lang, 'P') + "ut"; - if (GenTypeBasic(lang, parser, type, false) != "byte" && + std::string setter = "bb." + FunctionStart('P') + "ut"; + if (GenTypeBasic(type, false) != "byte" && type.base_type != BASE_TYPE_BOOL) { - setter += MakeCamel(GenTypeBasic(lang, parser, type, false)); + setter += MakeCamel(GenTypeBasic(type, false)); } return setter; } else { @@ -587,17 +610,15 @@ static std::string GenSetter(const LanguageParameters &lang, const Parser &parse } // Returns the method name for use with add/put calls. -static std::string GenMethod(const LanguageParameters &lang, const Parser &parser, const Type &type) { +std::string GenMethod(const Type &type) { return IsScalar(type.base_type) - ? MakeCamel(GenTypeBasic(lang, parser, type, false)) + ? MakeCamel(GenTypeBasic(type, false)) : (IsStruct(type) ? "Struct" : "Offset"); } // Recursively generate arguments for a constructor, to deal with nested // structs. -static void GenStructArgs(const LanguageParameters &lang, const Parser &parser, - const StructDef &struct_def, - std::string *code_ptr, const char *nameprefix) { +void GenStructArgs(const StructDef &struct_def, std::string *code_ptr, const char *nameprefix) { std::string &code = *code_ptr; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); @@ -607,14 +628,14 @@ static void GenStructArgs(const LanguageParameters &lang, const Parser &parser, // Generate arguments for a struct inside a struct. To ensure names // don't clash, and to make it obvious these arguments are constructing // a nested struct, prefix the name with the field name. - GenStructArgs(lang, parser, *field.value.type.struct_def, code_ptr, + GenStructArgs(*field.value.type.struct_def, code_ptr, (nameprefix + (field.name + "_")).c_str()); } else { code += ", "; - code += GenTypeBasic(lang, parser, DestinationType(lang, parser, field.value.type, false)); + code += GenTypeBasic(DestinationType(field.value.type, false)); code += " "; code += nameprefix; - code += MakeCamel(field.name, lang.first_camel_upper); + code += MakeCamel(field.name, lang_.first_camel_upper); } } } @@ -622,36 +643,33 @@ static void GenStructArgs(const LanguageParameters &lang, const Parser &parser, // Recusively generate struct construction statements of the form: // builder.putType(name); // and insert manual padding. -static void GenStructBody(const LanguageParameters &lang, const Parser &parser, - const StructDef &struct_def, - std::string *code_ptr, const char *nameprefix) { +void GenStructBody(const StructDef &struct_def, std::string *code_ptr, const char *nameprefix) { std::string &code = *code_ptr; - code += " builder." + FunctionStart(lang, 'P') + "rep("; + code += " builder." + FunctionStart('P') + "rep("; code += NumToString(struct_def.minalign) + ", "; code += NumToString(struct_def.bytesize) + ");\n"; for (auto it = struct_def.fields.vec.rbegin(); it != struct_def.fields.vec.rend(); ++it) { auto &field = **it; if (field.padding) { - code += " builder." + FunctionStart(lang, 'P') + "ad("; + code += " builder." + FunctionStart('P') + "ad("; code += NumToString(field.padding) + ");\n"; } if (IsStruct(field.value.type)) { - GenStructBody(lang, parser, *field.value.type.struct_def, code_ptr, + GenStructBody(*field.value.type.struct_def, code_ptr, (nameprefix + (field.name + "_")).c_str()); } else { - code += " builder." + FunctionStart(lang, 'P') + "ut"; - code += GenMethod(lang, parser, field.value.type) + "("; - code += SourceCast(lang, parser, field.value.type); - auto argname = nameprefix + MakeCamel(field.name, lang.first_camel_upper); + code += " builder." + FunctionStart('P') + "ut"; + code += GenMethod(field.value.type) + "("; + code += SourceCast(field.value.type); + auto argname = nameprefix + MakeCamel(field.name, lang_.first_camel_upper); code += argname; code += ");\n"; } } } -static void GenStruct(const LanguageParameters &lang, const Parser &parser, - StructDef &struct_def, std::string *code_ptr) { +void GenStruct(StructDef &struct_def, std::string *code_ptr) { if (struct_def.generated) return; std::string &code = *code_ptr; @@ -661,23 +679,23 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, // public type name() { // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default; // } - GenComment(struct_def.doc_comment, code_ptr, &lang.comment_config); + GenComment(struct_def.doc_comment, code_ptr, &lang_.comment_config); code += "public "; - if (lang.language == IDLOptions::kCSharp && + if (lang_.language == IDLOptions::kCSharp && struct_def.attributes.Lookup("csharp_partial")) { // generate a partial class for this C# struct/table code += "partial "; } else { - code += lang.unsubclassable_decl; + code += lang_.unsubclassable_decl; } - code += "class " + struct_def.name + lang.inheritance_marker; + code += "class " + struct_def.name + lang_.inheritance_marker; code += struct_def.fixed ? "Struct" : "Table"; code += " {\n"; if (!struct_def.fixed) { // Generate a special accessor for the table that when used as the root // of a FlatBuffer - std::string method_name = FunctionStart(lang, 'G') + "etRootAs" + struct_def.name; + std::string method_name = FunctionStart('G') + "etRootAs" + struct_def.name; std::string method_signature = " public static " + struct_def.name + " " + method_name; // create convenience method that doesn't require an existing object @@ -686,20 +704,20 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, // create method that allows object reuse code += method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { "; - code += lang.set_bb_byteorder; - code += "return (obj.__init(_bb." + FunctionStart(lang, 'G'); + code += lang_.set_bb_byteorder; + code += "return (obj.__init(_bb." + FunctionStart('G'); code += "etInt(_bb."; - code += lang.get_bb_position; + code += lang_.get_bb_position; code += ") + _bb."; - code += lang.get_bb_position; + code += lang_.get_bb_position; code += ", _bb)); }\n"; - if (parser.root_struct_def_ == &struct_def) { - if (parser.file_identifier_.length()) { + if (parser_.root_struct_def_ == &struct_def) { + if (parser_.file_identifier_.length()) { // Check if a buffer has the identifier. code += " public static "; - code += lang.bool_type + struct_def.name; + code += lang_.bool_type + struct_def.name; code += "BufferHasIdentifier(ByteBuffer _bb) { return "; - code += "__has_identifier(_bb, \"" + parser.file_identifier_; + code += "__has_identifier(_bb, \"" + parser_.file_identifier_; code += "\"); }\n"; } } @@ -714,14 +732,14 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, ++it) { auto &field = **it; if (field.deprecated) continue; - GenComment(field.doc_comment, code_ptr, &lang.comment_config, " "); - std::string type_name = GenTypeGet(lang, parser, field.value.type); - std::string type_name_dest = GenTypeNameDest(lang, parser, field.value.type); - std::string dest_mask = DestinationMask(lang, field.value.type, true); - std::string dest_cast = DestinationCast(lang, parser, field.value.type); - std::string src_cast = SourceCast(lang, parser, field.value.type); + GenComment(field.doc_comment, code_ptr, &lang_.comment_config, " "); + std::string type_name = GenTypeGet(field.value.type); + std::string type_name_dest = GenTypeNameDest(field.value.type); + std::string dest_mask = DestinationMask(field.value.type, true); + std::string dest_cast = DestinationCast(field.value.type); + std::string src_cast = SourceCast(field.value.type); std::string method_start = " public " + type_name_dest + " " + - MakeCamel(field.name, lang.first_camel_upper); + MakeCamel(field.name, lang_.first_camel_upper); // Most field accessors need to retrieve and test the field offset first, // this is the prefix code for that: @@ -731,16 +749,16 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, // Generate the accessors that don't do object reuse. if (field.value.type.base_type == BASE_TYPE_STRUCT) { // Calls the accessor that takes an accessor object with a new object. - if (lang.language == IDLOptions::kCSharp) { + if (lang_.language == IDLOptions::kCSharp) { code += method_start + " { get { return Get"; - code += MakeCamel(field.name, lang.first_camel_upper); + code += MakeCamel(field.name, lang_.first_camel_upper); code += "(new "; code += type_name + "()); } }\n"; - method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang.first_camel_upper); + method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang_.first_camel_upper); } else { code += method_start + "() { return "; - code += MakeCamel(field.name, lang.first_camel_upper); + code += MakeCamel(field.name, lang_.first_camel_upper); code += "(new "; code += type_name + "()); }\n"; } @@ -748,32 +766,32 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, field.value.type.element == BASE_TYPE_STRUCT) { // Accessors for vectors of structs also take accessor objects, this // generates a variant without that argument. - if (lang.language == IDLOptions::kCSharp) { - method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang.first_camel_upper); + if (lang_.language == IDLOptions::kCSharp) { + method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang_.first_camel_upper); code += method_start + "(int j) { return Get"; } else { code += method_start + "(int j) { return "; } - code += MakeCamel(field.name, lang.first_camel_upper); + code += MakeCamel(field.name, lang_.first_camel_upper); code += "(new "; code += type_name + "(), j); }\n"; } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { - if (lang.language == IDLOptions::kCSharp) { - method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang.first_camel_upper); + if (lang_.language == IDLOptions::kCSharp) { + method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang_.first_camel_upper); } } else if (field.value.type.base_type == BASE_TYPE_UNION) { - if (lang.language == IDLOptions::kCSharp) { + if (lang_.language == IDLOptions::kCSharp) { // union types in C# use generic Table-derived type for better type safety - method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang.first_camel_upper) + "<TTable>"; + method_start = " public " + type_name_dest + " Get" + MakeCamel(field.name, lang_.first_camel_upper) + "<TTable>"; offset_prefix = " where TTable : Table" + offset_prefix; type_name = type_name_dest; } } - std::string getter = dest_cast + GenGetter(lang, parser, field.value.type); + std::string getter = dest_cast + GenGetter(field.value.type); code += method_start; std::string default_cast = ""; // only create default casts for c# scalars or vectors of scalars - if (lang.language == IDLOptions::kCSharp && + if (lang_.language == IDLOptions::kCSharp && (IsScalar(field.value.type.base_type) || (field.value.type.base_type == BASE_TYPE_VECTOR && IsScalar(field.value.type.element)))) { // For scalars, default value will be returned by GetDefaultValue(). If the scalar is an enum, GetDefaultValue() @@ -785,8 +803,8 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, } std::string member_suffix = ""; if (IsScalar(field.value.type.base_type)) { - code += lang.getter_prefix; - member_suffix = lang.getter_suffix; + code += lang_.getter_prefix; + member_suffix = lang_.getter_suffix; if (struct_def.fixed) { code += " { return " + getter; code += "(bb_pos + " + NumToString(field.value.offset) + ")"; @@ -794,7 +812,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, } else { code += offset_prefix + getter; code += "(o + bb_pos)" + dest_mask + " : " + default_cast; - code += GenDefaultValue(lang, parser, field.value); + code += GenDefaultValue(field.value); } } else { switch (field.value.type.base_type) { @@ -814,8 +832,8 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, } break; case BASE_TYPE_STRING: - code += lang.getter_prefix; - member_suffix = lang.getter_suffix; + code += lang_.getter_prefix; + member_suffix = lang_.getter_suffix; code += offset_prefix + getter + "(o + bb_pos) : null"; break; case BASE_TYPE_VECTOR: { @@ -854,22 +872,22 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += member_suffix; code += "}\n"; if (field.value.type.base_type == BASE_TYPE_VECTOR) { - code += " public int " + MakeCamel(field.name, lang.first_camel_upper); + code += " public int " + MakeCamel(field.name, lang_.first_camel_upper); code += "Length"; - code += lang.getter_prefix; + code += lang_.getter_prefix; code += offset_prefix; code += "__vector_len(o) : 0; "; - code += lang.getter_suffix; + code += lang_.getter_suffix; code += "}\n"; } // Generate a ByteBuffer accessor for strings & vectors of scalars. if ((field.value.type.base_type == BASE_TYPE_VECTOR && IsScalar(field.value.type.VectorType().base_type)) || field.value.type.base_type == BASE_TYPE_STRING) { - switch (lang.language) { + switch (lang_.language) { case IDLOptions::kJava: code += " public ByteBuffer "; - code += MakeCamel(field.name, lang.first_camel_upper); + code += MakeCamel(field.name, lang_.first_camel_upper); code += "AsByteBuffer() { return __vector_as_bytebuffer("; code += NumToString(field.value.offset) + ", "; code += NumToString(field.value.type.base_type == BASE_TYPE_STRING ? 1 : @@ -878,7 +896,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, break; case IDLOptions::kCSharp: code += " public ArraySegment<byte>? Get"; - code += MakeCamel(field.name, lang.first_camel_upper); + code += MakeCamel(field.name, lang_.first_camel_upper); code += "Bytes() { return __vector_as_arraysegment("; code += NumToString(field.value.offset); code += "); }\n"; @@ -891,13 +909,13 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, auto nested = field.attributes.Lookup("nested_flatbuffer"); if (nested) { auto nested_qualified_name = - parser.namespaces_.back()->GetFullyQualifiedName(nested->constant); - auto nested_type = parser.structs_.Lookup(nested_qualified_name); - auto nested_type_name = WrapInNameSpace(parser, *nested_type); - auto nestedMethodName = MakeCamel(field.name, lang.first_camel_upper) + parser_.namespaces_.back()->GetFullyQualifiedName(nested->constant); + auto nested_type = parser_.structs_.Lookup(nested_qualified_name); + auto nested_type_name = WrapInNameSpace(*nested_type); + auto nestedMethodName = MakeCamel(field.name, lang_.first_camel_upper) + "As" + nested_type_name; auto getNestedMethodName = nestedMethodName; - if (lang.language == IDLOptions::kCSharp) { + if (lang_.language == IDLOptions::kCSharp) { getNestedMethodName = "Get" + nestedMethodName; } code += " public " + nested_type_name + " "; @@ -909,16 +927,16 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "return o != 0 ? obj.__init(__indirect(__vector(o)), bb) : null; }\n"; } // generate mutators for scalar fields or vectors of scalars - if (parser.opts.mutable_buffer) { + if (parser_.opts.mutable_buffer) { auto underlying_type = field.value.type.base_type == BASE_TYPE_VECTOR ? field.value.type.VectorType() : field.value.type; // boolean parameters have to be explicitly converted to byte representation auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL ? "(byte)(" + field.name + " ? 1 : 0)" : field.name; - auto mutator_prefix = MakeCamel("mutate", lang.first_camel_upper); + auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper); //a vector mutator also needs the index of the vector element it should mutate auto mutator_params = (field.value.type.base_type == BASE_TYPE_VECTOR ? "(int j, " : "(") + - GenTypeNameDest(lang, parser, underlying_type) + " " + + GenTypeNameDest(underlying_type) + " " + field.name + ") { "; auto setter_index = field.value.type.base_type == BASE_TYPE_VECTOR ? "__vector(o) + j * " + NumToString(InlineSize(underlying_type)) @@ -927,15 +945,15 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, (field.value.type.base_type == BASE_TYPE_VECTOR && IsScalar(field.value.type.VectorType().base_type))) { code += " public "; - code += struct_def.fixed ? "void " : lang.bool_type; + code += struct_def.fixed ? "void " : lang_.bool_type; code += mutator_prefix + MakeCamel(field.name, true); code += mutator_params; if (struct_def.fixed) { - code += GenSetter(lang, parser, underlying_type) + "(" + setter_index + ", "; + code += GenSetter(underlying_type) + "(" + setter_index + ", "; code += src_cast + setter_parameter + "); }\n"; } else { code += "int o = __offset(" + NumToString(field.value.offset) + ");"; - code += " if (o != 0) { " + GenSetter(lang, parser, underlying_type); + code += " if (o != 0) { " + GenSetter(underlying_type); code += "(" + setter_index + ", " + src_cast + setter_parameter + "); return true; } else { return false; } }\n"; } } @@ -944,14 +962,14 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "\n"; if (struct_def.fixed) { // create a struct constructor function - code += " public static " + GenOffsetType(lang, parser, struct_def) + " "; - code += FunctionStart(lang, 'C') + "reate"; + code += " public static " + GenOffsetType(struct_def) + " "; + code += FunctionStart('C') + "reate"; code += struct_def.name + "(FlatBufferBuilder builder"; - GenStructArgs(lang, parser, struct_def, code_ptr, ""); + GenStructArgs(struct_def, code_ptr, ""); code += ") {\n"; - GenStructBody(lang, parser, struct_def, code_ptr, ""); + GenStructBody(struct_def, code_ptr, ""); code += " return "; - code += GenOffsetConstruct(lang, parser, struct_def, "builder." + std::string(lang.get_fbb_offset)); + code += GenOffsetConstruct(struct_def, "builder." + std::string(lang_.get_fbb_offset)); code += ";\n }\n"; } else { // Generate a method that creates a table in one go. This is only possible @@ -972,28 +990,28 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, if (has_no_struct_fields && num_fields) { // Generate a table constructor of the form: // public static int createName(FlatBufferBuilder builder, args...) - code += " public static " + GenOffsetType(lang, parser, struct_def) + " "; - code += FunctionStart(lang, 'C') + "reate" + struct_def.name; + code += " public static " + GenOffsetType(struct_def) + " "; + code += FunctionStart('C') + "reate" + struct_def.name; code += "(FlatBufferBuilder builder"; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) continue; code += ",\n "; - code += GenTypeBasic(lang, parser, DestinationType(lang, parser, field.value.type, false)); + code += GenTypeBasic(DestinationType(field.value.type, false)); code += " "; code += field.name; if (!IsScalar(field.value.type.base_type)) code += "Offset"; // Java doesn't have defaults, which means this method must always // supply all arguments, and thus won't compile when fields are added. - if (lang.language != IDLOptions::kJava) { + if (lang_.language != IDLOptions::kJava) { code += " = "; - code += GenDefaultValueBasic(lang, parser, field.value); + code += GenDefaultValueBasic(field.value); } } code += ") {\n builder."; - code += FunctionStart(lang, 'S') + "tartObject("; + code += FunctionStart('S') + "tartObject("; code += NumToString(struct_def.fields.vec.size()) + ");\n"; for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; size; @@ -1005,7 +1023,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, (!struct_def.sortbysize || size == SizeOf(field.value.type.base_type))) { code += " " + struct_def.name + "."; - code += FunctionStart(lang, 'A') + "dd"; + code += FunctionStart('A') + "dd"; code += MakeCamel(field.name) + "(builder, " + field.name; if (!IsScalar(field.value.type.base_type)) code += "Offset"; code += ");\n"; @@ -1013,7 +1031,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, } } code += " return " + struct_def.name + "."; - code += FunctionStart(lang, 'E') + "nd" + struct_def.name; + code += FunctionStart('E') + "nd" + struct_def.name; code += "(builder);\n }\n\n"; } // Generate a set of static methods that allow table construction, @@ -1021,30 +1039,30 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, // public static void addName(FlatBufferBuilder builder, short name) // { builder.addShort(id, name, default); } // Unlike the Create function, these always work. - code += " public static void " + FunctionStart(lang, 'S') + "tart"; + code += " public static void " + FunctionStart('S') + "tart"; code += struct_def.name; code += "(FlatBufferBuilder builder) { builder."; - code += FunctionStart(lang, 'S') + "tartObject("; + code += FunctionStart('S') + "tartObject("; code += NumToString(struct_def.fields.vec.size()) + "); }\n"; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (field.deprecated) continue; - code += " public static void " + FunctionStart(lang, 'A') + "dd"; + code += " public static void " + FunctionStart('A') + "dd"; code += MakeCamel(field.name); code += "(FlatBufferBuilder builder, "; - code += GenTypeBasic(lang, parser, DestinationType(lang, parser, field.value.type, false)); + code += GenTypeBasic(DestinationType(field.value.type, false)); auto argname = MakeCamel(field.name, false); if (!IsScalar(field.value.type.base_type)) argname += "Offset"; - code += " " + argname + ") { builder." + FunctionStart(lang, 'A') + "dd"; - code += GenMethod(lang, parser, field.value.type) + "("; + code += " " + argname + ") { builder." + FunctionStart('A') + "dd"; + code += GenMethod(field.value.type) + "("; code += NumToString(it - struct_def.fields.vec.begin()) + ", "; - code += SourceCastBasic(lang, parser, field.value.type); + code += SourceCastBasic(field.value.type); code += argname; - if(!IsScalar(field.value.type.base_type) && field.value.type.base_type != BASE_TYPE_UNION && lang.language == IDLOptions::kCSharp) { + if(!IsScalar(field.value.type.base_type) && field.value.type.base_type != BASE_TYPE_UNION && lang_.language == IDLOptions::kCSharp) { code += ".Value"; } - code += ", " + GenDefaultValue(lang, parser, field.value, false); + code += ", " + GenDefaultValue(field.value, false); code += "); }\n"; if (field.value.type.base_type == BASE_TYPE_VECTOR) { auto vector_type = field.value.type.VectorType(); @@ -1052,127 +1070,72 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, auto elem_size = InlineSize(vector_type); if (!IsStruct(vector_type)) { // Generate a method to create a vector from a Java array. - code += " public static " + GenVectorOffsetType(lang) + " " + FunctionStart(lang, 'C') + "reate"; + code += " public static " + GenVectorOffsetType() + " " + FunctionStart('C') + "reate"; code += MakeCamel(field.name); code += "Vector(FlatBufferBuilder builder, "; - code += GenTypeBasic(lang, parser, vector_type) + "[] data) "; - code += "{ builder." + FunctionStart(lang, 'S') + "tartVector("; + code += GenTypeBasic(vector_type) + "[] data) "; + code += "{ builder." + FunctionStart('S') + "tartVector("; code += NumToString(elem_size); - code += ", data." + FunctionStart(lang, 'L') + "ength, "; + code += ", data." + FunctionStart('L') + "ength, "; code += NumToString(alignment); code += "); for (int i = data."; - code += FunctionStart(lang, 'L') + "ength - 1; i >= 0; i--) builder."; - code += FunctionStart(lang, 'A') + "dd"; - code += GenMethod(lang, parser, vector_type); + code += FunctionStart('L') + "ength - 1; i >= 0; i--) builder."; + code += FunctionStart('A') + "dd"; + code += GenMethod(vector_type); code += "("; - code += SourceCastBasic(lang, parser, vector_type, false); + code += SourceCastBasic(vector_type, false); code += "data[i]"; - if (lang.language == IDLOptions::kCSharp && + if (lang_.language == IDLOptions::kCSharp && (vector_type.base_type == BASE_TYPE_STRUCT || vector_type.base_type == BASE_TYPE_STRING)) code += ".Value"; code += "); return "; - code += "builder." + FunctionStart(lang, 'E') + "ndVector(); }\n"; + code += "builder." + FunctionStart('E') + "ndVector(); }\n"; } // Generate a method to start a vector, data to be added manually after. - code += " public static void " + FunctionStart(lang, 'S') + "tart"; + code += " public static void " + FunctionStart('S') + "tart"; code += MakeCamel(field.name); code += "Vector(FlatBufferBuilder builder, int numElems) "; - code += "{ builder." + FunctionStart(lang, 'S') + "tartVector("; + code += "{ builder." + FunctionStart('S') + "tartVector("; code += NumToString(elem_size); code += ", numElems, " + NumToString(alignment); code += "); }\n"; } } - code += " public static " + GenOffsetType(lang, parser, struct_def) + " "; - code += FunctionStart(lang, 'E') + "nd" + struct_def.name; + code += " public static " + GenOffsetType(struct_def) + " "; + code += FunctionStart('E') + "nd" + struct_def.name; code += "(FlatBufferBuilder builder) {\n int o = builder."; - code += FunctionStart(lang, 'E') + "ndObject();\n"; + code += FunctionStart('E') + "ndObject();\n"; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (!field.deprecated && field.required) { - code += " builder." + FunctionStart(lang, 'R') + "equired(o, "; + code += " builder." + FunctionStart('R') + "equired(o, "; code += NumToString(field.value.offset); code += "); // " + field.name + "\n"; } } - code += " return " + GenOffsetConstruct(lang, parser, struct_def, "o") + ";\n }\n"; - if (parser.root_struct_def_ == &struct_def) { + code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n"; + if (parser_.root_struct_def_ == &struct_def) { code += " public static void "; - code += FunctionStart(lang, 'F') + "inish" + struct_def.name; - code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType(lang, parser, struct_def) + " offset) {"; - code += " builder." + FunctionStart(lang, 'F') + "inish(offset"; - if (lang.language == IDLOptions::kCSharp) { + code += FunctionStart('F') + "inish" + struct_def.name; + code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def) + " offset) {"; + code += " builder." + FunctionStart('F') + "inish(offset"; + if (lang_.language == IDLOptions::kCSharp) { code += ".Value"; } - if (parser.file_identifier_.length()) - code += ", \"" + parser.file_identifier_ + "\""; + if (parser_.file_identifier_.length()) + code += ", \"" + parser_.file_identifier_ + "\""; code += "); }\n"; } } - code += "};\n\n"; + code += "}"; + // Java does not need the closing semi-colon on class definitions. + code += (lang_.language != IDLOptions::kJava) ? ";" : ""; + code += "\n\n"; } - -namespace general { -class GeneralGenerator : public BaseGenerator { - public: - GeneralGenerator(const Parser &parser, const std::string &path, - const std::string &file_name) - : BaseGenerator(parser, path, file_name){}; - bool generate() { - assert(parser_.opts.lang <= IDLOptions::kMAX); - auto lang = language_parameters[parser_.opts.lang]; - std::string one_file_code; - - for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); - ++it) { - std::string enumcode; - GenEnum(lang, parser_, **it, &enumcode); - if (parser_.opts.one_file) { - one_file_code += enumcode; - } else { - if (!SaveType(lang, (**it).name, enumcode, false)) return false; - } - } - - for (auto it = parser_.structs_.vec.begin(); - it != parser_.structs_.vec.end(); ++it) { - std::string declcode; - GenStruct(lang, parser_, **it, &declcode); - if (parser_.opts.one_file) { - one_file_code += declcode; - } else { - if (!SaveType(lang, (**it).name, declcode, true)) return false; - } - } - - if (parser_.opts.one_file) { - return SaveType(lang, file_name_, one_file_code, true); - } - return true; - } - - // Save out the generated code for a single class while adding - // declaration boilerplate. - bool SaveType(const LanguageParameters &lang, const std::string &defname, - const std::string &classcode, bool needs_includes) { - if (!classcode.length()) return true; - - std::string code; - code = code + "// " + FlatBuffersGeneratedWarning(); - std::string namespace_name = FullNamespace("."); - if (!namespace_name.empty()) { - code += lang.namespace_ident + namespace_name + lang.namespace_begin; - code += "\n\n"; - } - if (needs_includes) code += lang.includes; - code += classcode; - if (!namespace_name.empty()) code += lang.namespace_end; - auto filename = namespace_dir_ + defname + lang.file_extension; - return SaveFile(filename.c_str(), code, false); - } + const LanguageParameters & lang_; }; } // namespace general @@ -1188,19 +1151,25 @@ std::string GeneralMakeRule(const Parser &parser, const std::string &path, auto lang = language_parameters[parser.opts.lang]; std::string make_rule; - std::string directory = - BaseGenerator::NamespaceDir(parser, path) + kPathSeparator; for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end(); ++it) { + auto &enum_def = **it; if (make_rule != "") make_rule += " "; - make_rule += directory + (**it).name + lang.file_extension; + std::string directory = + BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace) + + kPathSeparator; + make_rule += directory + enum_def.name + lang.file_extension; } for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end(); ++it) { + auto &struct_def = **it; if (make_rule != "") make_rule += " "; - make_rule += directory + (**it).name + lang.file_extension; + std::string directory = + BaseGenerator::NamespaceDir(parser, path, *struct_def.defined_namespace) + + kPathSeparator; + make_rule += directory + struct_def.name + lang.file_extension; } make_rule += ": "; diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index 0c624240..1f6b103e 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -439,7 +439,7 @@ static void GenReceiver(const StructDef &struct_def, std::string *code_ptr) { code += "func (rcv *" + struct_def.name + ")"; } -// Generate a struct field, conditioned on its child type(s). +// Generate a struct field getter, conditioned on its child type(s). static void GenStructAccessor(const StructDef &struct_def, const FieldDef &field, std::string *code_ptr) { @@ -486,6 +486,48 @@ static void GenStructAccessor(const StructDef &struct_def, } } +// Mutate the value of a struct's scalar. +static void MutateScalarFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string type = MakeCamel(GenTypeBasic(field.value.type)); + std::string setter = "rcv._tab.Mutate" + type; + GenReceiver(struct_def, code_ptr); + code += " Mutate" + MakeCamel(field.name); + code += "(n " + TypeName(field) + ") bool { return " + setter; + code += "(rcv._tab.Pos + flatbuffers.UOffsetT("; + code += NumToString(field.value.offset) + "), n) }\n\n"; +} + +// Mutate the value of a table's scalar. +static void MutateScalarFieldOfTable(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string type = MakeCamel(GenTypeBasic(field.value.type)); + std::string setter = "rcv._tab.Mutate" + type + "Slot"; + GenReceiver(struct_def, code_ptr); + code += " Mutate" + MakeCamel(field.name); + code += "(n " + TypeName(field) + ") bool {\n\treturn "; + code += setter + "(" + NumToString(field.value.offset) + ", n)\n"; + code += "}\n\n"; +} + +// Generate a struct field setter, conditioned on its child type(s). +static void GenStructMutator(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + GenComment(field.doc_comment, code_ptr, nullptr, ""); + if (IsScalar(field.value.type.base_type)) { + if (struct_def.fixed) { + MutateScalarFieldOfStruct(struct_def, field, code_ptr); + } else { + MutateScalarFieldOfTable(struct_def, field, code_ptr); + } + } +} + // Generate table constructors, conditioned on its members' types. static void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) { @@ -529,6 +571,7 @@ static void GenStruct(const StructDef &struct_def, if (field.deprecated) continue; GenStructAccessor(struct_def, field, code_ptr); + GenStructMutator(struct_def, field, code_ptr); } if (struct_def.fixed) { @@ -624,7 +667,8 @@ class GoGenerator : public BaseGenerator { public: GoGenerator(const Parser &parser, const std::string &path, const std::string &file_name) - : BaseGenerator(parser, path, file_name){}; + : BaseGenerator(parser, path, file_name, "" /* not used*/, + "" /* not used */){}; bool generate() { for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); ++it) { @@ -663,9 +707,10 @@ class GoGenerator : public BaseGenerator { if (!classcode.length()) return true; std::string code = ""; - BeginFile(LastNamespacePart(), needs_imports, &code); + BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code); code += classcode; - std::string filename = namespace_dir_ + def.name + ".go"; + std::string filename = + NamespaceDir(*def.defined_namespace) + def.name + ".go"; return SaveFile(filename.c_str(), code, false); } }; @@ -678,4 +723,3 @@ bool GenerateGo(const Parser &parser, const std::string &path, } } // namespace flatbuffers - diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp new file mode 100644 index 00000000..6ada3e87 --- /dev/null +++ b/src/idl_gen_grpc.cpp @@ -0,0 +1,217 @@ +/* + * Copyright 2014 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. + */ + +// independent from idl_parser, since this code is not needed for most clients + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +#include "src/compiler/cpp_generator.h" + +namespace flatbuffers { + +class FlatBufMethod : public grpc_cpp_generator::Method { + public: + enum Streaming { kNone, kClient, kServer, kBiDi }; + + FlatBufMethod(const RPCCall *method) + : method_(method) { + streaming_ = kNone; + auto val = method_->attributes.Lookup("streaming"); + if (val) { + if (val->constant == "client") streaming_ = kClient; + if (val->constant == "server") streaming_ = kServer; + if (val->constant == "bidi") streaming_ = kBiDi; + } + } + + std::string name() const { return method_->name; } + + std::string GRPCType(const StructDef &sd) const { + return "flatbuffers::BufferRef<" + sd.name + ">"; + } + + std::string input_type_name() const { + return GRPCType(*method_->request); + } + std::string output_type_name() const { + return GRPCType(*method_->response); + } + + bool NoStreaming() const { return streaming_ == kNone; } + bool ClientOnlyStreaming() const { return streaming_ == kClient; } + bool ServerOnlyStreaming() const { return streaming_ == kServer; } + bool BidiStreaming() const { return streaming_ == kBiDi; } + + private: + const RPCCall *method_; + Streaming streaming_; +}; + +class FlatBufService : public grpc_cpp_generator::Service { + public: + FlatBufService(const ServiceDef *service) : service_(service) {} + + std::string name() const { return service_->name; } + + int method_count() const { + return static_cast<int>(service_->calls.vec.size()); + }; + + std::unique_ptr<const grpc_cpp_generator::Method> method(int i) const { + return std::unique_ptr<const grpc_cpp_generator::Method>( + new FlatBufMethod(service_->calls.vec[i])); + }; + + private: + const ServiceDef *service_; +}; + +class FlatBufPrinter : public grpc_cpp_generator::Printer { + public: + FlatBufPrinter(std::string *str) + : str_(str), escape_char_('$'), indent_(0) {} + + void Print(const std::map<std::string, std::string> &vars, + const char *string_template) { + std::string s = string_template; + // Replace any occurrences of strings in "vars" that are surrounded + // by the escape character by what they're mapped to. + size_t pos; + while ((pos = s.find(escape_char_)) != std::string::npos) { + // Found an escape char, must also find the closing one. + size_t pos2 = s.find(escape_char_, pos + 1); + // If placeholder not closed, ignore. + if (pos2 == std::string::npos) break; + auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1)); + // If unknown placeholder, ignore. + if (it == vars.end()) break; + // Subtitute placeholder. + s.replace(pos, pos2 - pos + 1, it->second); + } + Print(s.c_str()); + } + + void Print(const char *s) { + // Add this string, but for each part separated by \n, add indentation. + for (;;) { + // Current indentation. + str_->insert(str_->end(), indent_ * 2, ' '); + // See if this contains more than one line. + const char * lf = strchr(s, '\n'); + if (lf) { + (*str_) += std::string(s, lf + 1); + s = lf + 1; + if (!*s) break; // Only continue if there's more lines. + } else { + (*str_) += s; + break; + } + } + } + + void Indent() { indent_++; } + void Outdent() { indent_--; assert(indent_ >= 0); } + + private: + std::string *str_; + char escape_char_; + int indent_; +}; + +class FlatBufFile : public grpc_cpp_generator::File { + public: + FlatBufFile(const Parser &parser, const std::string &file_name) + : parser_(parser), file_name_(file_name) {} + FlatBufFile &operator=(const FlatBufFile &); + + std::string filename() const { return file_name_; } + std::string filename_without_ext() const { + return StripExtension(file_name_); + } + + std::string message_header_ext() const { return "_generated.h"; } + std::string service_header_ext() const { return ".grpc.fb.h"; } + + std::string package() const { + return parser_.namespaces_.back()->GetFullyQualifiedName(""); + } + + std::vector<std::string> package_parts() const { + return parser_.namespaces_.back()->components; + } + + std::string additional_headers() const { + return "#include \"flatbuffers/grpc.h\"\n"; + } + + int service_count() const { + return static_cast<int>(parser_.services_.vec.size()); + }; + + std::unique_ptr<const grpc_cpp_generator::Service> service(int i) const { + return std::unique_ptr<const grpc_cpp_generator::Service> ( + new FlatBufService(parser_.services_.vec[i])); + } + + std::unique_ptr<grpc_cpp_generator::Printer> CreatePrinter(std::string *str) const { + return std::unique_ptr<grpc_cpp_generator::Printer>( + new FlatBufPrinter(str)); + } + + private: + const Parser &parser_; + const std::string &file_name_; +}; + +bool GenerateGRPC(const Parser &parser, + const std::string &/*path*/, + const std::string &file_name) { + + int nservices = 0; + for (auto it = parser.services_.vec.begin(); + it != parser.services_.vec.end(); ++it) { + if (!(*it)->generated) nservices++; + } + if (!nservices) return true; + + grpc_cpp_generator::Parameters generator_parameters; + // TODO(wvo): make the other parameters in this struct configurable. + generator_parameters.use_system_headers = true; + + FlatBufFile fbfile(parser, file_name); + + std::string header_code = + grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters); + + std::string source_code = + grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) + + grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) + + grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) + + grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters); + + return flatbuffers::SaveFile((file_name + ".grpc.fb.h").c_str(), + header_code, false) && + flatbuffers::SaveFile((file_name + ".grpc.fb.cc").c_str(), + source_code, false); +} + +} // namespace flatbuffers + diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index d724b80f..32a06f1e 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -22,14 +22,71 @@ #include "flatbuffers/code_generators.h" namespace flatbuffers { + +static std::string GeneratedFileName(const std::string &path, + const std::string &file_name) { + return path + file_name + "_generated.js"; +} + namespace js { +// Iterate through all definitions we haven't generate code for (enums, structs, +// and tables) and output them to a single file. +class JsGenerator : public BaseGenerator { + public: + JsGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "", "."){}; + // Iterate through all definitions we haven't generate code for (enums, + // structs, and tables) and output them to a single file. + bool generate() { + if (IsEverythingGenerated()) return true; + + std::string enum_code, struct_code, exports_code, code; + generateEnums(&enum_code, &exports_code); + generateStructs(&struct_code, &exports_code); + + code = code + "// " + FlatBuffersGeneratedWarning(); + + // Generate code for all the namespace declarations. + GenNamespaces(&code, &exports_code); + + // Output the main declaration code from above. + code += enum_code; + code += struct_code; + + if (!exports_code.empty() && !parser_.opts.skip_js_exports) { + code += "// Exports for Node.js and RequireJS\n"; + code += exports_code; + } + + return SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false); + } + + private: + // Generate code for all enums. + void generateEnums(std::string *enum_code_ptr, + std::string *exports_code_ptr) { + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + auto &enum_def = **it; + GenEnum(enum_def, enum_code_ptr, exports_code_ptr); + } + } -static void GenNamespaces(const Parser &parser, std::string *code_ptr, - std::string *exports_ptr) { + // Generate code for all structs. + void generateStructs(std::string *decl_code_ptr, + std::string *exports_code_ptr) { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + GenStruct(struct_def, decl_code_ptr, exports_code_ptr); + } + } + void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) { std::set<std::string> namespaces; - for (auto it = parser.namespaces_.begin(); - it != parser.namespaces_.end(); ++it) { + for (auto it = parser_.namespaces_.begin(); + it != parser_.namespaces_.end(); ++it) { std::string namespace_so_far; // Gather all parent namespaces for this namespace @@ -62,22 +119,6 @@ static void GenNamespaces(const Parser &parser, std::string *code_ptr, } } -// Ensure that a type is prefixed with its namespace whenever it is used -// outside of its namespace. -static std::string WrapInNameSpace(const Namespace *ns, - const std::string &name) { - std::string qualified_name; - for (auto it = ns->components.begin(); - it != ns->components.end(); ++it) { - qualified_name += *it + "."; - } - return qualified_name + name; -} - -static std::string WrapInNameSpace(const Definition &def) { - return WrapInNameSpace(def.defined_namespace, def.name); -} - // Generate a documentation comment, if available. static void GenDocComment(const std::vector<std::string> &dc, std::string *code_ptr, @@ -123,7 +164,7 @@ static void GenDocComment(std::string *code_ptr, } // Generate an enum declaration and an enum string lookup table. -static void GenEnum(EnumDef &enum_def, std::string *code_ptr, +void GenEnum(EnumDef &enum_def, std::string *code_ptr, std::string *exports_ptr) { if (enum_def.generated) return; std::string &code = *code_ptr; @@ -170,7 +211,7 @@ static std::string GenType(const Type &type) { } } -static std::string GenGetter(const Type &type, const std::string &arguments) { +std::string GenGetter(const Type &type, const std::string &arguments) { switch (type.base_type) { case BASE_TYPE_STRING: return "this.bb.__string" + arguments; case BASE_TYPE_STRUCT: return "this.bb.__struct" + arguments; @@ -190,7 +231,7 @@ static std::string GenGetter(const Type &type, const std::string &arguments) { } } -static std::string GenDefaultValue(const Value &value, const std::string &context) { +std::string GenDefaultValue(const Value &value, const std::string &context) { if (value.type.enum_def) { if (auto val = value.type.enum_def->ReverseLookup( atoi(value.constant.c_str()), false)) { @@ -217,7 +258,7 @@ static std::string GenDefaultValue(const Value &value, const std::string &contex } } -static std::string GenTypeName(const Type &type, bool input) { +std::string GenTypeName(const Type &type, bool input) { if (!input) { if (type.base_type == BASE_TYPE_STRING) { return "string|Uint8Array"; @@ -269,7 +310,7 @@ static std::string MaybeScale(T value) { return value != 1 ? " * " + NumToString(value) : ""; } -static void GenStructArgs(const StructDef &struct_def, +void GenStructArgs(const StructDef &struct_def, std::string *annotations, std::string *arguments, const std::string &nameprefix) { @@ -320,8 +361,7 @@ static void GenStructBody(const StructDef &struct_def, } // Generate an accessor struct with constructor for a flatbuffers struct. -static void GenStruct(const Parser &parser, StructDef &struct_def, - std::string *code_ptr, std::string *exports_ptr) { +void GenStruct(StructDef &struct_def, std::string *code_ptr, std::string *exports_ptr) { if (struct_def.generated) return; std::string &code = *code_ptr; std::string &exports = *exports_ptr; @@ -375,13 +415,13 @@ static void GenStruct(const Parser &parser, StructDef &struct_def, code += "};\n\n"; // Generate the identifier check method - if (parser.root_struct_def_ == &struct_def && - !parser.file_identifier_.empty()) { + if (parser_.root_struct_def_ == &struct_def && + !parser_.file_identifier_.empty()) { GenDocComment(code_ptr, "@param {flatbuffers.ByteBuffer} bb\n" "@returns {boolean}"); code += object_name + ".bufferHasIdentifier = function(bb) {\n"; - code += " return bb.__has_identifier('" + parser.file_identifier_; + code += " return bb.__has_identifier('" + parser_.file_identifier_; code += "');\n};\n\n"; } } @@ -644,83 +684,21 @@ static void GenStruct(const Parser &parser, StructDef &struct_def, code += "};\n\n"; // Generate the method to complete buffer construction - if (parser.root_struct_def_ == &struct_def) { + if (parser_.root_struct_def_ == &struct_def) { GenDocComment(code_ptr, "@param {flatbuffers.Builder} builder\n" "@param {flatbuffers.Offset} offset"); code += object_name + ".finish" + struct_def.name + "Buffer"; code += " = function(builder, offset) {\n"; code += " builder.finish(offset"; - if (!parser.file_identifier_.empty()) { - code += ", '" + parser.file_identifier_ + "'"; + if (!parser_.file_identifier_.empty()) { + code += ", '" + parser_.file_identifier_ + "'"; } code += ");\n"; code += "};\n\n"; } } } - -} // namespace js - -static std::string GeneratedFileName(const std::string &path, - const std::string &file_name) { - return path + file_name + "_generated.js"; -} - -namespace js { -// Iterate through all definitions we haven't generate code for (enums, structs, -// and tables) and output them to a single file. -class JsGenerator : public BaseGenerator { - public: - JsGenerator(const Parser &parser, const std::string &path, - const std::string &file_name) - : BaseGenerator(parser, path, file_name){}; - // Iterate through all definitions we haven't generate code for (enums, - // structs, and tables) and output them to a single file. - bool generate() { - if (IsEverythingGenerated()) return true; - - std::string enum_code, struct_code, exports_code, code; - generateEnums(&enum_code, &exports_code); - generateStructs(&struct_code, &exports_code); - - code = code + "// " + FlatBuffersGeneratedWarning(); - - // Generate code for all the namespace declarations. - GenNamespaces(parser_, &code, &exports_code); - - // Output the main declaration code from above. - code += enum_code; - code += struct_code; - - if (!exports_code.empty() && !parser_.opts.skip_js_exports) { - code += "// Exports for Node.js and RequireJS\n"; - code += exports_code; - } - - return SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false); - } - - private: - // Generate code for all enums. - void generateEnums(std::string *enum_code_ptr, - std::string *exports_code_ptr) { - for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); - ++it) { - auto &enum_def = **it; - GenEnum(enum_def, enum_code_ptr, exports_code_ptr); - } - } - - // Generate code for all structs. - void generateStructs(std::string *decl_code_ptr, - std::string *exports_code_ptr) { - for (auto it = parser_.structs_.vec.begin(); - it != parser_.structs_.vec.end(); ++it) { - auto &struct_def = **it; - GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr); - } - } }; } // namespace js diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp index 599d5571..af3b06d5 100644 --- a/src/idl_gen_php.cpp +++ b/src/idl_gen_php.cpp @@ -25,35 +25,74 @@ namespace flatbuffers { namespace php { + // Hardcode spaces per indentation. + const std::string Indent = " "; + class PhpGenerator : public BaseGenerator { + public: + PhpGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "\\", "\\"){}; + bool generate() { + if (!generateEnums()) return false; + if (!generateStructs()) return false; + return true; + } - static std::string GenGetter(const Type &type); - static std::string GenDefaultValue(const Value &value); - static std::string GenMethod(const FieldDef &field); - static void GenStructBuilder(const StructDef &struct_def, - std::string *code_ptr); - static std::string GenTypeBasic(const Type &type); - static std::string GenTypeGet(const Type &type); - - // Ensure that a type is prefixed with its namespace whenever it is used - // outside of its namespace. - static std::string WrapInNameSpace(const Namespace *ns, - const std::string &name) { - std::string qualified_name = "\\"; - for (auto it = ns->components.begin(); - it != ns->components.end(); ++it) { - qualified_name += *it + "\\"; + private: + bool generateEnums() { + for (auto it = parser_.enums_.vec.begin(); + it != parser_.enums_.vec.end(); ++it) { + auto &enum_def = **it; + std::string enumcode; + GenEnum(enum_def, &enumcode); + if (!SaveType(enum_def, enumcode, false)) return false; + } + return true; } - return qualified_name + name; - } - static std::string WrapInNameSpace(const Definition &def) { - return WrapInNameSpace(def.defined_namespace, def.name); - } + bool generateStructs() { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + std::string declcode; + GenStruct(struct_def, &declcode); + if (!SaveType(struct_def, declcode, true)) return false; + } + return true; + } + // Begin by declaring namespace and imports. + void BeginFile(const std::string name_space_name, + const bool needs_imports, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "<?php\n"; + code = code + "// " + FlatBuffersGeneratedWarning(); + code += "namespace " + name_space_name + ";\n\n"; - // Hardcode spaces per indentation. - const std::string Indent = " "; + if (needs_imports) { + code += "use \\Google\\FlatBuffers\\Struct;\n"; + code += "use \\Google\\FlatBuffers\\Table;\n"; + code += "use \\Google\\FlatBuffers\\ByteBuffer;\n"; + code += "use \\Google\\FlatBuffers\\FlatBufferBuilder;\n"; + code += "\n"; + } + } + + // Save out the generated code for a Php Table type. + bool SaveType(const Definition &def, const std::string &classcode, + bool needs_imports) { + if (!classcode.length()) return true; + + std::string code = ""; + BeginFile(FullNamespace("\\", *def.defined_namespace), + needs_imports, &code); + code += classcode; + std::string filename = NamespaceDir(*def.defined_namespace) + + kPathSeparator + def.name + ".php"; + return SaveFile(filename.c_str(), code, false); + } + // Begin a class declaration. static void BeginClass(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; @@ -189,8 +228,7 @@ namespace php { } // Get the value of a table's scalar. - static void GetScalarFieldOfTable(const FieldDef &field, - std::string *code_ptr) { + void GetScalarFieldOfTable(const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; std::string getter = GenGetter(field.value.type); @@ -213,8 +251,7 @@ namespace php { // Get a struct by initializing an existing struct. // Specific to Struct. - static void GetStructFieldOfStruct(const FieldDef &field, - std::string *code_ptr) { + void GetStructFieldOfStruct(const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; code += Indent + "/**\n"; @@ -233,8 +270,7 @@ namespace php { // Get a struct by initializing an existing struct. // Specific to Table. - static void GetStructFieldOfTable(const FieldDef &field, - std::string *code_ptr) { + void GetStructFieldOfTable(const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; code += Indent + "public function get"; @@ -260,8 +296,7 @@ namespace php { } // Get the value of a string. - static void GetStringField(const FieldDef &field, - std::string *code_ptr) { + void GetStringField(const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; code += Indent + "public function get"; code += MakeCamel(field.name); @@ -278,8 +313,7 @@ namespace php { } // Get the value of a union from an object. - static void GetUnionField(const FieldDef &field, - std::string *code_ptr) { + void GetUnionField(const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; code += Indent + "/**\n"; @@ -298,9 +332,8 @@ namespace php { } // Get the value of a vector's struct member. - static void GetMemberOfVectorOfStruct(const StructDef &struct_def, - const FieldDef &field, - std::string *code_ptr) { + void GetMemberOfVectorOfStruct(const StructDef &struct_def, + const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; auto vectortype = field.value.type.VectorType(); @@ -362,7 +395,7 @@ namespace php { // Get the value of a vector's non-struct member. Uses a named return // argument to conveniently set the zero value for the result. - static void GetMemberOfVectorOfNonStruct(const FieldDef &field, + void GetMemberOfVectorOfNonStruct(const FieldDef &field, std::string *code_ptr) { std::string &code = *code_ptr; auto vectortype = field.value.type.VectorType(); @@ -603,9 +636,7 @@ namespace php { } // Get the offset of the end of a table. - static void GetEndOffsetOnTable(const Parser &parser, - const StructDef &struct_def, - std::string *code_ptr) { + void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; @@ -632,7 +663,7 @@ namespace php { code += Indent + Indent + "return $o;\n"; code += Indent + "}\n"; - if (parser.root_struct_def_ == &struct_def) { + if (parser_.root_struct_def_ == &struct_def) { code += "\n"; code += Indent + "public static function finish"; code += struct_def.name; @@ -640,16 +671,15 @@ namespace php { code += Indent + "{\n"; code += Indent + Indent + "$builder->finish($offset"; - if (parser.file_identifier_.length()) - code += ", \"" + parser.file_identifier_ + "\""; + if (parser_.file_identifier_.length()) + code += ", \"" + parser_.file_identifier_ + "\""; code += ");\n"; code += Indent + "}\n"; } } // Generate a struct field, conditioned on its child type(s). - static void GenStructAccessor(const StructDef &struct_def, - const FieldDef &field, + void GenStructAccessor(const StructDef &struct_def, const FieldDef &field, std::string *code_ptr) { GenComment(field.doc_comment, code_ptr, nullptr); @@ -696,9 +726,7 @@ namespace php { } // Generate table constructors, conditioned on its members' types. - static void GenTableBuilders(const Parser &parser, - const StructDef &struct_def, - std::string *code_ptr) { + void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) { GetStartOfTable(struct_def, code_ptr); for (auto it = struct_def.fields.vec.begin(); @@ -725,11 +753,11 @@ namespace php { } } - GetEndOffsetOnTable(parser, struct_def, code_ptr); + GetEndOffsetOnTable(struct_def, code_ptr); } // Generate struct or table methods. - static void GenStruct(const Parser &parser, const StructDef &struct_def, + void GenStruct(const StructDef &struct_def, std::string *code_ptr) { if (struct_def.generated) return; @@ -744,13 +772,13 @@ namespace php { std::string &code = *code_ptr; if (!struct_def.fixed) { - if (parser.file_identifier_.length()) { + if (parser_.file_identifier_.length()) { // Return the identifier code += Indent + "public static function " + struct_def.name; code += "Identifier()\n"; code += Indent + "{\n"; code += Indent + Indent + "return \""; - code += parser.file_identifier_ + "\";\n"; + code += parser_.file_identifier_ + "\";\n"; code += Indent + "}\n\n"; // Check if a buffer has the identifier. @@ -763,12 +791,12 @@ namespace php { code += Indent + "}\n\n"; } - if (parser.file_extension_.length()) { + if (parser_.file_extension_.length()) { // Return the extension code += Indent + "public static function " + struct_def.name; code += "Extension()\n"; code += Indent + "{\n"; - code += Indent + Indent + "return \"" + parser.file_extension_; + code += Indent + Indent + "return \"" + parser_.file_extension_; code += "\";\n"; code += Indent + "}\n\n"; } @@ -791,7 +819,7 @@ namespace php { GenStructBuilder(struct_def, code_ptr); } else { // Create a set of functions that allow table construction. - GenTableBuilders(parser, struct_def, code_ptr); + GenTableBuilders(struct_def, code_ptr); } EndClass(code_ptr); } @@ -859,7 +887,7 @@ namespace php { return ctypename[type.base_type]; } - static std::string GenDefaultValue(const Value &value) { + std::string GenDefaultValue(const Value &value) { if (value.type.enum_def) { if (auto val = value.type.enum_def->ReverseLookup( atoi(value.constant.c_str()), false)) { @@ -927,71 +955,7 @@ namespace php { code += Indent + Indent + "return $builder->offset();\n"; code += Indent + "}\n"; } - - class PhpGenerator : public BaseGenerator { - public: - PhpGenerator(const Parser &parser, const std::string &path, - const std::string &file_name) - : BaseGenerator(parser, path, file_name){}; - bool generate() { - if (!generateEnums()) return false; - if (!generateStructs()) return false; - return true; - } - - private: - bool generateEnums() { - for (auto it = parser_.enums_.vec.begin(); - it != parser_.enums_.vec.end(); ++it) { - auto &enum_def = **it; - std::string enumcode; - GenEnum(enum_def, &enumcode); - if (!SaveType(enum_def, enumcode, false)) return false; - } - return true; - } - - bool generateStructs() { - for (auto it = parser_.structs_.vec.begin(); - it != parser_.structs_.vec.end(); ++it) { - auto &struct_def = **it; - std::string declcode; - GenStruct(parser_, struct_def, &declcode); - if (!SaveType(struct_def, declcode, true)) return false; - } - return true; - } - - // Begin by declaring namespace and imports. - void BeginFile(const std::string name_space_name, - const bool needs_imports, std::string *code_ptr) { - std::string &code = *code_ptr; - code += "<?php\n"; - code = code + "// " + FlatBuffersGeneratedWarning(); - code += "namespace " + name_space_name + ";\n\n"; - - if (needs_imports) { - code += "use \\Google\\FlatBuffers\\Struct;\n"; - code += "use \\Google\\FlatBuffers\\Table;\n"; - code += "use \\Google\\FlatBuffers\\ByteBuffer;\n"; - code += "use \\Google\\FlatBuffers\\FlatBufferBuilder;\n"; - code += "\n"; - } - } - - // Save out the generated code for a Php Table type. - bool SaveType(const Definition &def, const std::string &classcode, - bool needs_imports) { - if (!classcode.length()) return true; - - std::string code = ""; - BeginFile(FullNamespace("\\"), needs_imports, &code); - code += classcode; - - std::string filename = - namespace_dir_ + kPathSeparator + def.name + ".php"; - return SaveFile(filename.c_str(), code, false); - } + }; } // namespace php diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 2917854d..52944bfc 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -595,7 +595,8 @@ class PythonGenerator : public BaseGenerator { public: PythonGenerator(const Parser &parser, const std::string &path, const std::string &file_name) - : BaseGenerator(parser, path, file_name){}; + : BaseGenerator(parser, path, file_name, "" /* not used */, + "" /* not used */){}; bool generate() { if (!generateEnums()) return false; if (!generateStructs()) return false; @@ -651,9 +652,10 @@ class PythonGenerator : public BaseGenerator { } std::string code = ""; - BeginFile(LastNamespacePart(), needs_imports, &code); + BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code); code += classcode; - std::string filename = namespace_dir_ + kPathSeparator + def.name + ".py"; + std::string filename = NamespaceDir(*def.defined_namespace) + + kPathSeparator + def.name + ".py"; return SaveFile(filename.c_str(), code, false); } }; diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index f5badab1..fc213675 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -17,6 +17,14 @@ #include <algorithm> #include <list> +#ifdef _WIN32 +#if !defined(_USE_MATH_DEFINES) +#define _USE_MATH_DEFINES // For M_PI. +#endif // !defined(_USE_MATH_DEFINES) +#endif // _WIN32 + +#include <math.h> + #include "flatbuffers/idl.h" #include "flatbuffers/util.h" @@ -415,6 +423,12 @@ CheckedError Parser::Next() { return NoError(); } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') { const char *start = cursor_ - 1; + if (c == '-' && *cursor_ == '0' && (cursor_[1] == 'x' || cursor_[1] == 'X')) { + ++start; + ++cursor_; + attribute_.append(&c, &c + 1); + c = '0'; + } if (c == '0' && (*cursor_ == 'x' || *cursor_ == 'X')) { cursor_++; while (isxdigit(static_cast<unsigned char>(*cursor_))) cursor_++; @@ -575,9 +589,9 @@ CheckedError Parser::ParseField(StructDef &struct_def) { FieldDef *typefield = nullptr; if (type.base_type == BASE_TYPE_UNION) { // For union fields, add a second auto-generated field to hold the type, - // with _type appended as the name. - ECHECK(AddField(struct_def, name + "_type", type.enum_def->underlying_type, - &typefield)); + // with a special suffix. + ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(), + type.enum_def->underlying_type, &typefield)); } FieldDef *field; @@ -678,17 +692,45 @@ CheckedError Parser::ParseField(StructDef &struct_def) { } CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, - size_t parent_fieldn) { + size_t parent_fieldn, + const StructDef *parent_struct_def) { switch (val.type.base_type) { case BASE_TYPE_UNION: { assert(field); + std::string constant; if (!parent_fieldn || - field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE) - return Error("missing type field before this union value: " + - field->name); + field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE) { + // We haven't seen the type field yet. Sadly a lot of JSON writers + // output these in alphabetical order, meaning it comes after this + // value. So we scan past the value to find it, then come back here. + auto type_name = field->name + UnionTypeFieldSuffix(); + assert(parent_struct_def); + auto type_field = parent_struct_def->fields.Lookup(type_name); + assert(type_field); // Guaranteed by ParseField(). + // Remember where we are in the source file, so we can come back here. + auto backup = *static_cast<ParserState *>(this); + ECHECK(SkipAnyJsonValue()); // The table. + EXPECT(','); + auto next_name = attribute_; + if (Is(kTokenStringConstant)) { + NEXT(); + } else { + EXPECT(kTokenIdentifier); + } + if (next_name != type_name) + return Error("missing type field after this union value: " + + type_name); + EXPECT(':'); + Value type_val = type_field->value; + ECHECK(ParseAnyValue(type_val, type_field, 0, nullptr)); + constant = type_val.constant; + // Got the information we needed, now rewind: + *static_cast<ParserState *>(this) = backup; + } else { + constant = field_stack_.back().first.constant; + } uint8_t enum_idx; - ECHECK(atot(field_stack_.back().first.constant.c_str(), *this, - &enum_idx)); + ECHECK(atot(constant.c_str(), *this, &enum_idx)); auto enum_val = val.type.enum_def->ReverseLookup(enum_idx); if (!enum_val) return Error("illegal type id for: " + field->name); ECHECK(ParseTable(*enum_val->struct_def, &val.constant, nullptr)); @@ -763,7 +805,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, NEXT(); // Ignore this field. } else { Value val = field->value; - ECHECK(ParseAnyValue(val, field, fieldn)); + ECHECK(ParseAnyValue(val, field, fieldn, &struct_def)); size_t i = field_stack_.size(); // Hardcoded insertion-sort with error-check. // If fields are specified in order, then this loop exits immediately. @@ -862,7 +904,7 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) { if ((!opts.strict_json || !count) && Is(']')) { NEXT(); break; } Value val; val.type = type; - ECHECK(ParseAnyValue(val, nullptr, 0)); + ECHECK(ParseAnyValue(val, nullptr, 0, nullptr)); field_stack_.push_back(std::make_pair(val, nullptr)); count++; if (Is(']')) { NEXT(); break; } @@ -1004,8 +1046,30 @@ CheckedError Parser::ParseHash(Value &e, FieldDef* field) { } CheckedError Parser::ParseSingleValue(Value &e) { - // First check if this could be a string/identifier enum value: - if (e.type.base_type != BASE_TYPE_STRING && + // First see if this could be a conversion function: + if (token_ == kTokenIdentifier && *cursor_ == '(') { + auto functionname = attribute_; + NEXT(); + EXPECT('('); + ECHECK(ParseSingleValue(e)); + EXPECT(')'); + #define FLATBUFFERS_FN_DOUBLE(name, op) \ + if (functionname == name) { \ + auto x = strtod(e.constant.c_str(), nullptr); \ + e.constant = NumToString(op); \ + } + FLATBUFFERS_FN_DOUBLE("deg", x / M_PI * 180); + FLATBUFFERS_FN_DOUBLE("rad", x * M_PI / 180); + FLATBUFFERS_FN_DOUBLE("sin", sin(x)); + FLATBUFFERS_FN_DOUBLE("cos", cos(x)); + FLATBUFFERS_FN_DOUBLE("tan", tan(x)); + FLATBUFFERS_FN_DOUBLE("asin", asin(x)); + FLATBUFFERS_FN_DOUBLE("acos", acos(x)); + FLATBUFFERS_FN_DOUBLE("atan", atan(x)); + // TODO(wvo): add more useful conversion functions here. + #undef FLATBUFFERS_FN_DOUBLE + // Then check if this could be a string/identifier enum value: + } else if (e.type.base_type != BASE_TYPE_STRING && e.type.base_type != BASE_TYPE_NONE && (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) { if (IsIdentifierStart(attribute_[0])) { // Enum value. @@ -1150,7 +1214,13 @@ CheckedError Parser::ParseEnum(bool is_union, EnumDef **dest) { auto full_name = value_name; std::vector<std::string> value_comment = doc_comment_; EXPECT(kTokenIdentifier); - if (is_union) ECHECK(ParseNamespacing(&full_name, &value_name)); + if (is_union) { + ECHECK(ParseNamespacing(&full_name, &value_name)); + // Since we can't namespace the actual enum identifiers, turn + // namespace parts into part of the identifier. + value_name = full_name; + std::replace(value_name.begin(), value_name.end(), '.', '_'); + } auto prevsize = enum_def.vals.vec.size(); auto value = enum_def.vals.vec.size() ? enum_def.vals.vec.back()->value + 1 @@ -1288,7 +1358,8 @@ CheckedError Parser::ParseDecl() { } } - ECHECK(CheckClash(fields, struct_def, "_type", BASE_TYPE_UNION)); + ECHECK(CheckClash(fields, struct_def, UnionTypeFieldSuffix(), + BASE_TYPE_UNION)); ECHECK(CheckClash(fields, struct_def, "Type", BASE_TYPE_UNION)); ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_VECTOR)); ECHECK(CheckClash(fields, struct_def, "Length", BASE_TYPE_VECTOR)); @@ -1358,6 +1429,10 @@ void Parser::MarkGenerated() { it != structs_.vec.end(); ++it) { (*it)->generated = true; } + for (auto it = services_.vec.begin(); + it != services_.vec.end(); ++it) { + (*it)->generated = true; + } } CheckedError Parser::ParseNamespace() { @@ -2018,4 +2093,57 @@ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset< } } +std::string Parser::ConformTo(const Parser &base) { + for (auto sit = structs_.vec.begin(); sit != structs_.vec.end(); ++sit) { + auto &struct_def = **sit; + auto qualified_name = + struct_def.defined_namespace->GetFullyQualifiedName(struct_def.name); + auto struct_def_base = base.structs_.Lookup(qualified_name); + if (!struct_def_base) continue; + for (auto fit = struct_def.fields.vec.begin(); + fit != struct_def.fields.vec.end(); ++fit) { + auto &field = **fit; + auto field_base = struct_def_base->fields.Lookup(field.name); + if (field_base) { + if (field.value.offset != field_base->value.offset) + return "offsets differ for field: " + field.name; + if (field.value.constant != field_base->value.constant) + return "defaults differ for field: " + field.name; + if (!EqualByName(field.value.type, field_base->value.type)) + return "types differ for field: " + field.name; + } else { + // Doesn't have to exist, deleting fields is fine. + // But we should check if there is a field that has the same offset + // but is incompatible (in the case of field renaming). + for (auto fbit = struct_def_base->fields.vec.begin(); + fbit != struct_def_base->fields.vec.end(); ++fbit) { + field_base = *fbit; + if (field.value.offset == field_base->value.offset) { + if (!EqualByName(field.value.type, field_base->value.type)) + return "field renamed to different type: " + field.name; + break; + } + } + } + } + } + for (auto eit = enums_.vec.begin(); eit != enums_.vec.end(); ++eit) { + auto &enum_def = **eit; + auto qualified_name = + enum_def.defined_namespace->GetFullyQualifiedName(enum_def.name); + auto enum_def_base = base.enums_.Lookup(qualified_name); + if (!enum_def_base) continue; + for (auto evit = enum_def.vals.vec.begin(); + evit != enum_def.vals.vec.end(); ++evit) { + auto &enum_val = **evit; + auto enum_val_base = enum_def_base->vals.Lookup(enum_val.name); + if (enum_val_base) { + if (enum_val.value != enum_val_base->value) + return "values differ for enum: " + enum_val.name; + } + } + } + return ""; +} + } // namespace flatbuffers diff --git a/tests/MyGame/Example/Any.cs b/tests/MyGame/Example/Any.cs index 1f018ad8..8fdc2fca 100644 --- a/tests/MyGame/Example/Any.cs +++ b/tests/MyGame/Example/Any.cs @@ -8,6 +8,7 @@ public enum Any : byte NONE = 0, Monster = 1, TestSimpleTableWithEnum = 2, + MyGame_Example2_Monster = 3, }; diff --git a/tests/MyGame/Example/Any.go b/tests/MyGame/Example/Any.go index 0322364c..df9f823f 100644 --- a/tests/MyGame/Example/Any.go +++ b/tests/MyGame/Example/Any.go @@ -6,4 +6,5 @@ const ( AnyNONE = 0 AnyMonster = 1 AnyTestSimpleTableWithEnum = 2 + AnyMyGame_Example2_Monster = 3 ) diff --git a/tests/MyGame/Example/Any.java b/tests/MyGame/Example/Any.java index 27dc1bcb..6e4fb76c 100644 --- a/tests/MyGame/Example/Any.java +++ b/tests/MyGame/Example/Any.java @@ -7,9 +7,10 @@ public final class Any { public static final byte NONE = 0; public static final byte Monster = 1; public static final byte TestSimpleTableWithEnum = 2; + public static final byte MyGame_Example2_Monster = 3; - private static final String[] names = { "NONE", "Monster", "TestSimpleTableWithEnum", }; + public static final String[] names = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", }; public static String name(int e) { return names[e]; } -}; +} diff --git a/tests/MyGame/Example/Any.php b/tests/MyGame/Example/Any.php index f04f4ad3..da691760 100644 --- a/tests/MyGame/Example/Any.php +++ b/tests/MyGame/Example/Any.php @@ -8,11 +8,13 @@ class Any const NONE = 0; const Monster = 1; const TestSimpleTableWithEnum = 2; + const MyGame_Example2_Monster = 3; private static $names = array( "NONE", "Monster", "TestSimpleTableWithEnum", + "MyGame_Example2_Monster", ); public static function Name($e) diff --git a/tests/MyGame/Example/Any.py b/tests/MyGame/Example/Any.py index b642ede0..f1b8d519 100644 --- a/tests/MyGame/Example/Any.py +++ b/tests/MyGame/Example/Any.py @@ -6,4 +6,5 @@ class Any(object): NONE = 0 Monster = 1 TestSimpleTableWithEnum = 2 + MyGame_Example2_Monster = 3 diff --git a/tests/MyGame/Example/Color.java b/tests/MyGame/Example/Color.java index 502ec9fb..7c113b72 100644 --- a/tests/MyGame/Example/Color.java +++ b/tests/MyGame/Example/Color.java @@ -8,8 +8,8 @@ public final class Color { public static final byte Green = 2; public static final byte Blue = 8; - private static final String[] names = { "Red", "Green", "", "", "", "", "", "Blue", }; + public static final String[] names = { "Red", "Green", "", "", "", "", "", "Blue", }; public static String name(int e) { return names[e - Red]; } -}; +} diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index 6101f464..fdfd2b0a 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -78,8 +78,10 @@ public sealed class Monster : Table { public bool MutateTestf2(float testf2) { int o = __offset(56); if (o != 0) { bb.PutFloat(o + bb_pos, testf2); return true; } else { return false; } } public float Testf3 { get { int o = __offset(58); return o != 0 ? bb.GetFloat(o + bb_pos) : (float)0.0f; } } public bool MutateTestf3(float testf3) { int o = __offset(58); if (o != 0) { bb.PutFloat(o + bb_pos, testf3); return true; } else { return false; } } + public string GetTestarrayofstring2(int j) { int o = __offset(60); return o != 0 ? __string(__vector(o) + j * 4) : null; } + public int Testarrayofstring2Length { get { int o = __offset(60); return o != 0 ? __vector_len(o) : 0; } } - public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(28); } + public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(29); } public static void AddPos(FlatBufferBuilder builder, Offset<Vec3> posOffset) { builder.AddStruct(0, posOffset.Value, 0); } public static void AddMana(FlatBufferBuilder builder, short mana) { builder.AddShort(1, mana, 150); } public static void AddHp(FlatBufferBuilder builder, short hp) { builder.AddShort(2, hp, 100); } @@ -118,6 +120,9 @@ public sealed class Monster : Table { public static void AddTestf(FlatBufferBuilder builder, float testf) { builder.AddFloat(25, testf, 3.14159f); } public static void AddTestf2(FlatBufferBuilder builder, float testf2) { builder.AddFloat(26, testf2, 3.0f); } public static void AddTestf3(FlatBufferBuilder builder, float testf3) { builder.AddFloat(27, testf3, 0.0f); } + public static void AddTestarrayofstring2(FlatBufferBuilder builder, VectorOffset testarrayofstring2Offset) { builder.AddOffset(28, testarrayofstring2Offset.Value, 0); } + public static VectorOffset CreateTestarrayofstring2Vector(FlatBufferBuilder builder, StringOffset[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); } + public static void StartTestarrayofstring2Vector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); } public static Offset<Monster> EndMonster(FlatBufferBuilder builder) { int o = builder.EndObject(); builder.Required(o, 10); // name diff --git a/tests/MyGame/Example/Monster.go b/tests/MyGame/Example/Monster.go index fbd849e8..f4cd18c1 100644 --- a/tests/MyGame/Example/Monster.go +++ b/tests/MyGame/Example/Monster.go @@ -43,6 +43,10 @@ func (rcv *Monster) Mana() int16 { return 150 } +func (rcv *Monster) MutateMana(n int16) bool { + return rcv._tab.MutateInt16Slot(6, n) +} + func (rcv *Monster) Hp() int16 { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { @@ -51,6 +55,10 @@ func (rcv *Monster) Hp() int16 { return 100 } +func (rcv *Monster) MutateHp(n int16) bool { + return rcv._tab.MutateInt16Slot(8, n) +} + func (rcv *Monster) Name() []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(10)) if o != 0 { @@ -92,6 +100,10 @@ func (rcv *Monster) Color() int8 { return 8 } +func (rcv *Monster) MutateColor(n int8) bool { + return rcv._tab.MutateInt8Slot(16, n) +} + func (rcv *Monster) TestType() byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(18)) if o != 0 { @@ -100,6 +112,10 @@ func (rcv *Monster) TestType() byte { return 0 } +func (rcv *Monster) MutateTestType(n byte) bool { + return rcv._tab.MutateByteSlot(18, n) +} + func (rcv *Monster) Test(obj *flatbuffers.Table) bool { o := flatbuffers.UOffsetT(rcv._tab.Offset(20)) if o != 0 { @@ -173,6 +189,8 @@ func (rcv *Monster) TestarrayoftablesLength() int { return 0 } +/// an example documentation comment: this will end up in the generated code +/// multiline too func (rcv *Monster) Enemy(obj *Monster) *Monster { o := flatbuffers.UOffsetT(rcv._tab.Offset(28)) if o != 0 { @@ -232,6 +250,10 @@ func (rcv *Monster) Testbool() byte { return 0 } +func (rcv *Monster) MutateTestbool(n byte) bool { + return rcv._tab.MutateByteSlot(34, n) +} + func (rcv *Monster) Testhashs32Fnv1() int32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(36)) if o != 0 { @@ -240,6 +262,10 @@ func (rcv *Monster) Testhashs32Fnv1() int32 { return 0 } +func (rcv *Monster) MutateTesthashs32Fnv1(n int32) bool { + return rcv._tab.MutateInt32Slot(36, n) +} + func (rcv *Monster) Testhashu32Fnv1() uint32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(38)) if o != 0 { @@ -248,6 +274,10 @@ func (rcv *Monster) Testhashu32Fnv1() uint32 { return 0 } +func (rcv *Monster) MutateTesthashu32Fnv1(n uint32) bool { + return rcv._tab.MutateUint32Slot(38, n) +} + func (rcv *Monster) Testhashs64Fnv1() int64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(40)) if o != 0 { @@ -256,6 +286,10 @@ func (rcv *Monster) Testhashs64Fnv1() int64 { return 0 } +func (rcv *Monster) MutateTesthashs64Fnv1(n int64) bool { + return rcv._tab.MutateInt64Slot(40, n) +} + func (rcv *Monster) Testhashu64Fnv1() uint64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(42)) if o != 0 { @@ -264,6 +298,10 @@ func (rcv *Monster) Testhashu64Fnv1() uint64 { return 0 } +func (rcv *Monster) MutateTesthashu64Fnv1(n uint64) bool { + return rcv._tab.MutateUint64Slot(42, n) +} + func (rcv *Monster) Testhashs32Fnv1a() int32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(44)) if o != 0 { @@ -272,6 +310,10 @@ func (rcv *Monster) Testhashs32Fnv1a() int32 { return 0 } +func (rcv *Monster) MutateTesthashs32Fnv1a(n int32) bool { + return rcv._tab.MutateInt32Slot(44, n) +} + func (rcv *Monster) Testhashu32Fnv1a() uint32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(46)) if o != 0 { @@ -280,6 +322,10 @@ func (rcv *Monster) Testhashu32Fnv1a() uint32 { return 0 } +func (rcv *Monster) MutateTesthashu32Fnv1a(n uint32) bool { + return rcv._tab.MutateUint32Slot(46, n) +} + func (rcv *Monster) Testhashs64Fnv1a() int64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(48)) if o != 0 { @@ -288,6 +334,10 @@ func (rcv *Monster) Testhashs64Fnv1a() int64 { return 0 } +func (rcv *Monster) MutateTesthashs64Fnv1a(n int64) bool { + return rcv._tab.MutateInt64Slot(48, n) +} + func (rcv *Monster) Testhashu64Fnv1a() uint64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(50)) if o != 0 { @@ -296,6 +346,10 @@ func (rcv *Monster) Testhashu64Fnv1a() uint64 { return 0 } +func (rcv *Monster) MutateTesthashu64Fnv1a(n uint64) bool { + return rcv._tab.MutateUint64Slot(50, n) +} + func (rcv *Monster) Testarrayofbools(j int) byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(52)) if o != 0 { @@ -321,6 +375,10 @@ func (rcv *Monster) Testf() float32 { return 3.14159 } +func (rcv *Monster) MutateTestf(n float32) bool { + return rcv._tab.MutateFloat32Slot(54, n) +} + func (rcv *Monster) Testf2() float32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(56)) if o != 0 { @@ -329,6 +387,10 @@ func (rcv *Monster) Testf2() float32 { return 3.0 } +func (rcv *Monster) MutateTestf2(n float32) bool { + return rcv._tab.MutateFloat32Slot(56, n) +} + func (rcv *Monster) Testf3() float32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(58)) if o != 0 { @@ -337,7 +399,28 @@ func (rcv *Monster) Testf3() float32 { return 0.0 } -func MonsterStart(builder *flatbuffers.Builder) { builder.StartObject(28) } +func (rcv *Monster) MutateTestf3(n float32) bool { + return rcv._tab.MutateFloat32Slot(58, n) +} + +func (rcv *Monster) Testarrayofstring2(j int) []byte { + o := flatbuffers.UOffsetT(rcv._tab.Offset(60)) + if o != 0 { + a := rcv._tab.Vector(o) + return rcv._tab.ByteVector(a + flatbuffers.UOffsetT(j * 4)) + } + return nil +} + +func (rcv *Monster) Testarrayofstring2Length() int { + o := flatbuffers.UOffsetT(rcv._tab.Offset(60)) + if o != 0 { + return rcv._tab.VectorLen(o) + } + return 0 +} + +func MonsterStart(builder *flatbuffers.Builder) { builder.StartObject(29) } func MonsterAddPos(builder *flatbuffers.Builder, pos flatbuffers.UOffsetT) { builder.PrependStructSlot(0, flatbuffers.UOffsetT(pos), 0) } func MonsterAddMana(builder *flatbuffers.Builder, mana int16) { builder.PrependInt16Slot(1, mana, 150) } func MonsterAddHp(builder *flatbuffers.Builder, hp int16) { builder.PrependInt16Slot(2, hp, 100) } @@ -377,4 +460,7 @@ func MonsterStartTestarrayofboolsVector(builder *flatbuffers.Builder, numElems i func MonsterAddTestf(builder *flatbuffers.Builder, testf float32) { builder.PrependFloat32Slot(25, testf, 3.14159) } func MonsterAddTestf2(builder *flatbuffers.Builder, testf2 float32) { builder.PrependFloat32Slot(26, testf2, 3.0) } func MonsterAddTestf3(builder *flatbuffers.Builder, testf3 float32) { builder.PrependFloat32Slot(27, testf3, 0.0) } +func MonsterAddTestarrayofstring2(builder *flatbuffers.Builder, testarrayofstring2 flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(28, flatbuffers.UOffsetT(testarrayofstring2), 0) } +func MonsterStartTestarrayofstring2Vector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(4, numElems, 4) +} func MonsterEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index d85e4baa..dc27f844 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -84,8 +84,10 @@ public final class Monster extends Table { public boolean mutateTestf2(float testf2) { int o = __offset(56); if (o != 0) { bb.putFloat(o + bb_pos, testf2); return true; } else { return false; } } public float testf3() { int o = __offset(58); return o != 0 ? bb.getFloat(o + bb_pos) : 0.0f; } public boolean mutateTestf3(float testf3) { int o = __offset(58); if (o != 0) { bb.putFloat(o + bb_pos, testf3); return true; } else { return false; } } + public String testarrayofstring2(int j) { int o = __offset(60); return o != 0 ? __string(__vector(o) + j * 4) : null; } + public int testarrayofstring2Length() { int o = __offset(60); return o != 0 ? __vector_len(o) : 0; } - public static void startMonster(FlatBufferBuilder builder) { builder.startObject(28); } + public static void startMonster(FlatBufferBuilder builder) { builder.startObject(29); } public static void addPos(FlatBufferBuilder builder, int posOffset) { builder.addStruct(0, posOffset, 0); } public static void addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); } public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); } @@ -124,11 +126,14 @@ public final class Monster extends Table { public static void addTestf(FlatBufferBuilder builder, float testf) { builder.addFloat(25, testf, 3.14159f); } public static void addTestf2(FlatBufferBuilder builder, float testf2) { builder.addFloat(26, testf2, 3.0f); } public static void addTestf3(FlatBufferBuilder builder, float testf3) { builder.addFloat(27, testf3, 0.0f); } + public static void addTestarrayofstring2(FlatBufferBuilder builder, int testarrayofstring2Offset) { builder.addOffset(28, testarrayofstring2Offset, 0); } + public static int createTestarrayofstring2Vector(FlatBufferBuilder builder, int[] data) { builder.startVector(4, data.length, 4); for (int i = data.length - 1; i >= 0; i--) builder.addOffset(data[i]); return builder.endVector(); } + public static void startTestarrayofstring2Vector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems, 4); } public static int endMonster(FlatBufferBuilder builder) { int o = builder.endObject(); builder.required(o, 10); // name return o; } public static void finishMonsterBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset, "MONS"); } -}; +} diff --git a/tests/MyGame/Example/Monster.php b/tests/MyGame/Example/Monster.php index 9f82d5a9..c06cffbc 100644 --- a/tests/MyGame/Example/Monster.php +++ b/tests/MyGame/Example/Monster.php @@ -361,21 +361,40 @@ class Monster extends Table } /** + * @param int offset + * @return string + */ + public function getTestarrayofstring2($j) + { + $o = $this->__offset(60); + return $o != 0 ? $this->__string($this->__vector($o) + $j * 4) : 0; + } + + /** + * @return int + */ + public function getTestarrayofstring2Length() + { + $o = $this->__offset(60); + return $o != 0 ? $this->__vector_len($o) : 0; + } + + /** * @param FlatBufferBuilder $builder * @return void */ public static function startMonster(FlatBufferBuilder $builder) { - $builder->StartObject(28); + $builder->StartObject(29); } /** * @param FlatBufferBuilder $builder * @return Monster */ - public static function createMonster(FlatBufferBuilder $builder, $pos, $mana, $hp, $name, $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) + public static function createMonster(FlatBufferBuilder $builder, $pos, $mana, $hp, $name, $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) { - $builder->startObject(28); + $builder->startObject(29); self::addPos($builder, $pos); self::addMana($builder, $mana); self::addHp($builder, $hp); @@ -403,6 +422,7 @@ class Monster extends Table self::addTestf($builder, $testf); self::addTestf2($builder, $testf2); self::addTestf3($builder, $testf3); + self::addTestarrayofstring2($builder, $testarrayofstring2); $o = $builder->endObject(); $builder->required($o, 10); // name return $o; @@ -819,6 +839,40 @@ class Monster extends Table /** * @param FlatBufferBuilder $builder + * @param VectorOffset + * @return void + */ + public static function addTestarrayofstring2(FlatBufferBuilder $builder, $testarrayofstring2) + { + $builder->addOffsetX(28, $testarrayofstring2, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @param array offset array + * @return int vector offset + */ + public static function createTestarrayofstring2Vector(FlatBufferBuilder $builder, array $data) + { + $builder->startVector(4, count($data), 4); + for ($i = count($data) - 1; $i >= 0; $i--) { + $builder->addOffset($data[$i]); + } + return $builder->endVector(); + } + + /** + * @param FlatBufferBuilder $builder + * @param int $numElems + * @return void + */ + public static function startTestarrayofstring2Vector(FlatBufferBuilder $builder, $numElems) + { + $builder->startVector(4, $numElems, 4); + } + + /** + * @param FlatBufferBuilder $builder * @return int table offset */ public static function endMonster(FlatBufferBuilder $builder) diff --git a/tests/MyGame/Example/Monster.py b/tests/MyGame/Example/Monster.py index 40d7e8ac..a7a78363 100644 --- a/tests/MyGame/Example/Monster.py +++ b/tests/MyGame/Example/Monster.py @@ -282,7 +282,22 @@ class Monster(object): return self._tab.Get(flatbuffers.number_types.Float32Flags, o + self._tab.Pos) return 0.0 -def MonsterStart(builder): builder.StartObject(28) + # Monster + def Testarrayofstring2(self, j): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(60)) + if o != 0: + a = self._tab.Vector(o) + return self._tab.String(a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 4)) + return "" + + # Monster + def Testarrayofstring2Length(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(60)) + if o != 0: + return self._tab.VectorLen(o) + return 0 + +def MonsterStart(builder): builder.StartObject(29) def MonsterAddPos(builder, pos): builder.PrependStructSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(pos), 0) def MonsterAddMana(builder, mana): builder.PrependInt16Slot(1, mana, 150) def MonsterAddHp(builder, hp): builder.PrependInt16Slot(2, hp, 100) @@ -316,4 +331,6 @@ def MonsterStartTestarrayofboolsVector(builder, numElems): return builder.StartV def MonsterAddTestf(builder, testf): builder.PrependFloat32Slot(25, testf, 3.14159) def MonsterAddTestf2(builder, testf2): builder.PrependFloat32Slot(26, testf2, 3.0) def MonsterAddTestf3(builder, testf3): builder.PrependFloat32Slot(27, testf3, 0.0) +def MonsterAddTestarrayofstring2(builder, testarrayofstring2): builder.PrependUOffsetTRelativeSlot(28, flatbuffers.number_types.UOffsetTFlags.py_type(testarrayofstring2), 0) +def MonsterStartTestarrayofstring2Vector(builder, numElems): return builder.StartVector(4, numElems, 4) def MonsterEnd(builder): return builder.EndObject() diff --git a/tests/MyGame/Example/Stat.go b/tests/MyGame/Example/Stat.go index 1071cae8..8c56bfb0 100644 --- a/tests/MyGame/Example/Stat.go +++ b/tests/MyGame/Example/Stat.go @@ -37,6 +37,10 @@ func (rcv *Stat) Val() int64 { return 0 } +func (rcv *Stat) MutateVal(n int64) bool { + return rcv._tab.MutateInt64Slot(6, n) +} + func (rcv *Stat) Count() uint16 { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { @@ -45,6 +49,10 @@ func (rcv *Stat) Count() uint16 { return 0 } +func (rcv *Stat) MutateCount(n uint16) bool { + return rcv._tab.MutateUint16Slot(8, n) +} + func StatStart(builder *flatbuffers.Builder) { builder.StartObject(3) } func StatAddId(builder *flatbuffers.Builder, id flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(id), 0) } func StatAddVal(builder *flatbuffers.Builder, val int64) { builder.PrependInt64Slot(1, val, 0) } diff --git a/tests/MyGame/Example/Stat.java b/tests/MyGame/Example/Stat.java index 3cac509b..cd339c62 100644 --- a/tests/MyGame/Example/Stat.java +++ b/tests/MyGame/Example/Stat.java @@ -39,5 +39,5 @@ public final class Stat extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/MyGame/Example/Test.go b/tests/MyGame/Example/Test.go index e849488d..ee0d9aae 100644 --- a/tests/MyGame/Example/Test.go +++ b/tests/MyGame/Example/Test.go @@ -15,7 +15,11 @@ func (rcv *Test) Init(buf []byte, i flatbuffers.UOffsetT) { } func (rcv *Test) A() int16 { return rcv._tab.GetInt16(rcv._tab.Pos + flatbuffers.UOffsetT(0)) } +func (rcv *Test) MutateA(n int16) bool { return rcv._tab.MutateInt16(rcv._tab.Pos + flatbuffers.UOffsetT(0), n) } + func (rcv *Test) B() int8 { return rcv._tab.GetInt8(rcv._tab.Pos + flatbuffers.UOffsetT(2)) } +func (rcv *Test) MutateB(n int8) bool { return rcv._tab.MutateInt8(rcv._tab.Pos + flatbuffers.UOffsetT(2), n) } + func CreateTest(builder *flatbuffers.Builder, a int16, b int8) flatbuffers.UOffsetT { builder.Prep(2, 4) diff --git a/tests/MyGame/Example/Test.java b/tests/MyGame/Example/Test.java index 52474152..6e33da9b 100644 --- a/tests/MyGame/Example/Test.java +++ b/tests/MyGame/Example/Test.java @@ -23,5 +23,5 @@ public final class Test extends Struct { builder.putShort(a); return builder.offset(); } -}; +} diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.go b/tests/MyGame/Example/TestSimpleTableWithEnum.go index 965c7189..96218e58 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.go +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.go @@ -29,6 +29,10 @@ func (rcv *TestSimpleTableWithEnum) Color() int8 { return 2 } +func (rcv *TestSimpleTableWithEnum) MutateColor(n int8) bool { + return rcv._tab.MutateInt8Slot(4, n) +} + func TestSimpleTableWithEnumStart(builder *flatbuffers.Builder) { builder.StartObject(1) } func TestSimpleTableWithEnumAddColor(builder *flatbuffers.Builder, color int8) { builder.PrependInt8Slot(0, color, 2) } func TestSimpleTableWithEnumEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.java b/tests/MyGame/Example/TestSimpleTableWithEnum.java index 85e18c2b..a1de020e 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.java +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.java @@ -29,5 +29,5 @@ public final class TestSimpleTableWithEnum extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/MyGame/Example/Vec3.go b/tests/MyGame/Example/Vec3.go index 082945ed..8bf97da3 100644 --- a/tests/MyGame/Example/Vec3.go +++ b/tests/MyGame/Example/Vec3.go @@ -15,10 +15,20 @@ func (rcv *Vec3) Init(buf []byte, i flatbuffers.UOffsetT) { } func (rcv *Vec3) X() float32 { return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(0)) } +func (rcv *Vec3) MutateX(n float32) bool { return rcv._tab.MutateFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(0), n) } + func (rcv *Vec3) Y() float32 { return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(4)) } +func (rcv *Vec3) MutateY(n float32) bool { return rcv._tab.MutateFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(4), n) } + func (rcv *Vec3) Z() float32 { return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(8)) } +func (rcv *Vec3) MutateZ(n float32) bool { return rcv._tab.MutateFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(8), n) } + func (rcv *Vec3) Test1() float64 { return rcv._tab.GetFloat64(rcv._tab.Pos + flatbuffers.UOffsetT(16)) } +func (rcv *Vec3) MutateTest1(n float64) bool { return rcv._tab.MutateFloat64(rcv._tab.Pos + flatbuffers.UOffsetT(16), n) } + func (rcv *Vec3) Test2() int8 { return rcv._tab.GetInt8(rcv._tab.Pos + flatbuffers.UOffsetT(24)) } +func (rcv *Vec3) MutateTest2(n int8) bool { return rcv._tab.MutateInt8(rcv._tab.Pos + flatbuffers.UOffsetT(24), n) } + func (rcv *Vec3) Test3(obj *Test) *Test { if obj == nil { obj = new(Test) diff --git a/tests/MyGame/Example/Vec3.java b/tests/MyGame/Example/Vec3.java index 7ae1ce20..261947cc 100644 --- a/tests/MyGame/Example/Vec3.java +++ b/tests/MyGame/Example/Vec3.java @@ -40,5 +40,5 @@ public final class Vec3 extends Struct { builder.putFloat(x); return builder.offset(); } -}; +} diff --git a/tests/MyGame/Example2/Monster.cs b/tests/MyGame/Example2/Monster.cs new file mode 100644 index 00000000..e6c512ab --- /dev/null +++ b/tests/MyGame/Example2/Monster.cs @@ -0,0 +1,23 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +namespace MyGame.Example2 +{ + +using System; +using FlatBuffers; + +public sealed class Monster : Table { + public static Monster GetRootAsMonster(ByteBuffer _bb) { return GetRootAsMonster(_bb, new Monster()); } + public static Monster GetRootAsMonster(ByteBuffer _bb, Monster obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); } + public Monster __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + + public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(0); } + public static Offset<MyGame.Example2.Monster> EndMonster(FlatBufferBuilder builder) { + int o = builder.EndObject(); + return new Offset<MyGame.Example2.Monster>(o); + } +}; + + +} diff --git a/tests/MyGame/Example2/Monster.go b/tests/MyGame/Example2/Monster.go new file mode 100644 index 00000000..e12b0545 --- /dev/null +++ b/tests/MyGame/Example2/Monster.go @@ -0,0 +1,18 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package Example2 + +import ( + flatbuffers "github.com/google/flatbuffers/go" +) +type Monster struct { + _tab flatbuffers.Table +} + +func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) { + rcv._tab.Bytes = buf + rcv._tab.Pos = i +} + +func MonsterStart(builder *flatbuffers.Builder) { builder.StartObject(0) } +func MonsterEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/MyGame/Example2/Monster.java b/tests/MyGame/Example2/Monster.java new file mode 100644 index 00000000..968eee56 --- /dev/null +++ b/tests/MyGame/Example2/Monster.java @@ -0,0 +1,23 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package MyGame.Example2; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class Monster extends Table { + public static Monster getRootAsMonster(ByteBuffer _bb) { return getRootAsMonster(_bb, new Monster()); } + public static Monster getRootAsMonster(ByteBuffer _bb, Monster obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__init(_bb.getInt(_bb.position()) + _bb.position(), _bb)); } + public Monster __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + + public static void startMonster(FlatBufferBuilder builder) { builder.startObject(0); } + public static int endMonster(FlatBufferBuilder builder) { + int o = builder.endObject(); + return o; + } +} + diff --git a/tests/MyGame/Example2/Monster.php b/tests/MyGame/Example2/Monster.php new file mode 100644 index 00000000..b00f150e --- /dev/null +++ b/tests/MyGame/Example2/Monster.php @@ -0,0 +1,79 @@ +<?php +// automatically generated by the FlatBuffers compiler, do not modify + +namespace MyGame\Example2; + +use \Google\FlatBuffers\Struct; +use \Google\FlatBuffers\Table; +use \Google\FlatBuffers\ByteBuffer; +use \Google\FlatBuffers\FlatBufferBuilder; + +class Monster extends Table +{ + /** + * @param ByteBuffer $bb + * @return Monster + */ + public static function getRootAsMonster(ByteBuffer $bb) + { + $obj = new Monster(); + return ($obj->init($bb->getInt($bb->getPosition()) + $bb->getPosition(), $bb)); + } + + public static function MonsterIdentifier() + { + return "MONS"; + } + + public static function MonsterBufferHasIdentifier(ByteBuffer $buf) + { + return self::__has_identifier($buf, self::MonsterIdentifier()); + } + + public static function MonsterExtension() + { + return "mon"; + } + + /** + * @param int $_i offset + * @param ByteBuffer $_bb + * @return Monster + **/ + public function init($_i, ByteBuffer $_bb) + { + $this->bb_pos = $_i; + $this->bb = $_bb; + return $this; + } + + /** + * @param FlatBufferBuilder $builder + * @return void + */ + public static function startMonster(FlatBufferBuilder $builder) + { + $builder->StartObject(0); + } + + /** + * @param FlatBufferBuilder $builder + * @return Monster + */ + public static function createMonster(FlatBufferBuilder $builder, ) + { + $builder->startObject(0); + $o = $builder->endObject(); + return $o; + } + + /** + * @param FlatBufferBuilder $builder + * @return int table offset + */ + public static function endMonster(FlatBufferBuilder $builder) + { + $o = $builder->endObject(); + return $o; + } +} diff --git a/tests/MyGame/Example2/Monster.py b/tests/MyGame/Example2/Monster.py new file mode 100644 index 00000000..0b98211e --- /dev/null +++ b/tests/MyGame/Example2/Monster.py @@ -0,0 +1,15 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: Example2 + +import flatbuffers + +class Monster(object): + __slots__ = ['_tab'] + + # Monster + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + +def MonsterStart(builder): builder.StartObject(0) +def MonsterEnd(builder): return builder.EndObject() diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 00b16d90..4b4a5353 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -12,6 +12,6 @@ :: See the License for the specific language governing permissions and :: limitations under the License. -..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test\namespace_test1.fbs namespace_test\namespace_test2.fbs ..\flatc.exe --binary --schema monster_test.fbs diff --git a/tests/generate_code.sh b/tests/generate_code.sh index b74c2bec..1b347ba7 100644 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --binary --schema monster_test.fbs diff --git a/tests/go_test.go b/tests/go_test.go index 4d0067f4..e4e96de8 100644 --- a/tests/go_test.go +++ b/tests/go_test.go @@ -67,6 +67,7 @@ func TestAll(t *testing.T) { // Verify that the Go FlatBuffers runtime library generates the // expected bytes (does not use any schema): CheckByteLayout(t.Fatalf) + CheckMutateMethods(t.Fatalf) // Verify that panics are raised during exceptional conditions: CheckNotInObjectError(t.Fatalf) @@ -85,6 +86,7 @@ func TestAll(t *testing.T) { // Verify that the buffer generated by Go code is readable by the // generated Go code: CheckReadBuffer(generated, off, t.Fatalf) + CheckMutateBuffer(generated, off, t.Fatalf) // Verify that the buffer generated by C++ code is readable by the // generated Go code: @@ -93,6 +95,7 @@ func TestAll(t *testing.T) { t.Fatal(err) } CheckReadBuffer(monsterDataCpp, 0, t.Fatalf) + CheckMutateBuffer(monsterDataCpp, 0, t.Fatalf) // Verify that vtables are deduplicated when written: CheckVtableDeduplication(t.Fatalf) @@ -283,6 +286,120 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, } } +// CheckMutateBuffer checks that the given buffer can be mutated correctly +// as the example Monster. Only available scalar values are mutated. +func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string, ...interface{})) { + // make a copy to mutate + buf := make([]byte, len(org)) + copy(buf, org) + + // load monster data from the buffer + monster := example.GetRootAsMonster(buf, offset) + + // test case struct + type testcase struct { + field string + testfn func() bool + } + + testForOriginalValues := []testcase{ + testcase{"Hp", func() bool { return monster.Hp() == 80 }}, + testcase{"Mana", func() bool { return monster.Mana() == 150 }}, + testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(1.0) }}, + testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }}, + testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }}, + testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }}, + testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == int8(2) }}, + testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }}, + testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }}, + } + + testMutability := []testcase{ + testcase{"Hp", func() bool { return monster.MutateHp(70) }}, + testcase{"Mana", func() bool { return !monster.MutateMana(140) }}, + testcase{"Pos.X", func() bool { return monster.Pos(nil).MutateX(10.0) }}, + testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }}, + testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }}, + testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }}, + testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(20) }}, + testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }}, + testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }}, + } + + testForMutatedValues := []testcase{ + testcase{"Hp", func() bool { return monster.Hp() == 70 }}, + testcase{"Mana", func() bool { return monster.Mana() == 150 }}, + testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(10.0) }}, + testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }}, + testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }}, + testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }}, + testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == int8(20) }}, + testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }}, + testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }}, + } + + // make sure original values are okay + for _, t := range testForOriginalValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected original value") + } + } + + // try to mutate fields and check mutability + for _, t := range testMutability { + if !t.testfn() { + fail(FailString("field '"+t.field+"' failed mutability test", true, false)) + } + } + + // test whether values have changed + for _, t := range testForMutatedValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected mutated value") + } + } + + // make sure the buffer has changed + if reflect.DeepEqual(buf, org) { + fail("mutate buffer failed") + } + + // To make sure the buffer has changed accordingly + // Read data from the buffer and verify all fields + monster = example.GetRootAsMonster(buf, offset) + for _, t := range testForMutatedValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected mutated value") + } + } + + // reverting all fields to original values should + // re-create the original buffer. Mutate all fields + // back to their original values and compare buffers. + // This test is done to make sure mutations do not do + // any unnecessary changes to the buffer. + monster = example.GetRootAsMonster(buf, offset) + monster.MutateHp(80) + monster.Pos(nil).MutateX(1.0) + monster.Pos(nil).MutateY(2.0) + monster.Pos(nil).MutateZ(3.0) + monster.Pos(nil).MutateTest1(3.0) + monster.Pos(nil).MutateTest2(2) + monster.Pos(nil).Test3(nil).MutateA(5) + monster.Pos(nil).Test3(nil).MutateB(6) + + for _, t := range testForOriginalValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected original value") + } + } + + // buffer should have original values + if !reflect.DeepEqual(buf, org) { + fail("revert changes failed") + } +} + // Low level stress/fuzz test: serialize/deserialize a variety of // different kinds of data in different combinations func checkFuzz(fuzzFields, fuzzObjects int, fail func(string, ...interface{})) { @@ -1276,6 +1393,151 @@ func CheckByteEquality(a, b []byte, fail func(string, ...interface{})) { } } +// CheckMutateMethods checks all mutate methods one by one +func CheckMutateMethods(fail func(string, ...interface{})) { + b := flatbuffers.NewBuilder(0) + b.StartObject(15) + b.PrependBoolSlot(0, true, false) + b.PrependByteSlot(1, 1, 0) + b.PrependUint8Slot(2, 2, 0) + b.PrependUint16Slot(3, 3, 0) + b.PrependUint32Slot(4, 4, 0) + b.PrependUint64Slot(5, 5, 0) + b.PrependInt8Slot(6, 6, 0) + b.PrependInt16Slot(7, 7, 0) + b.PrependInt32Slot(8, 8, 0) + b.PrependInt64Slot(9, 9, 0) + b.PrependFloat32Slot(10, 10, 0) + b.PrependFloat64Slot(11, 11, 0) + + b.PrependUOffsetTSlot(12, 12, 0) + uoVal := b.Offset() - 12 + + b.PrependVOffsetT(13) + b.Slot(13) + + b.PrependSOffsetT(14) + b.Slot(14) + soVal := flatbuffers.SOffsetT(b.Offset() - 14) + + offset := b.EndObject() + + t := &flatbuffers.Table{ + Bytes: b.Bytes, + Pos: flatbuffers.UOffsetT(len(b.Bytes)) - offset, + } + + calcVOffsetT := func(slot int) (vtableOffset flatbuffers.VOffsetT) { + return flatbuffers.VOffsetT((flatbuffers.VtableMetadataFields + slot) * flatbuffers.SizeVOffsetT) + } + calcUOffsetT := func(vtableOffset flatbuffers.VOffsetT) (valueOffset flatbuffers.UOffsetT) { + return t.Pos + flatbuffers.UOffsetT(t.Offset(vtableOffset)) + } + + type testcase struct { + field string + testfn func() bool + } + + testForOriginalValues := []testcase{ + testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == true }}, + testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 1 }}, + testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 2) == 2 }}, + testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 3) == 3 }}, + testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 4) == 4 }}, + testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 5) == 5 }}, + testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 6) == 6 }}, + testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 7) == 7 }}, + testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 8) == 8 }}, + testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 9) == 9 }}, + testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 10) == 10 }}, + testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 11) == 11 }}, + testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == uoVal }}, + testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 13 }}, + testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == soVal }}, + } + + testMutability := []testcase{ + testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(0), false) }}, + testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(1), 2) }}, + testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(2), 4) }}, + testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(3), 6) }}, + testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(4), 8) }}, + testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(5), 10) }}, + testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(6), 12) }}, + testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(7), 14) }}, + testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(8), 16) }}, + testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(9), 18) }}, + testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(10), 20) }}, + testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(11), 22) }}, + testcase{"UOffsetTSlot", func() bool { return t.MutateUOffsetT(calcUOffsetT(calcVOffsetT(12)), 24) }}, + testcase{"VOffsetTSlot", func() bool { return t.MutateVOffsetT(calcUOffsetT(calcVOffsetT(13)), 26) }}, + testcase{"SOffsetTSlot", func() bool { return t.MutateSOffsetT(calcUOffsetT(calcVOffsetT(14)), 28) }}, + } + + testMutabilityWithoutSlot := []testcase{ + testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(16), false) }}, + testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(16), 2) }}, + testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(16), 2) }}, + testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(16), 2) }}, + testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(16), 2) }}, + testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(16), 2) }}, + testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(16), 2) }}, + testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(16), 2) }}, + testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(16), 2) }}, + testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(16), 2) }}, + testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(16), 2) }}, + testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(16), 2) }}, + } + + testForMutatedValues := []testcase{ + testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == false }}, + testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 2 }}, + testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 1) == 4 }}, + testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 1) == 6 }}, + testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 1) == 8 }}, + testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 1) == 10 }}, + testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 1) == 12 }}, + testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 1) == 14 }}, + testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 1) == 16 }}, + testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 1) == 18 }}, + testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 1) == 20 }}, + testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 1) == 22 }}, + testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == 24 }}, + testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 26 }}, + testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == 28 }}, + } + + // make sure original values are okay + for _, t := range testForOriginalValues { + if !t.testfn() { + fail(t.field + "' field doesn't have the expected original value") + } + } + + // try to mutate fields and check mutability + for _, t := range testMutability { + if !t.testfn() { + fail(FailString(t.field+"' field failed mutability test", "passed", "failed")) + } + } + + // try to mutate fields and check mutability + // these have wrong slots so should fail + for _, t := range testMutabilityWithoutSlot { + if t.testfn() { + fail(FailString(t.field+"' field failed no slot mutability test", "failed", "passed")) + } + } + + // test whether values have changed + for _, t := range testForMutatedValues { + if !t.testfn() { + fail(t.field + "' field doesn't have the expected mutated value") + } + } +} + // BenchmarkVtableDeduplication measures the speed of vtable deduplication // by creating prePop vtables, then populating b.N objects with a // different single vtable. diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs Binary files differindex e16b2bd1..e131ac8c 100644 --- a/tests/monster_test.bfbs +++ b/tests/monster_test.bfbs diff --git a/tests/monster_test.fbs b/tests/monster_test.fbs index f302ce44..3fecd33c 100755 --- a/tests/monster_test.fbs +++ b/tests/monster_test.fbs @@ -2,13 +2,17 @@ include "include_test1.fbs"; +namespace MyGame.Example2; + +table Monster {} // Test having same name as below, but in different namespace. + namespace MyGame.Example; attribute "priority"; enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3, } -union Any { Monster, TestSimpleTableWithEnum } // TODO: add more elements +union Any { Monster, TestSimpleTableWithEnum, MyGame.Example2.Monster } struct Test { a:short; b:byte; } @@ -44,6 +48,7 @@ table Monster { /// multiline too testarrayoftables:[Monster] (id: 11); testarrayofstring:[string] (id: 10); + testarrayofstring2:[string] (id: 28); testarrayofbools:[bool] (id: 24); enemy:MyGame.Example.Monster (id:12); // Test referring by full namespace. test:Any (id: 8); @@ -65,7 +70,7 @@ table Monster { } rpc_service MonsterStorage { - Store(Monster):Stat (stream); + Store(Monster):Stat (streaming: "none"); Retrieve(Stat):Monster (idempotent); } diff --git a/tests/monster_test.grpc.fb.cc b/tests/monster_test.grpc.fb.cc new file mode 100644 index 00000000..71dbd2d4 --- /dev/null +++ b/tests/monster_test.grpc.fb.cc @@ -0,0 +1,85 @@ +// Generated by the gRPC protobuf plugin. +// If you make any local change, they will be lost. +// source: monster_test + +#include "monster_test_generated.h" +#include "monster_test.grpc.fb.h" +#include "flatbuffers/grpc.h" + +#include <grpc++/impl/codegen/async_stream.h> +#include <grpc++/impl/codegen/async_unary_call.h> +#include <grpc++/impl/codegen/channel_interface.h> +#include <grpc++/impl/codegen/client_unary_call.h> +#include <grpc++/impl/codegen/method_handler_impl.h> +#include <grpc++/impl/codegen/rpc_service_method.h> +#include <grpc++/impl/codegen/service_type.h> +#include <grpc++/impl/codegen/sync_stream.h> +namespace MyGame { +namespace Example { + +static const char* MonsterStorage_method_names[] = { + "/MyGame.Example..MonsterStorage/Store", + "/MyGame.Example..MonsterStorage/Retrieve", +}; + +std::unique_ptr< MonsterStorage::Stub> MonsterStorage::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { + std::unique_ptr< MonsterStorage::Stub> stub(new MonsterStorage::Stub(channel)); + return stub; +} + +MonsterStorage::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel) + : channel_(channel) , rpcmethod_Store_(MonsterStorage_method_names[0], ::grpc::RpcMethod::NORMAL_RPC, channel) + , rpcmethod_Retrieve_(MonsterStorage_method_names[1], ::grpc::RpcMethod::NORMAL_RPC, channel) + {} + +::grpc::Status MonsterStorage::Stub::Store(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, flatbuffers::BufferRef<Stat>* response) { + return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_Store_, context, request, response); +} + +::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Stat>>* MonsterStorage::Stub::AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, ::grpc::CompletionQueue* cq) { + return new ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Stat>>(channel_.get(), cq, rpcmethod_Store_, context, request); +} + +::grpc::Status MonsterStorage::Stub::Retrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, flatbuffers::BufferRef<Monster>* response) { + return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_Retrieve_, context, request, response); +} + +::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Monster>>* MonsterStorage::Stub::AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, ::grpc::CompletionQueue* cq) { + return new ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Monster>>(channel_.get(), cq, rpcmethod_Retrieve_, context, request); +} + +MonsterStorage::Service::Service() { + (void)MonsterStorage_method_names; + AddMethod(new ::grpc::RpcServiceMethod( + MonsterStorage_method_names[0], + ::grpc::RpcMethod::NORMAL_RPC, + new ::grpc::RpcMethodHandler< MonsterStorage::Service, flatbuffers::BufferRef<Monster>, flatbuffers::BufferRef<Stat>>( + std::mem_fn(&MonsterStorage::Service::Store), this))); + AddMethod(new ::grpc::RpcServiceMethod( + MonsterStorage_method_names[1], + ::grpc::RpcMethod::NORMAL_RPC, + new ::grpc::RpcMethodHandler< MonsterStorage::Service, flatbuffers::BufferRef<Stat>, flatbuffers::BufferRef<Monster>>( + std::mem_fn(&MonsterStorage::Service::Retrieve), this))); +} + +MonsterStorage::Service::~Service() { +} + +::grpc::Status MonsterStorage::Service::Store(::grpc::ServerContext* context, const flatbuffers::BufferRef<Monster>* request, flatbuffers::BufferRef<Stat>* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + +::grpc::Status MonsterStorage::Service::Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef<Stat>* request, flatbuffers::BufferRef<Monster>* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + + +} // namespace MyGame +} // namespace Example + diff --git a/tests/monster_test.grpc.fb.h b/tests/monster_test.grpc.fb.h new file mode 100644 index 00000000..4269234f --- /dev/null +++ b/tests/monster_test.grpc.fb.h @@ -0,0 +1,155 @@ +// Generated by the gRPC protobuf plugin. +// If you make any local change, they will be lost. +// source: monster_test +#ifndef GRPC_monster_5ftest__INCLUDED +#define GRPC_monster_5ftest__INCLUDED + +#include "monster_test_generated.h" + +#include <grpc++/impl/codegen/async_stream.h> +#include <grpc++/impl/codegen/async_unary_call.h> +#include <grpc++/impl/codegen/proto_utils.h> +#include <grpc++/impl/codegen/rpc_method.h> +#include <grpc++/impl/codegen/service_type.h> +#include <grpc++/impl/codegen/status.h> +#include <grpc++/impl/codegen/stub_options.h> +#include <grpc++/impl/codegen/sync_stream.h> + +namespace grpc { +class CompletionQueue; +class Channel; +class RpcService; +class ServerCompletionQueue; +class ServerContext; +} // namespace grpc + +namespace MyGame { +namespace Example { + +class MonsterStorage GRPC_FINAL { + public: + class StubInterface { + public: + virtual ~StubInterface() {} + virtual ::grpc::Status Store(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, flatbuffers::BufferRef<Stat>* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef<Stat>>> AsyncStore(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef<Stat>>>(AsyncStoreRaw(context, request, cq)); + } + virtual ::grpc::Status Retrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, flatbuffers::BufferRef<Monster>* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef<Monster>>> AsyncRetrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef<Monster>>>(AsyncRetrieveRaw(context, request, cq)); + } + private: + virtual ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef<Stat>>* AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, ::grpc::CompletionQueue* cq) = 0; + virtual ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef<Monster>>* AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, ::grpc::CompletionQueue* cq) = 0; + }; + class Stub GRPC_FINAL : public StubInterface { + public: + Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel); + ::grpc::Status Store(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, flatbuffers::BufferRef<Stat>* response) GRPC_OVERRIDE; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Stat>>> AsyncStore(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Stat>>>(AsyncStoreRaw(context, request, cq)); + } + ::grpc::Status Retrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, flatbuffers::BufferRef<Monster>* response) GRPC_OVERRIDE; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Monster>>> AsyncRetrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Monster>>>(AsyncRetrieveRaw(context, request, cq)); + } + + private: + std::shared_ptr< ::grpc::ChannelInterface> channel_; + ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Stat>>* AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef<Monster>& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE; + ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef<Monster>>* AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef<Stat>& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE; + const ::grpc::RpcMethod rpcmethod_Store_; + const ::grpc::RpcMethod rpcmethod_Retrieve_; + }; + static std::unique_ptr<Stub> NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); + + class Service : public ::grpc::Service { + public: + Service(); + virtual ~Service(); + virtual ::grpc::Status Store(::grpc::ServerContext* context, const flatbuffers::BufferRef<Monster>* request, flatbuffers::BufferRef<Stat>* response); + virtual ::grpc::Status Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef<Stat>* request, flatbuffers::BufferRef<Monster>* response); + }; + template <class BaseClass> + class WithAsyncMethod_Store : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithAsyncMethod_Store() { + ::grpc::Service::MarkMethodAsync(0); + } + ~WithAsyncMethod_Store() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Store(::grpc::ServerContext* context, const flatbuffers::BufferRef<Monster>* request, flatbuffers::BufferRef<Stat>* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestStore(::grpc::ServerContext* context, flatbuffers::BufferRef<Monster>* request, ::grpc::ServerAsyncResponseWriter< flatbuffers::BufferRef<Stat>>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); + } + }; + template <class BaseClass> + class WithAsyncMethod_Retrieve : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithAsyncMethod_Retrieve() { + ::grpc::Service::MarkMethodAsync(1); + } + ~WithAsyncMethod_Retrieve() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef<Stat>* request, flatbuffers::BufferRef<Monster>* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestRetrieve(::grpc::ServerContext* context, flatbuffers::BufferRef<Stat>* request, ::grpc::ServerAsyncResponseWriter< flatbuffers::BufferRef<Monster>>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(1, context, request, response, new_call_cq, notification_cq, tag); + } + }; + typedef WithAsyncMethod_Store< WithAsyncMethod_Retrieve< Service > > AsyncService; + template <class BaseClass> + class WithGenericMethod_Store : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithGenericMethod_Store() { + ::grpc::Service::MarkMethodGeneric(0); + } + ~WithGenericMethod_Store() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Store(::grpc::ServerContext* context, const flatbuffers::BufferRef<Monster>* request, flatbuffers::BufferRef<Stat>* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; + template <class BaseClass> + class WithGenericMethod_Retrieve : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithGenericMethod_Retrieve() { + ::grpc::Service::MarkMethodGeneric(1); + } + ~WithGenericMethod_Retrieve() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef<Stat>* request, flatbuffers::BufferRef<Monster>* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; +}; + +} // namespace Example +} // namespace MyGame + + +#endif // GRPC_monster_5ftest__INCLUDED diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 3da41fae..49aba113 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -6,17 +6,27 @@ #include "flatbuffers/flatbuffers.h" namespace MyGame { +namespace Example2 { + +struct Monster; +struct MonsterT; + +} // namespace Example2 + namespace Example { struct Test; struct TestSimpleTableWithEnum; +struct TestSimpleTableWithEnumT; struct Vec3; struct Stat; +struct StatT; struct Monster; +struct MonsterT; enum Color { Color_Red = 1, @@ -37,12 +47,30 @@ enum Any { Any_NONE = 0, Any_Monster = 1, Any_TestSimpleTableWithEnum = 2, + Any_MyGame_Example2_Monster = 3, Any_MIN = Any_NONE, - Any_MAX = Any_TestSimpleTableWithEnum + Any_MAX = Any_MyGame_Example2_Monster +}; + +struct AnyUnion { + Any type; + + flatbuffers::NativeTable *table; + AnyUnion() : type(Any_NONE), table(nullptr) {} + AnyUnion(const AnyUnion &); + AnyUnion &operator=(const AnyUnion &); + ~AnyUnion(); + + static flatbuffers::NativeTable *UnPack(const void *union_obj, Any type); + flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb) const; + + MonsterT *AsMonster() { return type == Any_Monster ? reinterpret_cast<MonsterT *>(table) : nullptr; } + TestSimpleTableWithEnumT *AsTestSimpleTableWithEnum() { return type == Any_TestSimpleTableWithEnum ? reinterpret_cast<TestSimpleTableWithEnumT *>(table) : nullptr; } + MyGame::Example2::MonsterT *AsMyGame_Example2_Monster() { return type == Any_MyGame_Example2_Monster ? reinterpret_cast<MyGame::Example2::MonsterT *>(table) : nullptr; } }; inline const char **EnumNamesAny() { - static const char *names[] = { "NONE", "Monster", "TestSimpleTableWithEnum", nullptr }; + static const char *names[] = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", nullptr }; return names; } @@ -57,6 +85,8 @@ MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { int8_t __padding0; public: + Test() { memset(this, 0, sizeof(Test)); } + Test(const Test &_o) { memcpy(this, &_o, sizeof(Test)); } Test(int16_t _a, int8_t _b) : a_(flatbuffers::EndianScalar(_a)), b_(flatbuffers::EndianScalar(_b)), __padding0(0) { (void)__padding0; } @@ -80,6 +110,8 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 FLATBUFFERS_FINAL_CLASS { int16_t __padding2; public: + Vec3() { memset(this, 0, sizeof(Vec3)); } + Vec3(const Vec3 &_o) { memcpy(this, &_o, sizeof(Vec3)); } Vec3(float _x, float _y, float _z, double _test1, Color _test2, const Test &_test3) : x_(flatbuffers::EndianScalar(_x)), y_(flatbuffers::EndianScalar(_y)), z_(flatbuffers::EndianScalar(_z)), __padding0(0), test1_(flatbuffers::EndianScalar(_test1)), test2_(flatbuffers::EndianScalar(static_cast<int8_t>(_test2))), __padding1(0), test3_(_test3), __padding2(0) { (void)__padding0; (void)__padding1; (void)__padding2; } @@ -98,6 +130,47 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 FLATBUFFERS_FINAL_CLASS { }; STRUCT_END(Vec3, 32); +} // namespace Example + +namespace Example2 { + +struct MonsterT : public flatbuffers::NativeTable { +}; + +struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } + std::unique_ptr<MonsterT> UnPack() const; +}; + +struct MonsterBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } + MonsterBuilder &operator=(const MonsterBuilder &); + flatbuffers::Offset<Monster> Finish() { + auto o = flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 0)); + return o; + } +}; + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb) { + MonsterBuilder builder_(_fbb); + return builder_.Finish(); +} + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o); + +} // namespace Example2 + +namespace Example { + +struct TestSimpleTableWithEnumT : public flatbuffers::NativeTable { + Color color; +}; + struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_COLOR = 4 @@ -109,6 +182,7 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta VerifyField<int8_t>(verifier, VT_COLOR) && verifier.EndTable(); } + std::unique_ptr<TestSimpleTableWithEnumT> UnPack() const; }; struct TestSimpleTableWithEnumBuilder { @@ -124,12 +198,20 @@ struct TestSimpleTableWithEnumBuilder { }; inline flatbuffers::Offset<TestSimpleTableWithEnum> CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, - Color color = Color_Green) { + Color color = Color_Green) { TestSimpleTableWithEnumBuilder builder_(_fbb); builder_.add_color(color); return builder_.Finish(); } +inline flatbuffers::Offset<TestSimpleTableWithEnum> CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o); + +struct StatT : public flatbuffers::NativeTable { + std::string id; + int64_t val; + uint16_t count; +}; + struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_ID = 4, @@ -150,6 +232,7 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyField<uint16_t>(verifier, VT_COUNT) && verifier.EndTable(); } + std::unique_ptr<StatT> UnPack() const; }; struct StatBuilder { @@ -167,9 +250,9 @@ struct StatBuilder { }; inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset<flatbuffers::String> id = 0, - int64_t val = 0, - uint16_t count = 0) { + flatbuffers::Offset<flatbuffers::String> id = 0, + int64_t val = 0, + uint16_t count = 0) { StatBuilder builder_(_fbb); builder_.add_val(val); builder_.add_id(id); @@ -177,6 +260,45 @@ inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb return builder_.Finish(); } +inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb, + const char *id = nullptr, + int64_t val = 0, + uint16_t count = 0) { + return CreateStat(_fbb, id ? 0 : _fbb.CreateString(id), val, count); +} + +inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb, const StatT *_o); + +struct MonsterT : public flatbuffers::NativeTable { + std::unique_ptr<Vec3> pos; + int16_t mana; + int16_t hp; + std::string name; + std::vector<uint8_t> inventory; + Color color; + AnyUnion test; + std::vector<Test> test4; + std::vector<std::string> testarrayofstring; + std::vector<std::unique_ptr<MonsterT>> testarrayoftables; + std::unique_ptr<MonsterT> enemy; + std::vector<uint8_t> testnestedflatbuffer; + std::unique_ptr<StatT> testempty; + bool testbool; + int32_t testhashs32_fnv1; + uint32_t testhashu32_fnv1; + int64_t testhashs64_fnv1; + uint64_t testhashu64_fnv1; + int32_t testhashs32_fnv1a; + uint32_t testhashu32_fnv1a; + int64_t testhashs64_fnv1a; + uint64_t testhashu64_fnv1a; + std::vector<bool> testarrayofbools; + float testf; + float testf2; + float testf3; + std::vector<std::string> testarrayofstring2; +}; + /// an example documentation comment: monster object struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { @@ -206,7 +328,8 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_TESTARRAYOFBOOLS = 52, VT_TESTF = 54, VT_TESTF2 = 56, - VT_TESTF3 = 58 + VT_TESTF3 = 58, + VT_TESTARRAYOFSTRING2 = 60 }; const Vec3 *pos() const { return GetStruct<const Vec3 *>(VT_POS); } Vec3 *mutable_pos() { return GetStruct<Vec3 *>(VT_POS); } @@ -267,6 +390,8 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_testf2(float _testf2) { return SetField(VT_TESTF2, _testf2); } float testf3() const { return GetField<float>(VT_TESTF3, 0.0f); } bool mutate_testf3(float _testf3) { return SetField(VT_TESTF3, _testf3); } + const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *testarrayofstring2() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_TESTARRAYOFSTRING2); } + flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *mutable_testarrayofstring2() { return GetPointer<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_TESTARRAYOFSTRING2); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField<Vec3>(verifier, VT_POS) && @@ -308,8 +433,12 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyField<float>(verifier, VT_TESTF) && VerifyField<float>(verifier, VT_TESTF2) && VerifyField<float>(verifier, VT_TESTF3) && + VerifyField<flatbuffers::uoffset_t>(verifier, VT_TESTARRAYOFSTRING2) && + verifier.Verify(testarrayofstring2()) && + verifier.VerifyVectorOfStrings(testarrayofstring2()) && verifier.EndTable(); } + std::unique_ptr<MonsterT> UnPack() const; }; struct MonsterBuilder { @@ -342,48 +471,51 @@ struct MonsterBuilder { void add_testf(float testf) { fbb_.AddElement<float>(Monster::VT_TESTF, testf, 3.14159f); } void add_testf2(float testf2) { fbb_.AddElement<float>(Monster::VT_TESTF2, testf2, 3.0f); } void add_testf3(float testf3) { fbb_.AddElement<float>(Monster::VT_TESTF3, testf3, 0.0f); } + void add_testarrayofstring2(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring2) { fbb_.AddOffset(Monster::VT_TESTARRAYOFSTRING2, testarrayofstring2); } MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } MonsterBuilder &operator=(const MonsterBuilder &); flatbuffers::Offset<Monster> Finish() { - auto o = flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 28)); + auto o = flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 29)); fbb_.Required(o, Monster::VT_NAME); // name return o; } }; inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, - const Vec3 *pos = 0, - int16_t mana = 150, - int16_t hp = 100, - flatbuffers::Offset<flatbuffers::String> name = 0, - flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory = 0, - Color color = Color_Blue, - Any test_type = Any_NONE, - flatbuffers::Offset<void> test = 0, - flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4 = 0, - flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring = 0, - flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables = 0, - flatbuffers::Offset<Monster> enemy = 0, - flatbuffers::Offset<flatbuffers::Vector<uint8_t>> testnestedflatbuffer = 0, - flatbuffers::Offset<Stat> testempty = 0, - bool testbool = false, - int32_t testhashs32_fnv1 = 0, - uint32_t testhashu32_fnv1 = 0, - int64_t testhashs64_fnv1 = 0, - uint64_t testhashu64_fnv1 = 0, - int32_t testhashs32_fnv1a = 0, - uint32_t testhashu32_fnv1a = 0, - int64_t testhashs64_fnv1a = 0, - uint64_t testhashu64_fnv1a = 0, - flatbuffers::Offset<flatbuffers::Vector<uint8_t>> testarrayofbools = 0, - float testf = 3.14159f, - float testf2 = 3.0f, - float testf3 = 0.0f) { + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + flatbuffers::Offset<flatbuffers::String> name = 0, + flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory = 0, + Color color = Color_Blue, + Any test_type = Any_NONE, + flatbuffers::Offset<void> test = 0, + flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4 = 0, + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring = 0, + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables = 0, + flatbuffers::Offset<Monster> enemy = 0, + flatbuffers::Offset<flatbuffers::Vector<uint8_t>> testnestedflatbuffer = 0, + flatbuffers::Offset<Stat> testempty = 0, + bool testbool = false, + int32_t testhashs32_fnv1 = 0, + uint32_t testhashu32_fnv1 = 0, + int64_t testhashs64_fnv1 = 0, + uint64_t testhashu64_fnv1 = 0, + int32_t testhashs32_fnv1a = 0, + uint32_t testhashu32_fnv1a = 0, + int64_t testhashs64_fnv1a = 0, + uint64_t testhashu64_fnv1a = 0, + flatbuffers::Offset<flatbuffers::Vector<uint8_t>> testarrayofbools = 0, + float testf = 3.14159f, + float testf2 = 3.0f, + float testf3 = 0.0f, + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring2 = 0) { MonsterBuilder builder_(_fbb); builder_.add_testhashu64_fnv1a(testhashu64_fnv1a); builder_.add_testhashs64_fnv1a(testhashs64_fnv1a); builder_.add_testhashu64_fnv1(testhashu64_fnv1); builder_.add_testhashs64_fnv1(testhashs64_fnv1); + builder_.add_testarrayofstring2(testarrayofstring2); builder_.add_testf3(testf3); builder_.add_testf2(testf2); builder_.add_testf(testf); @@ -410,25 +542,198 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder return builder_.Finish(); } +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + const char *name = nullptr, + const std::vector<uint8_t> *inventory = nullptr, + Color color = Color_Blue, + Any test_type = Any_NONE, + flatbuffers::Offset<void> test = 0, + const std::vector<const Test *> *test4 = nullptr, + const std::vector<flatbuffers::Offset<flatbuffers::String>> *testarrayofstring = nullptr, + const std::vector<flatbuffers::Offset<Monster>> *testarrayoftables = nullptr, + flatbuffers::Offset<Monster> enemy = 0, + const std::vector<uint8_t> *testnestedflatbuffer = nullptr, + flatbuffers::Offset<Stat> testempty = 0, + bool testbool = false, + int32_t testhashs32_fnv1 = 0, + uint32_t testhashu32_fnv1 = 0, + int64_t testhashs64_fnv1 = 0, + uint64_t testhashu64_fnv1 = 0, + int32_t testhashs32_fnv1a = 0, + uint32_t testhashu32_fnv1a = 0, + int64_t testhashs64_fnv1a = 0, + uint64_t testhashu64_fnv1a = 0, + const std::vector<uint8_t> *testarrayofbools = nullptr, + float testf = 3.14159f, + float testf2 = 3.0f, + float testf3 = 0.0f, + const std::vector<flatbuffers::Offset<flatbuffers::String>> *testarrayofstring2 = nullptr) { + return CreateMonster(_fbb, pos, mana, hp, name ? 0 : _fbb.CreateString(name), inventory ? 0 : _fbb.CreateVector<uint8_t>(*inventory), color, test_type, test, test4 ? 0 : _fbb.CreateVector<const Test *>(*test4), testarrayofstring ? 0 : _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*testarrayofstring), testarrayoftables ? 0 : _fbb.CreateVector<flatbuffers::Offset<Monster>>(*testarrayoftables), enemy, testnestedflatbuffer ? 0 : _fbb.CreateVector<uint8_t>(*testnestedflatbuffer), testempty, testbool, testhashs32_fnv1, testhashu32_fnv1, testhashs64_fnv1, testhashu64_fnv1, testhashs32_fnv1a, testhashu32_fnv1a, testhashs64_fnv1a, testhashu64_fnv1a, testarrayofbools ? 0 : _fbb.CreateVector<uint8_t>(*testarrayofbools), testf, testf2, testf3, testarrayofstring2 ? 0 : _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*testarrayofstring2)); +} + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o); + +} // namespace Example + +namespace Example2 { + +inline std::unique_ptr<MonsterT> Monster::UnPack() const { + auto _o = new MonsterT(); + return std::unique_ptr<MonsterT>(_o); +} + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) { + (void)_o; + return CreateMonster(_fbb); +} + +} // namespace Example2 + +namespace Example { + +inline std::unique_ptr<TestSimpleTableWithEnumT> TestSimpleTableWithEnum::UnPack() const { + auto _o = new TestSimpleTableWithEnumT(); + { auto _e = color(); _o->color = _e; }; + return std::unique_ptr<TestSimpleTableWithEnumT>(_o); +} + +inline flatbuffers::Offset<TestSimpleTableWithEnum> CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o) { + return CreateTestSimpleTableWithEnum(_fbb, + _o->color); +} + +inline std::unique_ptr<StatT> Stat::UnPack() const { + auto _o = new StatT(); + { auto _e = id(); if (_e) _o->id = _e->str(); }; + { auto _e = val(); _o->val = _e; }; + { auto _e = count(); _o->count = _e; }; + return std::unique_ptr<StatT>(_o); +} + +inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb, const StatT *_o) { + return CreateStat(_fbb, + _o->id.size() ? _fbb.CreateString(_o->id) : 0, + _o->val, + _o->count); +} + +inline std::unique_ptr<MonsterT> Monster::UnPack() const { + auto _o = new MonsterT(); + { auto _e = pos(); if (_e) _o->pos = std::unique_ptr<Vec3>(new Vec3(*_e)); }; + { auto _e = mana(); _o->mana = _e; }; + { auto _e = hp(); _o->hp = _e; }; + { auto _e = name(); if (_e) _o->name = _e->str(); }; + { auto _e = inventory(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->inventory.push_back(_e->Get(_i)); } } }; + { auto _e = color(); _o->color = _e; }; + { auto _e = test_type(); _o->test.type = _e; }; + { auto _e = test(); if (_e) _o->test.table = AnyUnion::UnPack(_e, test_type()); }; + { auto _e = test4(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->test4.push_back(*_e->Get(_i)); } } }; + { auto _e = testarrayofstring(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring.push_back(_e->Get(_i)->str()); } } }; + { auto _e = testarrayoftables(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayoftables.push_back(_e->Get(_i)->UnPack()); } } }; + { auto _e = enemy(); if (_e) _o->enemy = _e->UnPack(); }; + { auto _e = testnestedflatbuffer(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testnestedflatbuffer.push_back(_e->Get(_i)); } } }; + { auto _e = testempty(); if (_e) _o->testempty = _e->UnPack(); }; + { auto _e = testbool(); _o->testbool = _e; }; + { auto _e = testhashs32_fnv1(); _o->testhashs32_fnv1 = _e; }; + { auto _e = testhashu32_fnv1(); _o->testhashu32_fnv1 = _e; }; + { auto _e = testhashs64_fnv1(); _o->testhashs64_fnv1 = _e; }; + { auto _e = testhashu64_fnv1(); _o->testhashu64_fnv1 = _e; }; + { auto _e = testhashs32_fnv1a(); _o->testhashs32_fnv1a = _e; }; + { auto _e = testhashu32_fnv1a(); _o->testhashu32_fnv1a = _e; }; + { auto _e = testhashs64_fnv1a(); _o->testhashs64_fnv1a = _e; }; + { auto _e = testhashu64_fnv1a(); _o->testhashu64_fnv1a = _e; }; + { auto _e = testarrayofbools(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofbools.push_back(_e->Get(_i)!=0); } } }; + { auto _e = testf(); _o->testf = _e; }; + { auto _e = testf2(); _o->testf2 = _e; }; + { auto _e = testf3(); _o->testf3 = _e; }; + { auto _e = testarrayofstring2(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring2.push_back(_e->Get(_i)->str()); } } }; + return std::unique_ptr<MonsterT>(_o); +} + +inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) { + return CreateMonster(_fbb, + _o->pos ? _o->pos.get() : 0, + _o->mana, + _o->hp, + _fbb.CreateString(_o->name), + _o->inventory.size() ? _fbb.CreateVector(_o->inventory) : 0, + _o->color, + _o->test.type, + _o->test.Pack(_fbb), + _o->test4.size() ? _fbb.CreateVectorOfStructs(_o->test4) : 0, + _o->testarrayofstring.size() ? _fbb.CreateVectorOfStrings(_o->testarrayofstring) : 0, + _o->testarrayoftables.size() ? _fbb.CreateVector<flatbuffers::Offset<Monster>>(_o->testarrayoftables.size(), [&](size_t i) { return CreateMonster(_fbb, _o->testarrayoftables[i].get()); }) : 0, + _o->enemy ? CreateMonster(_fbb, _o->enemy.get()) : 0, + _o->testnestedflatbuffer.size() ? _fbb.CreateVector(_o->testnestedflatbuffer) : 0, + _o->testempty ? CreateStat(_fbb, _o->testempty.get()) : 0, + _o->testbool, + _o->testhashs32_fnv1, + _o->testhashu32_fnv1, + _o->testhashs64_fnv1, + _o->testhashu64_fnv1, + _o->testhashs32_fnv1a, + _o->testhashu32_fnv1a, + _o->testhashs64_fnv1a, + _o->testhashu64_fnv1a, + _o->testarrayofbools.size() ? _fbb.CreateVector(_o->testarrayofbools) : 0, + _o->testf, + _o->testf2, + _o->testf3, + _o->testarrayofstring2.size() ? _fbb.CreateVectorOfStrings(_o->testarrayofstring2) : 0); +} + inline bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, Any type) { switch (type) { case Any_NONE: return true; case Any_Monster: return verifier.VerifyTable(reinterpret_cast<const Monster *>(union_obj)); case Any_TestSimpleTableWithEnum: return verifier.VerifyTable(reinterpret_cast<const TestSimpleTableWithEnum *>(union_obj)); + case Any_MyGame_Example2_Monster: return verifier.VerifyTable(reinterpret_cast<const MyGame::Example2::Monster *>(union_obj)); default: return false; } } +inline flatbuffers::NativeTable *AnyUnion::UnPack(const void *union_obj, Any type) { + switch (type) { + case Any_NONE: return nullptr; + case Any_Monster: return reinterpret_cast<const Monster *>(union_obj)->UnPack().release(); + case Any_TestSimpleTableWithEnum: return reinterpret_cast<const TestSimpleTableWithEnum *>(union_obj)->UnPack().release(); + case Any_MyGame_Example2_Monster: return reinterpret_cast<const MyGame::Example2::Monster *>(union_obj)->UnPack().release(); + default: return nullptr; + } +} + +inline flatbuffers::Offset<void> AnyUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb) const { + switch (type) { + case Any_NONE: return 0; + case Any_Monster: return CreateMonster(_fbb, reinterpret_cast<const MonsterT *>(table)).Union(); + case Any_TestSimpleTableWithEnum: return CreateTestSimpleTableWithEnum(_fbb, reinterpret_cast<const TestSimpleTableWithEnumT *>(table)).Union(); + case Any_MyGame_Example2_Monster: return CreateMonster(_fbb, reinterpret_cast<const MyGame::Example2::MonsterT *>(table)).Union(); + default: return 0; + } +} + +inline AnyUnion::~AnyUnion() { + switch (type) { + case Any_Monster: delete reinterpret_cast<MonsterT *>(table); break; + case Any_TestSimpleTableWithEnum: delete reinterpret_cast<TestSimpleTableWithEnumT *>(table); break; + case Any_MyGame_Example2_Monster: delete reinterpret_cast<MyGame::Example2::MonsterT *>(table); break; + default:; + } +} + inline const MyGame::Example::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<MyGame::Example::Monster>(buf); } inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot<Monster>(buf); } -inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<MyGame::Example::Monster>(); } - inline const char *MonsterIdentifier() { return "MONS"; } inline bool MonsterBufferHasIdentifier(const void *buf) { return flatbuffers::BufferHasIdentifier(buf, MonsterIdentifier()); } +inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<MyGame::Example::Monster>(MonsterIdentifier()); } + inline const char *MonsterExtension() { return "mon"; } inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<MyGame::Example::Monster> root) { fbb.Finish(root, MonsterIdentifier()); } diff --git a/tests/monster_test_generated.js b/tests/monster_test_generated.js index 8316fbc6..f67e8480 100644 --- a/tests/monster_test_generated.js +++ b/tests/monster_test_generated.js @@ -13,6 +13,11 @@ MyGame.Example = MyGame.Example || {}; /** * @const */ +MyGame.Example2 = MyGame.Example2 || {}; + +/** + * @const +*/ MyGame.OtherNameSpace = MyGame.OtherNameSpace || {}; /** @@ -30,7 +35,59 @@ MyGame.Example.Color = { MyGame.Example.Any = { NONE: 0, Monster: 1, - TestSimpleTableWithEnum: 2 + TestSimpleTableWithEnum: 2, + MyGame_Example2_Monster: 3 +}; + +/** + * @constructor + */ +MyGame.Example2.Monster = function() { + /** + * @type {flatbuffers.ByteBuffer} + */ + this.bb = null; + + /** + * @type {number} + */ + this.bb_pos = 0; +}; + +/** + * @param {number} i + * @param {flatbuffers.ByteBuffer} bb + * @returns {MyGame.Example2.Monster} + */ +MyGame.Example2.Monster.prototype.__init = function(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; +}; + +/** + * @param {flatbuffers.ByteBuffer} bb + * @param {MyGame.Example2.Monster=} obj + * @returns {MyGame.Example2.Monster} + */ +MyGame.Example2.Monster.getRootAsMonster = function(bb, obj) { + return (obj || new MyGame.Example2.Monster).__init(bb.readInt32(bb.position()) + bb.position(), bb); +}; + +/** + * @param {flatbuffers.Builder} builder + */ +MyGame.Example2.Monster.startMonster = function(builder) { + builder.startObject(0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @returns {flatbuffers.Offset} + */ +MyGame.Example2.Monster.endMonster = function(builder) { + var offset = builder.endObject(); + return offset; }; /** @@ -702,10 +759,28 @@ MyGame.Example.Monster.prototype.testf3 = function() { }; /** + * @param {number} index + * @param {flatbuffers.Encoding=} optionalEncoding + * @returns {string|Uint8Array} + */ +MyGame.Example.Monster.prototype.testarrayofstring2 = function(index, optionalEncoding) { + var offset = this.bb.__offset(this.bb_pos, 60); + return offset ? this.bb.__string(this.bb.__vector(this.bb_pos + offset) + index * 4, optionalEncoding) : null; +}; + +/** + * @returns {number} + */ +MyGame.Example.Monster.prototype.testarrayofstring2Length = function() { + var offset = this.bb.__offset(this.bb_pos, 60); + return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0; +}; + +/** * @param {flatbuffers.Builder} builder */ MyGame.Example.Monster.startMonster = function(builder) { - builder.startObject(28); + builder.startObject(29); }; /** @@ -1039,6 +1114,35 @@ MyGame.Example.Monster.addTestf3 = function(builder, testf3) { /** * @param {flatbuffers.Builder} builder + * @param {flatbuffers.Offset} testarrayofstring2Offset + */ +MyGame.Example.Monster.addTestarrayofstring2 = function(builder, testarrayofstring2Offset) { + builder.addFieldOffset(28, testarrayofstring2Offset, 0); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {Array.<flatbuffers.Offset>} data + * @returns {flatbuffers.Offset} + */ +MyGame.Example.Monster.createTestarrayofstring2Vector = function(builder, data) { + builder.startVector(4, data.length, 4); + for (var i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]); + } + return builder.endVector(); +}; + +/** + * @param {flatbuffers.Builder} builder + * @param {number} numElems + */ +MyGame.Example.Monster.startTestarrayofstring2Vector = function(builder, numElems) { + builder.startVector(4, numElems, 4); +}; + +/** + * @param {flatbuffers.Builder} builder * @returns {flatbuffers.Offset} */ MyGame.Example.Monster.endMonster = function(builder) { diff --git a/tests/monsterdata_test.mon b/tests/monsterdata_test.mon Binary files differindex 7a7fbf70..01bd5279 100644 --- a/tests/monsterdata_test.mon +++ b/tests/monsterdata_test.mon diff --git a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java index ff555954..e23cecc0 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java +++ b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java @@ -8,8 +8,8 @@ public final class EnumInNestedNS { public static final byte B = 1; public static final byte C = 2; - private static final String[] names = { "A", "B", "C", }; + public static final String[] names = { "A", "B", "C", }; public static String name(int e) { return names[e]; } -}; +} diff --git a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go index f3684623..f834a721 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go +++ b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go @@ -15,7 +15,11 @@ func (rcv *StructInNestedNS) Init(buf []byte, i flatbuffers.UOffsetT) { } func (rcv *StructInNestedNS) A() int32 { return rcv._tab.GetInt32(rcv._tab.Pos + flatbuffers.UOffsetT(0)) } +func (rcv *StructInNestedNS) MutateA(n int32) bool { return rcv._tab.MutateInt32(rcv._tab.Pos + flatbuffers.UOffsetT(0), n) } + func (rcv *StructInNestedNS) B() int32 { return rcv._tab.GetInt32(rcv._tab.Pos + flatbuffers.UOffsetT(4)) } +func (rcv *StructInNestedNS) MutateB(n int32) bool { return rcv._tab.MutateInt32(rcv._tab.Pos + flatbuffers.UOffsetT(4), n) } + func CreateStructInNestedNS(builder *flatbuffers.Builder, a int32, b int32) flatbuffers.UOffsetT { builder.Prep(4, 8) diff --git a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java index 93f80522..fede07a0 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java +++ b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java @@ -22,5 +22,5 @@ public final class StructInNestedNS extends Struct { builder.putInt(a); return builder.offset(); } -}; +} diff --git a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go index cf2b557c..c956fb42 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go +++ b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go @@ -22,6 +22,10 @@ func (rcv *TableInNestedNS) Foo() int32 { return 0 } +func (rcv *TableInNestedNS) MutateFoo(n int32) bool { + return rcv._tab.MutateInt32Slot(4, n) +} + func TableInNestedNSStart(builder *flatbuffers.Builder) { builder.StartObject(1) } func TableInNestedNSAddFoo(builder *flatbuffers.Builder, foo int32) { builder.PrependInt32Slot(0, foo, 0) } func TableInNestedNSEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java index 2cfb1bac..fc518563 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java +++ b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java @@ -29,5 +29,5 @@ public final class TableInNestedNS extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/NamespaceA/SecondTableInA.java b/tests/namespace_test/NamespaceA/SecondTableInA.java index acabc393..e6f390a9 100644 --- a/tests/namespace_test/NamespaceA/SecondTableInA.java +++ b/tests/namespace_test/NamespaceA/SecondTableInA.java @@ -29,5 +29,5 @@ public final class SecondTableInA extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.go b/tests/namespace_test/NamespaceA/TableInFirstNS.go index 5580d5d5..0a002b3b 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.go +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.go @@ -35,6 +35,10 @@ func (rcv *TableInFirstNS) FooEnum() int8 { return 0 } +func (rcv *TableInFirstNS) MutateFooEnum(n int8) bool { + return rcv._tab.MutateInt8Slot(6, n) +} + func (rcv *TableInFirstNS) FooStruct(obj *StructInNestedNS) *StructInNestedNS { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.java b/tests/namespace_test/NamespaceA/TableInFirstNS.java index 87397930..b44df978 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.java +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.java @@ -28,5 +28,5 @@ public final class TableInFirstNS extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/NamespaceC/TableInC.cs b/tests/namespace_test/NamespaceC/TableInC.cs new file mode 100644 index 00000000..0f75dfec --- /dev/null +++ b/tests/namespace_test/NamespaceC/TableInC.cs @@ -0,0 +1,38 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +namespace NamespaceC +{ + +using System; +using FlatBuffers; + +public sealed class TableInC : Table { + public static TableInC GetRootAsTableInC(ByteBuffer _bb) { return GetRootAsTableInC(_bb, new TableInC()); } + public static TableInC GetRootAsTableInC(ByteBuffer _bb, TableInC obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); } + public TableInC __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + public NamespaceA.TableInFirstNS ReferToA1 { get { return GetReferToA1(new NamespaceA.TableInFirstNS()); } } + public NamespaceA.TableInFirstNS GetReferToA1(NamespaceA.TableInFirstNS obj) { int o = __offset(4); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } + public SecondTableInA ReferToA2 { get { return GetReferToA2(new SecondTableInA()); } } + public SecondTableInA GetReferToA2(SecondTableInA obj) { int o = __offset(6); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } + + public static Offset<NamespaceC.TableInC> CreateTableInC(FlatBufferBuilder builder, + Offset<NamespaceA.TableInFirstNS> refer_to_a1Offset = default(Offset<NamespaceA.TableInFirstNS>), + Offset<SecondTableInA> refer_to_a2Offset = default(Offset<SecondTableInA>)) { + builder.StartObject(2); + TableInC.AddReferToA2(builder, refer_to_a2Offset); + TableInC.AddReferToA1(builder, refer_to_a1Offset); + return TableInC.EndTableInC(builder); + } + + public static void StartTableInC(FlatBufferBuilder builder) { builder.StartObject(2); } + public static void AddReferToA1(FlatBufferBuilder builder, Offset<NamespaceA.TableInFirstNS> referToA1Offset) { builder.AddOffset(0, referToA1Offset.Value, 0); } + public static void AddReferToA2(FlatBufferBuilder builder, Offset<SecondTableInA> referToA2Offset) { builder.AddOffset(1, referToA2Offset.Value, 0); } + public static Offset<NamespaceC.TableInC> EndTableInC(FlatBufferBuilder builder) { + int o = builder.EndObject(); + return new Offset<NamespaceC.TableInC>(o); + } +}; + + +} diff --git a/tests/namespace_test/NamespaceC/TableInC.go b/tests/namespace_test/NamespaceC/TableInC.go new file mode 100644 index 00000000..dc75c45c --- /dev/null +++ b/tests/namespace_test/NamespaceC/TableInC.go @@ -0,0 +1,46 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package NamespaceC + +import ( + flatbuffers "github.com/google/flatbuffers/go" +) +type TableInC struct { + _tab flatbuffers.Table +} + +func (rcv *TableInC) Init(buf []byte, i flatbuffers.UOffsetT) { + rcv._tab.Bytes = buf + rcv._tab.Pos = i +} + +func (rcv *TableInC) ReferToA1(obj *TableInFirstNS) *TableInFirstNS { + o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) + if o != 0 { + x := rcv._tab.Indirect(o + rcv._tab.Pos) + if obj == nil { + obj = new(TableInFirstNS) + } + obj.Init(rcv._tab.Bytes, x) + return obj + } + return nil +} + +func (rcv *TableInC) ReferToA2(obj *SecondTableInA) *SecondTableInA { + o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) + if o != 0 { + x := rcv._tab.Indirect(o + rcv._tab.Pos) + if obj == nil { + obj = new(SecondTableInA) + } + obj.Init(rcv._tab.Bytes, x) + return obj + } + return nil +} + +func TableInCStart(builder *flatbuffers.Builder) { builder.StartObject(2) } +func TableInCAddReferToA1(builder *flatbuffers.Builder, referToA1 flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(referToA1), 0) } +func TableInCAddReferToA2(builder *flatbuffers.Builder, referToA2 flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(referToA2), 0) } +func TableInCEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/namespace_test/NamespaceC/TableInC.java b/tests/namespace_test/NamespaceC/TableInC.java new file mode 100644 index 00000000..19bb4cd5 --- /dev/null +++ b/tests/namespace_test/NamespaceC/TableInC.java @@ -0,0 +1,38 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package NamespaceC; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class TableInC extends Table { + public static TableInC getRootAsTableInC(ByteBuffer _bb) { return getRootAsTableInC(_bb, new TableInC()); } + public static TableInC getRootAsTableInC(ByteBuffer _bb, TableInC obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__init(_bb.getInt(_bb.position()) + _bb.position(), _bb)); } + public TableInC __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } + + public NamespaceA.TableInFirstNS referToA1() { return referToA1(new NamespaceA.TableInFirstNS()); } + public NamespaceA.TableInFirstNS referToA1(NamespaceA.TableInFirstNS obj) { int o = __offset(4); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } + public SecondTableInA referToA2() { return referToA2(new SecondTableInA()); } + public SecondTableInA referToA2(SecondTableInA obj) { int o = __offset(6); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } + + public static int createTableInC(FlatBufferBuilder builder, + int refer_to_a1Offset, + int refer_to_a2Offset) { + builder.startObject(2); + TableInC.addReferToA2(builder, refer_to_a2Offset); + TableInC.addReferToA1(builder, refer_to_a1Offset); + return TableInC.endTableInC(builder); + } + + public static void startTableInC(FlatBufferBuilder builder) { builder.startObject(2); } + public static void addReferToA1(FlatBufferBuilder builder, int referToA1Offset) { builder.addOffset(0, referToA1Offset, 0); } + public static void addReferToA2(FlatBufferBuilder builder, int referToA2Offset) { builder.addOffset(1, referToA2Offset, 0); } + public static int endTableInC(FlatBufferBuilder builder) { + int o = builder.endObject(); + return o; + } +} + diff --git a/tests/namespace_test/NamespaceC/TableInC.php b/tests/namespace_test/NamespaceC/TableInC.php new file mode 100644 index 00000000..116aea1f --- /dev/null +++ b/tests/namespace_test/NamespaceC/TableInC.php @@ -0,0 +1,100 @@ +<?php +// automatically generated by the FlatBuffers compiler, do not modify + +namespace NamespaceC; + +use \Google\FlatBuffers\Struct; +use \Google\FlatBuffers\Table; +use \Google\FlatBuffers\ByteBuffer; +use \Google\FlatBuffers\FlatBufferBuilder; + +class TableInC extends Table +{ + /** + * @param ByteBuffer $bb + * @return TableInC + */ + public static function getRootAsTableInC(ByteBuffer $bb) + { + $obj = new TableInC(); + return ($obj->init($bb->getInt($bb->getPosition()) + $bb->getPosition(), $bb)); + } + + /** + * @param int $_i offset + * @param ByteBuffer $_bb + * @return TableInC + **/ + public function init($_i, ByteBuffer $_bb) + { + $this->bb_pos = $_i; + $this->bb = $_bb; + return $this; + } + + public function getReferToA1() + { + $obj = new TableInFirstNS(); + $o = $this->__offset(4); + return $o != 0 ? $obj->init($this->__indirect($o + $this->bb_pos), $this->bb) : 0; + } + + public function getReferToA2() + { + $obj = new SecondTableInA(); + $o = $this->__offset(6); + return $o != 0 ? $obj->init($this->__indirect($o + $this->bb_pos), $this->bb) : 0; + } + + /** + * @param FlatBufferBuilder $builder + * @return void + */ + public static function startTableInC(FlatBufferBuilder $builder) + { + $builder->StartObject(2); + } + + /** + * @param FlatBufferBuilder $builder + * @return TableInC + */ + public static function createTableInC(FlatBufferBuilder $builder, $refer_to_a1, $refer_to_a2) + { + $builder->startObject(2); + self::addReferToA1($builder, $refer_to_a1); + self::addReferToA2($builder, $refer_to_a2); + $o = $builder->endObject(); + return $o; + } + + /** + * @param FlatBufferBuilder $builder + * @param int + * @return void + */ + public static function addReferToA1(FlatBufferBuilder $builder, $referToA1) + { + $builder->addOffsetX(0, $referToA1, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @param int + * @return void + */ + public static function addReferToA2(FlatBufferBuilder $builder, $referToA2) + { + $builder->addOffsetX(1, $referToA2, 0); + } + + /** + * @param FlatBufferBuilder $builder + * @return int table offset + */ + public static function endTableInC(FlatBufferBuilder $builder) + { + $o = $builder->endObject(); + return $o; + } +} diff --git a/tests/namespace_test/NamespaceC/TableInC.py b/tests/namespace_test/NamespaceC/TableInC.py new file mode 100644 index 00000000..5a4376ed --- /dev/null +++ b/tests/namespace_test/NamespaceC/TableInC.py @@ -0,0 +1,39 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: NamespaceC + +import flatbuffers + +class TableInC(object): + __slots__ = ['_tab'] + + # TableInC + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # TableInC + def ReferToA1(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + if o != 0: + x = self._tab.Indirect(o + self._tab.Pos) + from .TableInFirstNS import TableInFirstNS + obj = TableInFirstNS() + obj.Init(self._tab.Bytes, x) + return obj + return None + + # TableInC + def ReferToA2(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6)) + if o != 0: + x = self._tab.Indirect(o + self._tab.Pos) + from .SecondTableInA import SecondTableInA + obj = SecondTableInA() + obj.Init(self._tab.Bytes, x) + return obj + return None + +def TableInCStart(builder): builder.StartObject(2) +def TableInCAddReferToA1(builder, referToA1): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(referToA1), 0) +def TableInCAddReferToA2(builder, referToA2): builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(referToA2), 0) +def TableInCEnd(builder): return builder.EndObject() diff --git a/tests/namespace_test/namespace_test1_generated.h b/tests/namespace_test/namespace_test1_generated.h index 9e10eb4c..59d4030a 100644 --- a/tests/namespace_test/namespace_test1_generated.h +++ b/tests/namespace_test/namespace_test1_generated.h @@ -33,6 +33,8 @@ MANUALLY_ALIGNED_STRUCT(4) StructInNestedNS FLATBUFFERS_FINAL_CLASS { int32_t b_; public: + StructInNestedNS() { memset(this, 0, sizeof(StructInNestedNS)); } + StructInNestedNS(const StructInNestedNS &_o) { memcpy(this, &_o, sizeof(StructInNestedNS)); } StructInNestedNS(int32_t _a, int32_t _b) : a_(flatbuffers::EndianScalar(_a)), b_(flatbuffers::EndianScalar(_b)) { } @@ -69,7 +71,7 @@ struct TableInNestedNSBuilder { }; inline flatbuffers::Offset<TableInNestedNS> CreateTableInNestedNS(flatbuffers::FlatBufferBuilder &_fbb, - int32_t foo = 0) { + int32_t foo = 0) { TableInNestedNSBuilder builder_(_fbb); builder_.add_foo(foo); return builder_.Finish(); diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index 8e6b3ada..77578bc6 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -60,9 +60,9 @@ struct TableInFirstNSBuilder { }; inline flatbuffers::Offset<TableInFirstNS> CreateTableInFirstNS(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset<NamespaceA::NamespaceB::TableInNestedNS> foo_table = 0, - NamespaceA::NamespaceB::EnumInNestedNS foo_enum = NamespaceA::NamespaceB::EnumInNestedNS_A, - const NamespaceA::NamespaceB::StructInNestedNS *foo_struct = 0) { + flatbuffers::Offset<NamespaceA::NamespaceB::TableInNestedNS> foo_table = 0, + NamespaceA::NamespaceB::EnumInNestedNS foo_enum = NamespaceA::NamespaceB::EnumInNestedNS_A, + const NamespaceA::NamespaceB::StructInNestedNS *foo_struct = 0) { TableInFirstNSBuilder builder_(_fbb); builder_.add_foo_struct(foo_struct); builder_.add_foo_table(foo_table); @@ -107,8 +107,8 @@ struct TableInCBuilder { }; inline flatbuffers::Offset<TableInC> CreateTableInC(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset<NamespaceA::TableInFirstNS> refer_to_a1 = 0, - flatbuffers::Offset<NamespaceA::SecondTableInA> refer_to_a2 = 0) { + flatbuffers::Offset<NamespaceA::TableInFirstNS> refer_to_a1 = 0, + flatbuffers::Offset<NamespaceA::SecondTableInA> refer_to_a2 = 0) { TableInCBuilder builder_(_fbb); builder_.add_refer_to_a2(refer_to_a2); builder_.add_refer_to_a1(refer_to_a1); @@ -146,7 +146,7 @@ struct SecondTableInABuilder { }; inline flatbuffers::Offset<SecondTableInA> CreateSecondTableInA(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset<NamespaceC::TableInC> refer_to_c = 0) { + flatbuffers::Offset<NamespaceC::TableInC> refer_to_c = 0) { SecondTableInABuilder builder_(_fbb); builder_.add_refer_to_c(refer_to_c); return builder_.Finish(); @@ -154,4 +154,12 @@ inline flatbuffers::Offset<SecondTableInA> CreateSecondTableInA(flatbuffers::Fla } // namespace NamespaceA +namespace NamespaceC { + +} // namespace NamespaceC + +namespace NamespaceA { + +} // namespace NamespaceA + #endif // FLATBUFFERS_GENERATED_NAMESPACETEST2_NAMESPACEA_H_ diff --git a/tests/py_test.py b/tests/py_test.py index 9129de70..e6609449 100644 --- a/tests/py_test.py +++ b/tests/py_test.py @@ -162,9 +162,10 @@ class TestFuzz(unittest.TestCase): ''' Low level stress/fuzz test: serialize/deserialize a variety of different kinds of data in different combinations ''' - ofInt32Bytes = compat.binary_type([0x83, 0x33, 0x33, 0x33]) - ofInt64Bytes = compat.binary_type([0x84, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44]) + binary_type = compat.binary_types[0] # this will always exist + ofInt32Bytes = binary_type([0x83, 0x33, 0x33, 0x33]) + ofInt64Bytes = binary_type([0x84, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44]) overflowingInt32Val = flatbuffers.encode.Get(flatbuffers.packer.int32, ofInt32Bytes, 0) overflowingInt64Val = flatbuffers.encode.Get(flatbuffers.packer.int64, diff --git a/tests/test.cpp b/tests/test.cpp index ca3ea03a..6ec4e678 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -15,6 +15,7 @@ */ #define FLATBUFFERS_DEBUG_VERIFICATION_FAILURE 1 +#define FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" @@ -24,7 +25,9 @@ #include "namespace_test/namespace_test1_generated.h" #include "namespace_test/namespace_test2_generated.h" -#include <random> +#ifndef FLATBUFFERS_CPP98_STL + #include <random> +#endif using namespace MyGame::Example; @@ -114,13 +117,19 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) { mb3.add_name(wilma); mlocs[2] = mb3.Finish(); - // Create an array of strings. Also test string pooling. - flatbuffers::Offset<flatbuffers::String> strings[4]; - strings[0] = builder.CreateSharedString("bob"); - strings[1] = builder.CreateSharedString("fred"); - strings[2] = builder.CreateSharedString("bob"); - strings[3] = builder.CreateSharedString("fred"); - auto vecofstrings = builder.CreateVector(strings, 4); + // Create an array of strings. Also test string pooling, and lambdas. + const char *names[] = { "bob", "fred", "bob", "fred" }; + auto vecofstrings = + builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(4, + [&](size_t i) { + return builder.CreateSharedString(names[i]); + }); + + // Creating vectors of strings in one convenient call. + std::vector<std::string> names2; + names2.push_back("jane"); + names2.push_back("mary"); + auto vecofstrings2 = builder.CreateVectorOfStrings(names2); // Create an array of sorted tables, can be used with binary search when read: auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3); @@ -128,7 +137,9 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) { // shortcut for creating monster with all fields set: auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster, mlocs[1].Union(), // Store a union. - testv, vecofstrings, vecoftables, 0); + testv, vecofstrings, vecoftables, 0, 0, 0, false, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, + vecofstrings2); FinishMonsterBuffer(builder, mloc); @@ -148,12 +159,30 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) { } // example of accessing a buffer loaded in memory: -void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) { +void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, + bool pooled = true) { // First, verify the buffers integrity (optional) flatbuffers::Verifier verifier(flatbuf, length); TEST_EQ(VerifyMonsterBuffer(verifier), true); + std::vector<uint8_t> test_buff; + test_buff.resize(length * 2); + std::memcpy(&test_buff[0], flatbuf , length); + std::memcpy(&test_buff[length], flatbuf , length); + + flatbuffers::Verifier verifierl(&test_buff[0], length - 1); + TEST_EQ(VerifyMonsterBuffer(verifierl), false); + TEST_EQ(verifierl.GetComputedSize(), 0); + + flatbuffers::Verifier verifier1(&test_buff[0], length); + TEST_EQ(VerifyMonsterBuffer(verifier1), true); + TEST_EQ(verifier1.GetComputedSize(), length); + + flatbuffers::Verifier verifier2(&test_buff[length], length); + TEST_EQ(VerifyMonsterBuffer(verifier2), true); + TEST_EQ(verifier2.GetComputedSize(), length); + TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0); TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true); TEST_EQ(strcmp(MonsterExtension(), "mon"), 0); @@ -194,9 +223,18 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) { TEST_EQ(vecofstrings->Length(), 4U); TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob"); TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred"); - // These should have pointer equality because of string pooling. - TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str()); - TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str()); + if (pooled) { + // These should have pointer equality because of string pooling. + TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str()); + TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str()); + } + + auto vecofstrings2 = monster->testarrayofstring2(); + if (vecofstrings2) { + TEST_EQ(vecofstrings2->Length(), 2U); + TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane"); + TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary"); + } // Example of accessing a vector of tables: auto vecoftables = monster->testarrayoftables(); @@ -274,6 +312,77 @@ void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) { AccessFlatBufferTest(flatbuf, length); } +// Unpack a FlatBuffer into objects. +void ObjectFlatBuffersTest(uint8_t *flatbuf) { + // Turn a buffer into C++ objects. + auto monster1 = GetMonster(flatbuf)->UnPack(); + + // Re-serialize the data. + flatbuffers::FlatBufferBuilder fbb1; + fbb1.Finish(CreateMonster(fbb1, monster1.get()), MonsterIdentifier()); + + // Unpack again, and re-serialize again. + auto monster2 = GetMonster(fbb1.GetBufferPointer())->UnPack(); + flatbuffers::FlatBufferBuilder fbb2; + fbb2.Finish(CreateMonster(fbb2, monster2.get()), MonsterIdentifier()); + + // Now we've gone full round-trip, the two buffers should match. + auto len1 = fbb1.GetSize(); + auto len2 = fbb2.GetSize(); + TEST_EQ(len1, len2); + TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), + len1), 0); + + // Test it with the original buffer test to make sure all data survived. + AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false); + + // Test accessing fields, similar to AccessFlatBufferTest above. + TEST_EQ(monster2->hp, 80); + TEST_EQ(monster2->mana, 150); // default + TEST_EQ_STR(monster2->name.c_str(), "MyMonster"); + + auto &pos = monster2->pos; + TEST_NOTNULL(pos); + TEST_EQ(pos->z(), 3); + TEST_EQ(pos->test3().a(), 10); + TEST_EQ(pos->test3().b(), 20); + + auto &inventory = monster2->inventory; + TEST_EQ(inventory.size(), 10UL); + unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + for (auto it = inventory.begin(); it != inventory.end(); ++it) + TEST_EQ(*it, inv_data[it - inventory.begin()]); + + TEST_EQ(monster2->color, Color_Blue); + + auto monster3 = monster2->test.AsMonster(); + TEST_NOTNULL(monster3); + TEST_EQ_STR(monster3->name.c_str(), "Fred"); + + auto &vecofstrings = monster2->testarrayofstring; + TEST_EQ(vecofstrings.size(), 4U); + TEST_EQ_STR(vecofstrings[0].c_str(), "bob"); + TEST_EQ_STR(vecofstrings[1].c_str(), "fred"); + + auto &vecofstrings2 = monster2->testarrayofstring2; + TEST_EQ(vecofstrings2.size(), 2U); + TEST_EQ_STR(vecofstrings2[0].c_str(), "jane"); + TEST_EQ_STR(vecofstrings2[1].c_str(), "mary"); + + auto &vecoftables = monster2->testarrayoftables; + TEST_EQ(vecoftables.size(), 3U); + TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney"); + TEST_EQ(vecoftables[0]->hp, 1000); + TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred"); + TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma"); + + auto &tests = monster2->test4; + TEST_EQ(tests[0].a(), 10); + TEST_EQ(tests[0].b(), 20); + TEST_EQ(tests[1].a(), 30); + TEST_EQ(tests[1].b(), 40); +} + // example of parsing text straight into a buffer, and generating // text back from it: void ParseAndGenerateTextTest() { @@ -770,7 +879,7 @@ void ErrorTest() { TestError("table X { Y:int; Y:int; }", "field already"); TestError("struct X { Y:string; }", "only scalar"); TestError("struct X { Y:int (deprecated); }", "deprecate"); - TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {", + TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {}, A:1 }", "missing type field"); TestError("union Z { X } table X { Y:Z; } root_type X; { Y_type: 99, Y: {", "type id"); @@ -801,20 +910,32 @@ void ErrorTest() { TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once"); } -// Additional parser testing not covered elsewhere. -void ScientificTest() { +template<typename T> T TestValue(const char *json, const char *type_name) { flatbuffers::Parser parser; // Simple schema. - TEST_EQ(parser.Parse("table X { Y:float; } root_type X;"), true); + TEST_EQ(parser.Parse(std::string("table X { Y:" + std::string(type_name) + "; } root_type X;").c_str()), true); - // Test scientific notation numbers. - TEST_EQ(parser.Parse("{ Y:0.0314159e+2 }"), true); - auto root = flatbuffers::GetRoot<float>(parser.builder_.GetBufferPointer()); + TEST_EQ(parser.Parse(json), true); + auto root = flatbuffers::GetRoot<T>(parser.builder_.GetBufferPointer()); // root will point to the table, which is a 32bit vtable offset followed // by a float: - TEST_EQ(sizeof(flatbuffers::soffset_t) == 4 && // Test assumes 32bit offsets - fabs(root[1] - 3.14159) < 0.001, true); + TEST_EQ(sizeof(flatbuffers::soffset_t), 4); // Test assumes 32bit offsets + return root[1]; +} + +bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } + +// Additional parser testing not covered elsewhere. +void ValueTest() { + // Test scientific notation numbers. + TEST_EQ(FloatCompare(TestValue<float>("{ Y:0.0314159e+2 }","float"), (float)3.14159), true); + + // Test conversion functions. + TEST_EQ(FloatCompare(TestValue<float>("{ Y:cos(rad(180)) }","float"), -1), true); + + // Test negative hex constant. + TEST_EQ(TestValue<int>("{ Y:-0x80 }","int") == -128, true); } void EnumStringsTest() { @@ -929,6 +1050,34 @@ void UnknownFieldsTest() { TEST_EQ(jsongen == "{str: \"test\",i: 10}", true); } +void ParseUnionTest() { + // Unions must be parseable with the type field following the object. + flatbuffers::Parser parser; + TEST_EQ(parser.Parse("table T { A:int; }" + "union U { T }" + "table V { X:U; }" + "root_type V;" + "{ X:{ A:1 }, X_type: T }"), true); +} + +void ConformTest() { + flatbuffers::Parser parser; + TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true); + + auto test_conform = [&](const char *test, const char *expected_err) { + flatbuffers::Parser parser2; + TEST_EQ(parser2.Parse(test), true); + auto err = parser2.ConformTo(parser); + TEST_NOTNULL(strstr(err.c_str(), expected_err)); + }; + + test_conform("table T { A:byte; }", "types differ for field"); + test_conform("table T { B:int; A:int; }", "offsets differ for field"); + test_conform("table T { A:int = 1; }", "defaults differ for field"); + test_conform("table T { B:float; }", "field renamed to different type"); + test_conform("enum E:byte { B, A }", "values differ for enum"); +} + int main(int /*argc*/, const char * /*argv*/[]) { // Run our various test suites: @@ -940,6 +1089,8 @@ int main(int /*argc*/, const char * /*argv*/[]) { MutateFlatBuffersTest(flatbuf.get(), rawbuf.length()); + ObjectFlatBuffersTest(flatbuf.get()); + #ifndef FLATBUFFERS_NO_FILE_TESTS ParseAndGenerateTextTest(); ReflectionTest(flatbuf.get(), rawbuf.length()); @@ -950,13 +1101,15 @@ int main(int /*argc*/, const char * /*argv*/[]) { FuzzTest2(); ErrorTest(); - ScientificTest(); + ValueTest(); EnumStringsTest(); IntegerOutOfRangeTest(); UnicodeTest(); UnicodeSurrogatesTest(); UnicodeInvalidSurrogatesTest(); UnknownFieldsTest(); + ParseUnionTest(); + ConformTest(); if (!testing_fails) { TEST_OUTPUT_LINE("ALL TESTS PASSED"); |