• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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