1 /*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // This is a sandbox for modeling C++17 code generator.
18 // C++17 code generator: "flatc --cpp_std c++17".
19 // Warning:
20 // This is an experimental feature and could change at any time.
21
22 #include "flatbuffers/flatbuffers.h"
23 #include "flatbuffers/flexbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/minireflect.h"
26 #include "flatbuffers/registry.h"
27 #include "flatbuffers/util.h"
28 #include "stringify_util.h"
29 #include "test_assert.h"
30
31 // Embed generated code into an isolated namespace.
32 namespace cpp17 {
33 #include "generated_cpp17/monster_test_generated.h"
34 #include "generated_cpp17/optional_scalars_generated.h"
35 } // namespace cpp17
36
37 namespace cpp11 {
38 #include "../monster_test_generated.h"
39 #include "../optional_scalars_generated.h"
40 } // namespace cpp11
41
42 using ::cpp17::MyGame::Example::Monster;
43 using ::cpp17::MyGame::Example::Vec3;
44
45 /*******************************************************************************
46 ** Build some FB objects.
47 *******************************************************************************/
BuildMonster(flatbuffers::FlatBufferBuilder & fbb)48 const Monster *BuildMonster(flatbuffers::FlatBufferBuilder &fbb) {
49 using ::cpp17::MyGame::Example::Color;
50 using ::cpp17::MyGame::Example::MonsterBuilder;
51 using ::cpp17::MyGame::Example::Test;
52 auto name = fbb.CreateString("my_monster");
53 auto inventory = fbb.CreateVector(std::vector<uint8_t>{ 4, 5, 6, 7 });
54 MonsterBuilder builder(fbb);
55 auto vec3 = Vec3{ /*x=*/1.1f,
56 /*y=*/2.2f,
57 /*z=*/3.3f,
58 /*test1=*/6.6,
59 /*test2=*/Color::Green,
60 /*test3=*/
61 Test(
62 /*a=*/11,
63 /*b=*/90) };
64 builder.add_pos(&vec3);
65 builder.add_name(name);
66 builder.add_mana(1);
67 builder.add_hp(2);
68 builder.add_testbool(true);
69 builder.add_testhashs32_fnv1(4);
70 builder.add_testhashu32_fnv1(5);
71 builder.add_testhashs64_fnv1(6);
72 builder.add_testhashu64_fnv1(7);
73 builder.add_testhashs32_fnv1a(8);
74 builder.add_testhashu32_fnv1a(9);
75 builder.add_testhashs64_fnv1a(10);
76 builder.add_testhashu64_fnv1a(11);
77 builder.add_testf(12.1f);
78 builder.add_testf2(13.1f);
79 builder.add_testf3(14.1f);
80 builder.add_single_weak_reference(15);
81 builder.add_co_owning_reference(16);
82 builder.add_non_owning_reference(17);
83 builder.add_inventory(inventory);
84 fbb.Finish(builder.Finish());
85 const Monster *monster =
86 flatbuffers::GetRoot<Monster>(fbb.GetBufferPointer());
87 return monster;
88 }
89
90 /*******************************************************************************
91 ** Test Case: Static Field Reflection Traits for Table & Structs.
92 *******************************************************************************/
93 // This test tests & demonstrates the power of the static reflection. Using it,
94 // we can given any Flatbuffer type to a generic function and it will be able to
95 // produce is full recursive string representation of it.
96 //
97 // This test covers all types: primitive types, structs, tables, Vectors, etc.
98 //
StringifyAnyFlatbuffersTypeTest()99 void StringifyAnyFlatbuffersTypeTest() {
100 flatbuffers::FlatBufferBuilder fbb;
101 // We are using a Monster here, but we could have used any type, because the
102 // code that follows is totally generic!
103 const auto *monster = BuildMonster(fbb);
104
105 std::string expected = R"(MyGame.Example.Monster{
106 pos = MyGame.Example.Vec3{
107 x = 1.1
108 y = 2.2
109 z = 3.3
110 test1 = 6.6
111 test2 = 2
112 test3 = MyGame.Example.Test{
113 a = 11
114 b = 90
115 }
116 }
117 mana = 1
118 hp = 2
119 name = "my_monster"
120 inventory = [
121 4,
122 5,
123 6,
124 7
125 ]
126 color = 8
127 test_type = 0
128 testbool = 1
129 testhashs32_fnv1 = 4
130 testhashu32_fnv1 = 5
131 testhashs64_fnv1 = 6
132 testhashu64_fnv1 = 7
133 testhashs32_fnv1a = 8
134 testhashu32_fnv1a = 9
135 testhashs64_fnv1a = 10
136 testhashu64_fnv1a = 11
137 testf = 12.1
138 testf2 = 13.1
139 testf3 = 14.1
140 single_weak_reference = 15
141 co_owning_reference = 16
142 non_owning_reference = 17
143 any_unique_type = 0
144 any_ambiguous_type = 0
145 signed_enum = -1
146 })";
147
148 // Call a generic function that has no specific knowledge of the flatbuffer we
149 // are passing in; it should use only static reflection to produce a string
150 // representations of the field names and values recursively. We give it an
151 // initial indentation so that the result can be compared with our raw string
152 // above, which we wanted to indent so that it will look nicer in this code.
153 //
154 // A note about JSON: as can be seen from the string above, this produces a
155 // JSON-like notation, but we are not using any of Flatbuffers' JSON infra to
156 // produce this! It is produced entirely using compile-time reflection, and
157 // thus does not require any runtime access to the *.fbs definition files!
158 std::optional<std::string> result =
159 cpp17::StringifyFlatbufferValue(*monster, /*indent=*/" ");
160
161 TEST_ASSERT(result.has_value());
162 TEST_EQ_STR(expected.c_str(), result->c_str());
163 }
164
165 /*******************************************************************************
166 ** Test Traits::FieldType
167 *******************************************************************************/
168 using pos_type = Monster::Traits::FieldType<0>;
169 static_assert(std::is_same_v<pos_type, const Vec3*>);
170
171 using mana_type = Monster::Traits::FieldType<1>;
172 static_assert(std::is_same_v<mana_type, int16_t>);
173
174 using name_type = Monster::Traits::FieldType<3>;
175 static_assert(std::is_same_v<name_type, const flatbuffers::String*>);
176
177 /*******************************************************************************
178 ** Generic Create Function Test.
179 *******************************************************************************/
CreateTableByTypeTest()180 void CreateTableByTypeTest() {
181 flatbuffers::FlatBufferBuilder builder;
182
183 // We will create an object of this type using only the type.
184 using type_to_create_t = cpp17::MyGame::Example::Stat;
185
186 [&builder] {
187 auto id_str = builder.CreateString("my_id");
188 auto table = type_to_create_t::Traits::Create(builder, id_str, 42, 7);
189 // Be sure that the correct return type was inferred.
190 static_assert(
191 std::is_same_v<decltype(table), flatbuffers::Offset<type_to_create_t>>);
192 builder.Finish(table);
193 }();
194
195 // Access it.
196 auto stat =
197 flatbuffers::GetRoot<type_to_create_t>(builder.GetBufferPointer());
198 TEST_EQ_STR(stat->id()->c_str(), "my_id");
199 TEST_EQ(stat->val(), 42);
200 TEST_EQ(stat->count(), 7);
201 }
202
OptionalScalarsTest()203 void OptionalScalarsTest() {
204 static_assert(
205 std::is_same<flatbuffers::Optional<float>, std::optional<float>>::value);
206 static_assert(std::is_same<flatbuffers::nullopt_t, std::nullopt_t>::value);
207
208 // test C++ nullable
209 flatbuffers::FlatBufferBuilder fbb;
210 FinishScalarStuffBuffer(fbb, cpp17::optional_scalars::CreateScalarStuff(
211 fbb, 1, static_cast<int8_t>(2)));
212 auto opts =
213 cpp17::optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer());
214 TEST_ASSERT(!opts->maybe_bool());
215 TEST_ASSERT(!opts->maybe_f32().has_value());
216 TEST_ASSERT(opts->maybe_i8().has_value());
217 TEST_EQ(opts->maybe_i8().value(), 2);
218 TEST_ASSERT(opts->mutate_maybe_i8(3));
219 TEST_ASSERT(opts->maybe_i8().has_value());
220 TEST_EQ(opts->maybe_i8().value(), 3);
221 TEST_ASSERT(!opts->mutate_maybe_i16(-10));
222
223 cpp17::optional_scalars::ScalarStuffT obj;
224 opts->UnPackTo(&obj);
225 TEST_ASSERT(!obj.maybe_bool);
226 TEST_ASSERT(!obj.maybe_f32.has_value());
227 TEST_ASSERT(obj.maybe_i8.has_value() && obj.maybe_i8.value() == 3);
228 TEST_ASSERT(obj.maybe_i8 && *obj.maybe_i8 == 3);
229 obj.maybe_i32 = -1;
230
231 fbb.Clear();
232 FinishScalarStuffBuffer(
233 fbb, cpp17::optional_scalars::ScalarStuff::Pack(fbb, &obj));
234 opts = cpp17::optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer());
235 TEST_ASSERT(opts->maybe_i8().has_value());
236 TEST_EQ(opts->maybe_i8().value(), 3);
237 TEST_ASSERT(opts->maybe_i32().has_value());
238 TEST_EQ(opts->maybe_i32().value(), -1);
239
240 TEST_EQ(std::optional<int32_t>(opts->maybe_i32()).value(), -1);
241 TEST_EQ(std::optional<int64_t>(opts->maybe_i32()).value(), -1);
242 TEST_ASSERT(opts->maybe_i32() == std::optional<int64_t>(-1));
243 }
244
FlatBufferCpp17Tests()245 int FlatBufferCpp17Tests() {
246 CreateTableByTypeTest();
247 OptionalScalarsTest();
248 StringifyAnyFlatbuffersTypeTest();
249 return 0;
250 }
251
main(int,const char * [])252 int main(int /*argc*/, const char * /*argv*/[]) {
253 InitTestEngine();
254
255 FlatBufferCpp17Tests();
256
257 if (!testing_fails) {
258 TEST_OUTPUT_LINE("C++17: ALL TESTS PASSED");
259 } else {
260 TEST_OUTPUT_LINE("C++17: %d FAILED TESTS", testing_fails);
261 }
262 return CloseTestEngine();
263 }
264