diff --git a/.gitignore b/.gitignore
index 0296f8fac..f27c9b41b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -156,4 +156,7 @@ kotlin/**/generated
MODULE.bazel.lock
# Ignore the generated docs
-docs/site
\ No newline at end of file
+docs/site
+
+# Ignore generated files
+*.fbs.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e39c49cf..7bf48e6ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -218,6 +218,8 @@ set(FlatHash_SRCS
set(FlatBuffers_Tests_SRCS
${FlatBuffers_Library_SRCS}
src/idl_gen_fbs.cpp
+ tests/default_vectors_strings_test.cpp
+ tests/default_vectors_strings_test.h
tests/evolution_test.cpp
tests/flexbuffers_test.cpp
tests/fuzz_test.cpp
@@ -496,28 +498,34 @@ if(FLATBUFFERS_BUILD_SHAREDLIB)
endif()
endif()
-function(compile_schema SRC_FBS OPT OUT_GEN_FILE)
+function(compile_schema SRC_FBS OPT SUFFIX OUT_GEN_FILE)
get_filename_component(SRC_FBS_DIR ${SRC_FBS} PATH)
- string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS})
+ string(REGEX REPLACE "\\.fbs$" "${SUFFIX}.h" GEN_HEADER ${SRC_FBS})
add_custom_command(
OUTPUT ${GEN_HEADER}
COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}"
${OPT}
+ --filename-suffix ${SUFFIX}
-o "${SRC_FBS_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
DEPENDS flatc ${SRC_FBS}
COMMENT "flatc generation: `${SRC_FBS}` -> `${GEN_HEADER}`"
- )
+ )
set(${OUT_GEN_FILE} ${GEN_HEADER} PARENT_SCOPE)
endfunction()
function(compile_schema_for_test SRC_FBS OPT)
- compile_schema("${SRC_FBS}" "${OPT}" GEN_FILE)
+ compile_schema("${SRC_FBS}" "${OPT}" "_generated" GEN_FILE)
+ target_sources(flattests PRIVATE ${GEN_FILE})
+endfunction()
+
+function(compile_schema_for_test_fbsh SRC_FBS OPT)
+ compile_schema("${SRC_FBS}" "${OPT}" ".fbs" GEN_FILE)
target_sources(flattests PRIVATE ${GEN_FILE})
endfunction()
function(compile_schema_for_samples SRC_FBS OPT)
- compile_schema("${SRC_FBS}" "${OPT}" GEN_FILE)
+ compile_schema("${SRC_FBS}" "${OPT}" "_generated" GEN_FILE)
target_sources(flatsample PRIVATE ${GEN_FILE})
endfunction()
@@ -542,6 +550,7 @@ if(FLATBUFFERS_BUILD_TESTS)
SET(FLATC_OPT_SCOPED_ENUMS ${FLATC_OPT_COMP};--scoped-enums)
compile_schema_for_test(tests/alignment_test.fbs "${FLATC_OPT_COMP}")
+ compile_schema_for_test_fbsh(tests/default_vectors_strings_test.fbs "${FLATC_OPT_COMP}")
compile_schema_for_test(tests/arrays_test.fbs "${FLATC_OPT_SCOPED_ENUMS}")
compile_schema_for_test(tests/native_inline_table_test.fbs "${FLATC_OPT_COMP}")
compile_schema_for_test(tests/native_type_test.fbs "${FLATC_OPT_COMP}")
diff --git a/build_defs.bzl b/build_defs.bzl
index 51d3ce20f..b22fdfef0 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -187,6 +187,7 @@ def flatbuffer_cc_library(
visibility = None,
compatible_with = None,
restricted_to = None,
+ filename_suffix = "_generated",
target_compatible_with = None,
srcs_filegroup_visibility = None,
gen_reflections = False):
@@ -230,10 +231,13 @@ def flatbuffer_cc_library(
Fileset([name]_reflection): (Optional) all generated reflection binaries.
cc_library([name]): library with sources and flatbuffers deps.
"""
- output_headers = [
- (out_prefix + "%s_generated.h") % (s.replace(".fbs", "").split("/")[-1].split(":")[-1])
- for s in srcs
- ]
+
+ output_headers = []
+ for s in srcs:
+ base_name = s.split("/")[-1].split(":")[-1].replace(".fbs", "")
+ header = out_prefix + base_name + filename_suffix + ".h"
+ output_headers.append(header)
+
if deps and includes:
# There is no inherent reason we couldn't support both, but this discourages
# use of includes without good reason.
diff --git a/include/flatbuffers/table.h b/include/flatbuffers/table.h
index 0149216ff..622d13408 100644
--- a/include/flatbuffers/table.h
+++ b/include/flatbuffers/table.h
@@ -18,6 +18,7 @@
#define FLATBUFFERS_TABLE_H_
#include "flatbuffers/base.h"
+#include "flatbuffers/vector.h"
#include "flatbuffers/verifier.h"
namespace flatbuffers {
@@ -70,6 +71,32 @@ class Table {
return GetPointer
(field);
}
+ template
+ const Vector* GetVectorPointerOrEmpty(voffset_t field) const {
+ auto* ptr = GetPointer*, OffsetSize>(field);
+ return ptr ? ptr : EmptyVector();
+ }
+
+ template
+ const Vector* GetVectorPointer64OrEmpty(voffset_t field) const {
+ return GetVectorPointerOrEmpty
(field);
+ }
+
+ template
+ Vector* GetMutableVectorPointerOrEmpty(voffset_t field) {
+ auto* ptr = GetPointer*, OffsetSize>(field);
+ // This is a const_cast, but safe, since all mutable operations on an
+ // empty vector are NOPs.
+ return ptr ? ptr : const_cast*>(EmptyVector());
+ }
+
+ template
+ Vector* GetMutableVectorPointer64OrEmpty(voffset_t field) {
+ return GetMutableVectorPointerOrEmpty
(field);
+ }
+
template
P GetStruct(voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field);
@@ -177,6 +204,39 @@ class Table {
return VerifyOffsetRequired(verifier, field);
}
+ // Verify a string that may have a default value.
+ template
+ bool VerifyStringWithDefault(const Verifier& verifier,
+ voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return field_offset == 0 ||
+ verifier.VerifyString(GetPointer(field));
+ }
+
+ // Verify a vector that has a default empty value.
+ template
+ bool VerifyVectorWithDefault(const Verifier& verifier,
+ voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return field_offset == 0 ||
+ verifier.VerifyVector(
+ GetPointer*, OffsetSize>(field));
+ }
+
+ template
+ bool VerifyVector64WithDefault(const Verifier& verifier,
+ voffset_t field) const {
+ return VerifyVectorWithDefault(verifier, field);
+ }
+
+ protected:
+ template
+ static const Vector* EmptyVector() {
+ static const SizeT empty_vector_length = 0;
+ return reinterpret_cast*>(&empty_vector_length);
+ }
+
private:
// private constructor & copy constructor: you obtain instances of this
// class by pointing to existing data only
diff --git a/scripts/generate_code.py b/scripts/generate_code.py
index 09ebf88de..c89403efa 100755
--- a/scripts/generate_code.py
+++ b/scripts/generate_code.py
@@ -400,6 +400,10 @@ flatc(
schema="nested_union_test.fbs",
)
+flatc(
+ NO_INCL_OPTS + CPP_OPTS,
+ schema="default_vectors_strings_test.fbs",
+)
# Optional Scalars
optional_scalars_schema = "optional_scalars.fbs"
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index 54706b037..f6e61ff2a 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -1924,6 +1924,10 @@ class CppGenerator : public BaseGenerator {
} else {
return "0";
}
+ } else if (IsVector(type) && field.value.constant == "[]") {
+ return "0";
+ } else if (IsString(type) && field.value.constant != "0") {
+ return "0";
} else if (IsStruct(type) && (field.value.constant == "0")) {
return "nullptr";
} else {
@@ -2427,12 +2431,40 @@ class CppGenerator : public BaseGenerator {
break;
}
case BASE_TYPE_STRING: {
- code_ += "{{PRE}}verifier.VerifyString({{NAME}}())\\";
+ if (field.value.constant != "0") {
+ if (field.offset64) {
+ code_ +=
+ "{{PRE}}VerifyStringWithDefault<::flatbuffers::uoffset64_t>("
+ "verifier, "
+ "{{OFFSET}})\\";
+ } else {
+ code_ += "{{PRE}}VerifyStringWithDefault(verifier, {{OFFSET}})\\";
+ }
+ } else {
+ code_ += "{{PRE}}verifier.VerifyString({{NAME}}())\\";
+ }
break;
}
case BASE_TYPE_VECTOR64:
case BASE_TYPE_VECTOR: {
- code_ += "{{PRE}}verifier.VerifyVector({{NAME}}())\\";
+ if (field.value.constant == "[]") {
+ const auto& vec_type = field.value.type.VectorType();
+ const std::string vtype_wire = GenTypeWire(
+ vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
+ std::string verify_call;
+ if (field.offset64) {
+ verify_call = "{{PRE}}VerifyVector64WithDefault<" + vtype_wire;
+ } else {
+ verify_call = "{{PRE}}VerifyVectorWithDefault<" + vtype_wire;
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
+ verify_call += ", ::flatbuffers::uoffset64_t";
+ }
+ verify_call += ">(verifier, {{OFFSET}})\\";
+ code_ += verify_call;
+ } else {
+ code_ += "{{PRE}}verifier.VerifyVector({{NAME}}())\\";
+ }
switch (field.value.type.element) {
case BASE_TYPE_STRING: {
@@ -2723,7 +2755,37 @@ class CppGenerator : public BaseGenerator {
code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, call));
code_.SetValue("NULLABLE_EXT", NullableExtension());
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
- code_ += " return {{FIELD_VALUE}};";
+ if (IsVector(type) && field.value.constant == "[]") {
+ const auto& vec_type = type.VectorType();
+ const std::string vtype_wire = GenTypeWire(
+ vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
+ std::string get_call;
+ if (field.offset64) {
+ get_call = " return GetVectorPointer64OrEmpty<" + vtype_wire;
+ } else {
+ get_call = " return GetVectorPointerOrEmpty<" + vtype_wire;
+ }
+ if (type.base_type == BASE_TYPE_VECTOR64) {
+ get_call += ", ::flatbuffers::uoffset64_t";
+ }
+ get_call += ">(" + offset_str + ");";
+ code_ += get_call;
+ } else if (IsString(type) && field.value.constant != "0") {
+ // TODO: Add logic to always convert the string to a valid C++ string
+ // literal by handling string escapes.
+ code_ += " auto* ptr = {{FIELD_VALUE}};";
+ code_ += " if (ptr) return ptr;";
+ code_ += " static const struct { uint32_t len; const char s[" +
+ NumToString(field.value.constant.length() + 1) +
+ "]; } bfbs_string = { " +
+ NumToString(field.value.constant.length()) + ", \"" +
+ field.value.constant + "\" };";
+ code_ +=
+ " return reinterpret_cast(&bfbs_string);";
+ } else {
+ code_ += " return {{FIELD_VALUE}};";
+ }
code_ += " }";
} else {
auto wire_type = GenTypeBasic(type, false);
@@ -2910,22 +2972,43 @@ class CppGenerator : public BaseGenerator {
} else {
auto postptr = " *" + NullableExtension();
auto wire_type = GenTypeGet(type, " ", "", postptr.c_str(), true);
- const std::string accessor = [&]() {
- if (IsStruct(type)) {
- return "GetStruct<";
- }
- if (field.offset64) {
- return "GetPointer64<";
- }
- return "GetPointer<";
- }();
- auto underlying = accessor + wire_type + ">(" + offset_str + ")";
code_.SetValue("FIELD_TYPE", wire_type);
- code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, underlying));
- code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
- code_ += " return {{FIELD_VALUE}};";
- code_ += " }";
+ if (IsVector(type) && field.value.constant == "[]") {
+ const auto& vec_type = type.VectorType();
+ const std::string vtype_wire = GenTypeWire(
+ vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
+ code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
+ std::string get_call;
+ if (field.offset64) {
+ get_call =
+ " return GetMutableVectorPointer64OrEmpty<" + vtype_wire;
+ } else {
+ get_call = " return GetMutableVectorPointerOrEmpty<" + vtype_wire;
+ }
+ if (type.base_type == BASE_TYPE_VECTOR64) {
+ get_call += ", ::flatbuffers::uoffset64_t";
+ }
+ get_call += ">(" + offset_str + ");";
+ code_ += get_call;
+ code_ += " }";
+ } else {
+ const std::string accessor = [&]() {
+ if (IsStruct(type)) {
+ return "GetStruct<";
+ }
+ if (field.offset64) {
+ return "GetPointer64<";
+ }
+ return "GetPointer<";
+ }();
+ auto underlying = accessor + wire_type + ">(" + offset_str + ")";
+ code_.SetValue("FIELD_VALUE",
+ GenUnderlyingCast(field, true, underlying));
+ code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
+ code_ += " return {{FIELD_VALUE}};";
+ code_ += " }";
+ }
}
}
@@ -3305,9 +3388,17 @@ class CppGenerator : public BaseGenerator {
} else {
code_.SetValue("CREATE_STRING", "CreateSharedString");
}
- code_ +=
- " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
- "_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;";
+ if (field->value.constant != "0") {
+ code_ +=
+ " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
+ "_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : "
+ "_fbb.{{CREATE_STRING}}(\"" +
+ field->value.constant + "\");";
+ } else {
+ code_ +=
+ " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
+ "_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;";
+ }
} else if (IsVector(field->value.type)) {
const std::string force_align_code =
GenVectorForceAlign(*field, Name(*field) + "->size()");
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index e01a6525d..0aaccbb2c 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -2772,7 +2772,8 @@ bool Parser::SupportsOptionalScalars() const {
bool Parser::SupportsDefaultVectorsAndStrings() const {
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
- IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim;
+ IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim |
+ IDLOptions::kCpp | IDLOptions::kBinary | IDLOptions::kJson;
return !(opts.lang_to_generate & ~supported_langs);
}
diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel
index d95eac83c..6a7373aaa 100644
--- a/tests/BUILD.bazel
+++ b/tests/BUILD.bazel
@@ -33,6 +33,8 @@ cc_test(
"alignment_test.cpp",
"alignment_test.h",
"alignment_test_generated.h",
+ "default_vectors_strings_test.cpp",
+ "default_vectors_strings_test.h",
"evolution_test.cpp",
"evolution_test.h",
"evolution_test/evolution_v1_generated.h",
@@ -134,6 +136,7 @@ cc_test(
deps = [
":alignment_test_cc_fbs",
":arrays_test_cc_fbs",
+ ":default_vectors_strings_test_cc_fbs",
":monster_extra_cc_fbs",
":monster_test_cc_fbs",
":native_type_test_cc_fbs",
@@ -272,3 +275,16 @@ flatbuffer_cc_library(
name = "alignment_test_cc_fbs",
srcs = ["alignment_test.fbs"],
)
+
+flatbuffer_cc_library(
+ name = "default_vectors_strings_test_cc_fbs",
+ srcs = ["default_vectors_strings_test.fbs"],
+ flatc_args = [
+ "--gen-compare",
+ "--gen-mutable",
+ "--gen-object-api",
+ "--reflect-names",
+ "--filename-suffix .fbs",
+ ],
+ filename_suffix = ".fbs"
+)
diff --git a/tests/default_vectors_strings_test.cpp b/tests/default_vectors_strings_test.cpp
new file mode 100644
index 000000000..ed9c80afe
--- /dev/null
+++ b/tests/default_vectors_strings_test.cpp
@@ -0,0 +1,249 @@
+#include "tests/default_vectors_strings_test.h"
+
+#include
+#include
+
+#include "include/flatbuffers/buffer.h"
+#include "include/flatbuffers/flatbuffer_builder.h"
+#include "include/flatbuffers/string.h"
+#include "include/flatbuffers/vector.h"
+#include "include/flatbuffers/verifier.h"
+#include "tests/default_vectors_strings_test.fbs.h"
+#include "tests/test_assert.h"
+
+namespace flatbuffers {
+namespace tests {
+
+using flatbuffers::FlatBufferBuilder64;
+using flatbuffers::Offset;
+using flatbuffers::String;
+using flatbuffers::Verifier;
+
+void DefaultVectorsStringsTest_EmptyOnDefault_Const() {
+ FlatBufferBuilder64 builder;
+
+ // Create table without providing the fields with defaults.
+ DefaultVectorsStringsTest::TableWithDefaultVectorsBuilder tbl_builder(
+ builder);
+ tbl_builder.add_regular_int(100);
+ auto offset = tbl_builder.Finish();
+ builder.Finish(offset);
+
+ Verifier verifier(builder.GetBufferPointer(), builder.GetSize());
+ TEST_ASSERT(
+ DefaultVectorsStringsTest::VerifyTableWithDefaultVectorsBuffer(verifier));
+
+ const auto* table = DefaultVectorsStringsTest::GetTableWithDefaultVectors(
+ builder.GetBufferPointer());
+ TEST_NOTNULL(table);
+
+ // Verify default scalar vectors.
+ TEST_NOTNULL(table->int_vec());
+ TEST_EQ(table->int_vec()->size(), 0);
+ TEST_NOTNULL(table->bool_vec());
+ TEST_EQ(table->bool_vec()->size(), 0);
+ TEST_NOTNULL(table->char_vec());
+ TEST_EQ(table->char_vec()->size(), 0);
+ TEST_NOTNULL(table->uchar_vec());
+ TEST_EQ(table->uchar_vec()->size(), 0);
+ TEST_NOTNULL(table->short_vec());
+ TEST_EQ(table->short_vec()->size(), 0);
+ TEST_NOTNULL(table->ushort_vec());
+ TEST_EQ(table->ushort_vec()->size(), 0);
+ TEST_NOTNULL(table->uint_vec());
+ TEST_EQ(table->uint_vec()->size(), 0);
+ TEST_NOTNULL(table->long_vec());
+ TEST_EQ(table->long_vec()->size(), 0);
+ TEST_NOTNULL(table->ulong_vec());
+ TEST_EQ(table->ulong_vec()->size(), 0);
+ TEST_NOTNULL(table->float_vec());
+ TEST_EQ(table->float_vec()->size(), 0);
+ TEST_NOTNULL(table->double_vec());
+ TEST_EQ(table->double_vec()->size(), 0);
+
+ // Verify default string_vec.
+ TEST_NOTNULL(table->string_vec());
+ TEST_EQ(table->string_vec()->size(), 0);
+
+ // Verify default string fields.
+ TEST_NOTNULL(table->empty_string());
+ TEST_EQ_STR(table->empty_string()->c_str(), "");
+ TEST_NOTNULL(table->some_string());
+ TEST_EQ_STR(table->some_string()->c_str(), "some");
+
+ // Verify default struct_vec.
+ TEST_NOTNULL(table->struct_vec());
+ TEST_EQ(table->struct_vec()->size(), 0);
+
+ // Verify default table_vec.
+ TEST_NOTNULL(table->table_vec());
+ TEST_EQ(table->table_vec()->size(), 0);
+
+ // Verify default enum_vec.
+ TEST_NOTNULL(table->enum_vec());
+ TEST_EQ(table->enum_vec()->size(), 0);
+
+ // Verify non-default vector field.
+ TEST_NULL(table->regular_int_vec());
+
+ // Verify pointer and offset64 combinations.
+ TEST_NOTNULL(table->int_vec64());
+ TEST_EQ(table->int_vec64()->size(), 0);
+ TEST_NOTNULL(table->int_vec_offset64());
+ TEST_EQ(table->int_vec_offset64()->size(), 0);
+
+ // Verify non-default field.
+ TEST_EQ(table->regular_int(), 100);
+}
+
+void DefaultVectorsStringsTest_EmptyOnDefault_Mutable() {
+ FlatBufferBuilder64 builder;
+
+ // Create table without providing the fields with defaults.
+ DefaultVectorsStringsTest::TableWithDefaultVectorsBuilder tbl_builder(
+ builder);
+ tbl_builder.add_regular_int(100);
+ auto offset = tbl_builder.Finish();
+ builder.Finish(offset);
+
+ Verifier verifier(builder.GetBufferPointer(), builder.GetSize());
+ TEST_ASSERT(
+ DefaultVectorsStringsTest::VerifyTableWithDefaultVectorsBuffer(verifier));
+
+ auto* mutable_table =
+ DefaultVectorsStringsTest::GetMutableTableWithDefaultVectors(
+ builder.GetBufferPointer());
+ TEST_NOTNULL(mutable_table);
+
+ // Verify default scalar vectors.
+ TEST_NOTNULL(mutable_table->mutable_int_vec());
+ TEST_EQ(mutable_table->mutable_int_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_bool_vec());
+ TEST_EQ(mutable_table->mutable_bool_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_char_vec());
+ TEST_EQ(mutable_table->mutable_char_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_uchar_vec());
+ TEST_EQ(mutable_table->mutable_uchar_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_short_vec());
+ TEST_EQ(mutable_table->mutable_short_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_ushort_vec());
+ TEST_EQ(mutable_table->mutable_ushort_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_uint_vec());
+ TEST_EQ(mutable_table->mutable_uint_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_long_vec());
+ TEST_EQ(mutable_table->mutable_long_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_ulong_vec());
+ TEST_EQ(mutable_table->mutable_ulong_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_float_vec());
+ TEST_EQ(mutable_table->mutable_float_vec()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_double_vec());
+ TEST_EQ(mutable_table->mutable_double_vec()->size(), 0);
+
+ // Verify default struct_vec.
+ TEST_NOTNULL(mutable_table->mutable_struct_vec());
+ TEST_EQ(mutable_table->mutable_struct_vec()->size(), 0);
+
+ // Verify default table_vec.
+ TEST_NOTNULL(mutable_table->mutable_table_vec());
+ TEST_EQ(mutable_table->mutable_table_vec()->size(), 0);
+
+ // Verify default enum_vec.
+ TEST_NOTNULL(mutable_table->mutable_enum_vec());
+ TEST_EQ(mutable_table->mutable_enum_vec()->size(), 0);
+
+ // Verify non-default vector field.
+ TEST_NULL(mutable_table->mutable_regular_int_vec());
+
+ // Verify pointer and offset64 combinations.
+ TEST_NOTNULL(mutable_table->mutable_int_vec64());
+ TEST_EQ(mutable_table->mutable_int_vec64()->size(), 0);
+ TEST_NOTNULL(mutable_table->mutable_int_vec_offset64());
+ TEST_EQ(mutable_table->mutable_int_vec_offset64()->size(), 0);
+}
+
+void DefaultVectorsStringsTest_WithValues() {
+ // Create a table with values for the defaulted vector fields.
+ FlatBufferBuilder64 builder;
+ auto int_vec64 = builder.CreateVector64(std::vector({30, 40}));
+ auto int_vec_offset64 =
+ builder.CreateVector64(std::vector({50, 60}));
+ auto int_vec = builder.CreateVector(std::vector({1, 2}));
+ auto bool_vec = builder.CreateVector(std::vector({true, false}));
+ auto string_vec = builder.CreateVector(std::vector>(
+ {builder.CreateString("a"), builder.CreateString("b")}));
+ auto empty_string = builder.CreateString("not empty");
+ auto some_string = builder.CreateString("not some");
+ DefaultVectorsStringsTest::MyStruct structs[] = {
+ DefaultVectorsStringsTest::MyStruct(1, 2),
+ DefaultVectorsStringsTest::MyStruct(3, 4)};
+ auto struct_vec = builder.CreateVectorOfStructs(structs, 2);
+ auto regular_int_vec = builder.CreateVector(std::vector({10, 20}));
+
+ DefaultVectorsStringsTest::TableWithDefaultVectorsBuilder tbl_builder(
+ builder);
+ tbl_builder.add_int_vec(int_vec);
+ tbl_builder.add_int_vec64(int_vec64);
+ tbl_builder.add_int_vec_offset64(int_vec_offset64);
+
+ tbl_builder.add_bool_vec(bool_vec);
+ tbl_builder.add_string_vec(string_vec);
+ tbl_builder.add_empty_string(empty_string);
+ tbl_builder.add_some_string(some_string);
+ tbl_builder.add_struct_vec(struct_vec);
+ tbl_builder.add_regular_int_vec(regular_int_vec);
+ auto offset = tbl_builder.Finish();
+ builder.Finish(offset);
+
+ Verifier verifier(builder.GetBufferPointer(), builder.GetSize());
+ TEST_ASSERT(
+ DefaultVectorsStringsTest::VerifyTableWithDefaultVectorsBuffer(verifier));
+
+ const auto* table = DefaultVectorsStringsTest::GetTableWithDefaultVectors(
+ builder.GetBufferPointer());
+
+ TEST_EQ(table->int_vec()->size(), 2);
+ TEST_EQ(table->int_vec()->Get(1), 2);
+ TEST_EQ(table->bool_vec()->size(), 2);
+ TEST_EQ(table->bool_vec()->Get(0), true);
+ TEST_EQ(table->string_vec()->size(), 2);
+ TEST_EQ_STR(table->string_vec()->Get(1)->c_str(), "b");
+ TEST_EQ_STR(table->empty_string()->c_str(), "not empty");
+ TEST_EQ_STR(table->some_string()->c_str(), "not some");
+ TEST_EQ(table->struct_vec()->size(), 2);
+ TEST_EQ(table->struct_vec()->Get(1)->b(), 4);
+ TEST_EQ(table->regular_int_vec()->size(), 2);
+ TEST_EQ(table->regular_int_vec()->Get(1), 20);
+
+ TEST_EQ(table->int_vec64()->size(), 2);
+ TEST_EQ(table->int_vec64()->Get(1), 40);
+ TEST_EQ(table->int_vec_offset64()->size(), 2);
+ TEST_EQ(table->int_vec_offset64()->Get(1), 60);
+}
+
+void DefaultVectorsStringsTest_BufferSize() {
+ FlatBufferBuilder64 builder;
+ // Create a table where all fields with default values are omitted.
+ DefaultVectorsStringsTest::TableWithDefaultVectorsBuilder tbl_builder(
+ builder);
+ auto offset = tbl_builder.Finish();
+ builder.Finish(offset);
+
+ Verifier verifier(builder.GetBufferPointer(), builder.GetSize());
+ TEST_ASSERT(
+ DefaultVectorsStringsTest::VerifyTableWithDefaultVectorsBuffer(verifier));
+
+ // The buffer should be small when only defaults are used.
+ // This value can be adjusted if the schema changes.
+ constexpr unsigned int buffer_size_threshold_in_bytes = 12;
+ TEST_ASSERT(builder.GetSize() <= buffer_size_threshold_in_bytes);
+}
+
+void DefaultVectorsStringsTest() {
+ DefaultVectorsStringsTest_EmptyOnDefault_Const();
+ DefaultVectorsStringsTest_EmptyOnDefault_Mutable();
+ DefaultVectorsStringsTest_WithValues();
+ DefaultVectorsStringsTest_BufferSize();
+}
+
+} // namespace tests
+} // namespace flatbuffers
diff --git a/tests/default_vectors_strings_test.fbs b/tests/default_vectors_strings_test.fbs
new file mode 100644
index 000000000..05c504293
--- /dev/null
+++ b/tests/default_vectors_strings_test.fbs
@@ -0,0 +1,39 @@
+// C++ Only Schema for Default Vectors Test
+namespace DefaultVectorsStringsTest;
+
+struct MyStruct {
+ a:int;
+ b:int;
+}
+
+table MyTable {
+ a:int;
+}
+
+enum MyEnum:byte { A, B }
+
+table TableWithDefaultVectors {
+ int_vec:[int] = [];
+ bool_vec:[bool] = [];
+ char_vec:[byte] = [];
+ uchar_vec:[ubyte] = [];
+ short_vec:[short] = [];
+ ushort_vec:[ushort] = [];
+ uint_vec:[uint] = [];
+ long_vec:[long] = [];
+ ulong_vec:[ulong] = [];
+ float_vec:[float] = [];
+ double_vec:[double] = [];
+ string_vec:[string] = [];
+ empty_string:string = "";
+ some_string:string = "some";
+ struct_vec:[MyStruct] = [];
+ table_vec:[MyTable] = [];
+ enum_vec:[MyEnum] = [];
+ regular_int_vec:[int];
+ regular_int:int;
+ int_vec_offset64:[int] = [] (offset64);
+ int_vec64:[int]= [] (vector64);
+}
+
+root_type TableWithDefaultVectors;
\ No newline at end of file
diff --git a/tests/default_vectors_strings_test.h b/tests/default_vectors_strings_test.h
new file mode 100644
index 000000000..1d36cddbb
--- /dev/null
+++ b/tests/default_vectors_strings_test.h
@@ -0,0 +1,17 @@
+#ifndef THIRDPARTY_FLATBUFFERS_TESTS_DEFAULT_VECTORS_STRINGS_TEST_H_
+#define THIRDPARTY_FLATBUFFERS_TESTS_DEFAULT_VECTORS_STRINGS_TEST_H_
+
+namespace flatbuffers {
+namespace tests {
+
+void DefaultVectorsStringsTest_EmptyOnDefault_Const();
+void DefaultVectorsStringsTest_EmptyOnDefault_Mutable();
+void DefaultVectorsStringsTest_EmptyOnDefault();
+void DefaultVectorsStringsTest_WithValues();
+void DefaultVectorsStringsTest_BufferSize();
+void DefaultVectorsStringsTest();
+
+} // namespace tests
+} // namespace flatbuffers
+
+#endif // THIRDPARTY_FLATBUFFERS_TESTS_DEFAULT_VECTORS_STRINGS_TEST_H_
diff --git a/tests/test.cpp b/tests/test.cpp
index 05e2f3345..c94d21592 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -34,6 +34,7 @@
#include "third_party/absl/container/flat_hash_set.h"
#endif
#include "alignment_test.h"
+#include "default_vectors_strings_test.h"
#include "evolution_test.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
@@ -1836,6 +1837,7 @@ int FlatBufferTests(const std::string& tests_data_path) {
Offset64Tests();
UnionUnderlyingTypeTest();
StructsInHashTableTest();
+ DefaultVectorsStringsTest();
return 0;
}
} // namespace