Skip to content

Commit cb93a67

Browse files
committed
Default Vector Support C++
1 parent 8cb53cc commit cb93a67

17 files changed

+502
-3963
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,7 @@ kotlin/**/generated
156156
MODULE.bazel.lock
157157

158158
# Ignore the generated docs
159-
docs/site
159+
docs/site
160+
161+
# Ignore generated files
162+
*.fbs.h

CMakeLists.txt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ set(FlatHash_SRCS
218218
set(FlatBuffers_Tests_SRCS
219219
${FlatBuffers_Library_SRCS}
220220
src/idl_gen_fbs.cpp
221+
tests/default_vectors_strings_test.cpp
222+
tests/default_vectors_strings_test.h
221223
tests/evolution_test.cpp
222224
tests/flexbuffers_test.cpp
223225
tests/fuzz_test.cpp
@@ -496,28 +498,34 @@ if(FLATBUFFERS_BUILD_SHAREDLIB)
496498
endif()
497499
endif()
498500

499-
function(compile_schema SRC_FBS OPT OUT_GEN_FILE)
501+
function(compile_schema SRC_FBS OPT SUFFIX OUT_GEN_FILE)
500502
get_filename_component(SRC_FBS_DIR ${SRC_FBS} PATH)
501-
string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS})
503+
string(REGEX REPLACE "\\.fbs$" "${SUFFIX}.h" GEN_HEADER ${SRC_FBS})
502504
add_custom_command(
503505
OUTPUT ${GEN_HEADER}
504506
COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}"
505507
${OPT}
508+
--filename-suffix ${SUFFIX}
506509
-o "${SRC_FBS_DIR}"
507510
"${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
508511
DEPENDS flatc ${SRC_FBS}
509512
COMMENT "flatc generation: `${SRC_FBS}` -> `${GEN_HEADER}`"
510-
)
513+
)
511514
set(${OUT_GEN_FILE} ${GEN_HEADER} PARENT_SCOPE)
512515
endfunction()
513516

514517
function(compile_schema_for_test SRC_FBS OPT)
515-
compile_schema("${SRC_FBS}" "${OPT}" GEN_FILE)
518+
compile_schema("${SRC_FBS}" "${OPT}" "_generated" GEN_FILE)
519+
target_sources(flattests PRIVATE ${GEN_FILE})
520+
endfunction()
521+
522+
function(compile_schema_for_test_fbsh SRC_FBS OPT)
523+
compile_schema("${SRC_FBS}" "${OPT}" ".fbs" GEN_FILE)
516524
target_sources(flattests PRIVATE ${GEN_FILE})
517525
endfunction()
518526

519527
function(compile_schema_for_samples SRC_FBS OPT)
520-
compile_schema("${SRC_FBS}" "${OPT}" GEN_FILE)
528+
compile_schema("${SRC_FBS}" "${OPT}" "_generated" GEN_FILE)
521529
target_sources(flatsample PRIVATE ${GEN_FILE})
522530
endfunction()
523531

@@ -542,6 +550,7 @@ if(FLATBUFFERS_BUILD_TESTS)
542550
SET(FLATC_OPT_SCOPED_ENUMS ${FLATC_OPT_COMP};--scoped-enums)
543551

544552
compile_schema_for_test(tests/alignment_test.fbs "${FLATC_OPT_COMP}")
553+
compile_schema_for_test_fbsh(tests/default_vectors_strings_test.fbs "${FLATC_OPT_COMP}")
545554
compile_schema_for_test(tests/arrays_test.fbs "${FLATC_OPT_SCOPED_ENUMS}")
546555
compile_schema_for_test(tests/native_inline_table_test.fbs "${FLATC_OPT_COMP}")
547556
compile_schema_for_test(tests/native_type_test.fbs "${FLATC_OPT_COMP}")

include/flatbuffers/table.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define FLATBUFFERS_TABLE_H_
1919

2020
#include "flatbuffers/base.h"
21+
#include "flatbuffers/vector.h"
2122
#include "flatbuffers/verifier.h"
2223

2324
namespace flatbuffers {
@@ -70,6 +71,32 @@ class Table {
7071
return GetPointer<P, uoffset64_t>(field);
7172
}
7273

74+
template <typename P, typename SizeT = uoffset_t,
75+
typename OffsetSize = uoffset_t>
76+
const Vector<P, SizeT>* GetVectorPointerOrEmpty(voffset_t field) const {
77+
auto* ptr = GetPointer<const Vector<P, SizeT>*, OffsetSize>(field);
78+
return ptr ? ptr : EmptyVector<P, SizeT>();
79+
}
80+
81+
template <typename P, typename SizeT = uoffset_t>
82+
const Vector<P, SizeT>* GetVectorPointer64OrEmpty(voffset_t field) const {
83+
return GetVectorPointerOrEmpty<P, SizeT, uoffset64_t>(field);
84+
}
85+
86+
template <typename P, typename SizeT = uoffset_t,
87+
typename OffsetSize = uoffset_t>
88+
Vector<P, SizeT>* GetMutableVectorPointerOrEmpty(voffset_t field) {
89+
auto* ptr = GetPointer<Vector<P, SizeT>*, OffsetSize>(field);
90+
// This is a const_cast, but safe, since all mutable operations on an
91+
// empty vector are NOPs.
92+
return ptr ? ptr : const_cast<Vector<P, SizeT>*>(EmptyVector<P, SizeT>());
93+
}
94+
95+
template <typename P, typename SizeT = uoffset_t>
96+
Vector<P, SizeT>* GetMutableVectorPointer64OrEmpty(voffset_t field) {
97+
return GetMutableVectorPointerOrEmpty<P, SizeT, uoffset64_t>(field);
98+
}
99+
73100
template <typename P>
74101
P GetStruct(voffset_t field) const {
75102
auto field_offset = GetOptionalFieldOffset(field);
@@ -177,6 +204,39 @@ class Table {
177204
return VerifyOffsetRequired<uoffset64_t>(verifier, field);
178205
}
179206

207+
// Verify a string that may have a default value.
208+
template <typename OffsetT = uoffset_t>
209+
bool VerifyStringWithDefault(const Verifier& verifier,
210+
voffset_t field) const {
211+
auto field_offset = GetOptionalFieldOffset(field);
212+
return field_offset == 0 ||
213+
verifier.VerifyString(GetPointer<const String*, OffsetT>(field));
214+
}
215+
216+
// Verify a vector that has a default empty value.
217+
template <typename P, typename SizeT = uoffset_t,
218+
typename OffsetSize = uoffset_t>
219+
bool VerifyVectorWithDefault(const Verifier& verifier,
220+
voffset_t field) const {
221+
auto field_offset = GetOptionalFieldOffset(field);
222+
return field_offset == 0 ||
223+
verifier.VerifyVector(
224+
GetPointer<const Vector<P, SizeT>*, OffsetSize>(field));
225+
}
226+
227+
template <typename P, typename SizeT = uoffset_t>
228+
bool VerifyVector64WithDefault(const Verifier& verifier,
229+
voffset_t field) const {
230+
return VerifyVectorWithDefault<P, SizeT, uoffset64_t>(verifier, field);
231+
}
232+
233+
protected:
234+
template <typename T, typename SizeT = uoffset_t>
235+
static const Vector<T, SizeT>* EmptyVector() {
236+
static const SizeT empty_vector_length = 0;
237+
return reinterpret_cast<const Vector<T, SizeT>*>(&empty_vector_length);
238+
}
239+
180240
private:
181241
// private constructor & copy constructor: you obtain instances of this
182242
// class by pointing to existing data only

scripts/generate_code.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,10 @@ def glob(path, pattern):
400400
schema="nested_union_test.fbs",
401401
)
402402

403+
flatc(
404+
NO_INCL_OPTS + CPP_OPTS,
405+
schema="default_vectors_strings_test.fbs",
406+
)
403407

404408
# Optional Scalars
405409
optional_scalars_schema = "optional_scalars.fbs"

src/idl_gen_cpp.cpp

Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,6 +1924,10 @@ class CppGenerator : public BaseGenerator {
19241924
} else {
19251925
return "0";
19261926
}
1927+
} else if (IsVector(type) && field.value.constant == "[]") {
1928+
return "0";
1929+
} else if (IsString(type) && field.value.constant != "0") {
1930+
return "0";
19271931
} else if (IsStruct(type) && (field.value.constant == "0")) {
19281932
return "nullptr";
19291933
} else {
@@ -2427,12 +2431,40 @@ class CppGenerator : public BaseGenerator {
24272431
break;
24282432
}
24292433
case BASE_TYPE_STRING: {
2430-
code_ += "{{PRE}}verifier.VerifyString({{NAME}}())\\";
2434+
if (field.value.constant != "0") {
2435+
if (field.offset64) {
2436+
code_ +=
2437+
"{{PRE}}VerifyStringWithDefault<::flatbuffers::uoffset64_t>("
2438+
"verifier, "
2439+
"{{OFFSET}})\\";
2440+
} else {
2441+
code_ += "{{PRE}}VerifyStringWithDefault(verifier, {{OFFSET}})\\";
2442+
}
2443+
} else {
2444+
code_ += "{{PRE}}verifier.VerifyString({{NAME}}())\\";
2445+
}
24312446
break;
24322447
}
24332448
case BASE_TYPE_VECTOR64:
24342449
case BASE_TYPE_VECTOR: {
2435-
code_ += "{{PRE}}verifier.VerifyVector({{NAME}}())\\";
2450+
if (field.value.constant == "[]") {
2451+
const auto& vec_type = field.value.type.VectorType();
2452+
const std::string vtype_wire = GenTypeWire(
2453+
vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
2454+
std::string verify_call;
2455+
if (field.offset64) {
2456+
verify_call = "{{PRE}}VerifyVector64WithDefault<" + vtype_wire;
2457+
} else {
2458+
verify_call = "{{PRE}}VerifyVectorWithDefault<" + vtype_wire;
2459+
}
2460+
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
2461+
verify_call += ", ::flatbuffers::uoffset64_t";
2462+
}
2463+
verify_call += ">(verifier, {{OFFSET}})\\";
2464+
code_ += verify_call;
2465+
} else {
2466+
code_ += "{{PRE}}verifier.VerifyVector({{NAME}}())\\";
2467+
}
24362468

24372469
switch (field.value.type.element) {
24382470
case BASE_TYPE_STRING: {
@@ -2723,7 +2755,37 @@ class CppGenerator : public BaseGenerator {
27232755
code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, call));
27242756
code_.SetValue("NULLABLE_EXT", NullableExtension());
27252757
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
2726-
code_ += " return {{FIELD_VALUE}};";
2758+
if (IsVector(type) && field.value.constant == "[]") {
2759+
const auto& vec_type = type.VectorType();
2760+
const std::string vtype_wire = GenTypeWire(
2761+
vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
2762+
std::string get_call;
2763+
if (field.offset64) {
2764+
get_call = " return GetVectorPointer64OrEmpty<" + vtype_wire;
2765+
} else {
2766+
get_call = " return GetVectorPointerOrEmpty<" + vtype_wire;
2767+
}
2768+
if (type.base_type == BASE_TYPE_VECTOR64) {
2769+
get_call += ", ::flatbuffers::uoffset64_t";
2770+
}
2771+
get_call += ">(" + offset_str + ");";
2772+
code_ += get_call;
2773+
} else if (IsString(type) && field.value.constant != "0") {
2774+
// TODO: Add logic to always convert the string to a valid C++ string
2775+
// literal by handling string escapes.
2776+
code_ += " auto* ptr = {{FIELD_VALUE}};";
2777+
code_ += " if (ptr) return ptr;";
2778+
code_ += " static const struct { uint32_t len; const char s[" +
2779+
NumToString(field.value.constant.length() + 1) +
2780+
"]; } bfbs_string = { " +
2781+
NumToString(field.value.constant.length()) + ", \"" +
2782+
field.value.constant + "\" };";
2783+
code_ +=
2784+
" return reinterpret_cast<const ::flatbuffers::String "
2785+
" *>(&bfbs_string);";
2786+
} else {
2787+
code_ += " return {{FIELD_VALUE}};";
2788+
}
27272789
code_ += " }";
27282790
} else {
27292791
auto wire_type = GenTypeBasic(type, false);
@@ -2910,22 +2972,43 @@ class CppGenerator : public BaseGenerator {
29102972
} else {
29112973
auto postptr = " *" + NullableExtension();
29122974
auto wire_type = GenTypeGet(type, " ", "", postptr.c_str(), true);
2913-
const std::string accessor = [&]() {
2914-
if (IsStruct(type)) {
2915-
return "GetStruct<";
2916-
}
2917-
if (field.offset64) {
2918-
return "GetPointer64<";
2919-
}
2920-
return "GetPointer<";
2921-
}();
2922-
auto underlying = accessor + wire_type + ">(" + offset_str + ")";
29232975
code_.SetValue("FIELD_TYPE", wire_type);
2924-
code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, underlying));
29252976

2926-
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
2927-
code_ += " return {{FIELD_VALUE}};";
2928-
code_ += " }";
2977+
if (IsVector(type) && field.value.constant == "[]") {
2978+
const auto& vec_type = type.VectorType();
2979+
const std::string vtype_wire = GenTypeWire(
2980+
vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
2981+
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
2982+
std::string get_call;
2983+
if (field.offset64) {
2984+
get_call =
2985+
" return GetMutableVectorPointer64OrEmpty<" + vtype_wire;
2986+
} else {
2987+
get_call = " return GetMutableVectorPointerOrEmpty<" + vtype_wire;
2988+
}
2989+
if (type.base_type == BASE_TYPE_VECTOR64) {
2990+
get_call += ", ::flatbuffers::uoffset64_t";
2991+
}
2992+
get_call += ">(" + offset_str + ");";
2993+
code_ += get_call;
2994+
code_ += " }";
2995+
} else {
2996+
const std::string accessor = [&]() {
2997+
if (IsStruct(type)) {
2998+
return "GetStruct<";
2999+
}
3000+
if (field.offset64) {
3001+
return "GetPointer64<";
3002+
}
3003+
return "GetPointer<";
3004+
}();
3005+
auto underlying = accessor + wire_type + ">(" + offset_str + ")";
3006+
code_.SetValue("FIELD_VALUE",
3007+
GenUnderlyingCast(field, true, underlying));
3008+
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
3009+
code_ += " return {{FIELD_VALUE}};";
3010+
code_ += " }";
3011+
}
29293012
}
29303013
}
29313014

@@ -3305,9 +3388,17 @@ class CppGenerator : public BaseGenerator {
33053388
} else {
33063389
code_.SetValue("CREATE_STRING", "CreateSharedString");
33073390
}
3308-
code_ +=
3309-
" auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
3310-
"_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;";
3391+
if (field->value.constant != "0") {
3392+
code_ +=
3393+
" auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
3394+
"_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : "
3395+
"_fbb.{{CREATE_STRING}}(\"" +
3396+
field->value.constant + "\");";
3397+
} else {
3398+
code_ +=
3399+
" auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
3400+
"_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;";
3401+
}
33113402
} else if (IsVector(field->value.type)) {
33123403
const std::string force_align_code =
33133404
GenVectorForceAlign(*field, Name(*field) + "->size()");

src/idl_parser.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2772,7 +2772,8 @@ bool Parser::SupportsOptionalScalars() const {
27722772

27732773
bool Parser::SupportsDefaultVectorsAndStrings() const {
27742774
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
2775-
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim;
2775+
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim |
2776+
IDLOptions::kCpp | IDLOptions::kBinary | IDLOptions::kJson;
27762777
return !(opts.lang_to_generate & ~supported_langs);
27772778
}
27782779

0 commit comments

Comments
 (0)