Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,7 @@ kotlin/**/generated
MODULE.bazel.lock

# Ignore the generated docs
docs/site
docs/site

# Ignore generated files
*.fbs.h
19 changes: 14 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand All @@ -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}")
Expand Down
12 changes: 8 additions & 4 deletions build_defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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.
Expand Down
60 changes: 60 additions & 0 deletions include/flatbuffers/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define FLATBUFFERS_TABLE_H_

#include "flatbuffers/base.h"
#include "flatbuffers/vector.h"
#include "flatbuffers/verifier.h"

namespace flatbuffers {
Expand Down Expand Up @@ -70,6 +71,32 @@ class Table {
return GetPointer<P, uoffset64_t>(field);
}

template <typename P, typename SizeT = uoffset_t,
typename OffsetSize = uoffset_t>
const Vector<P, SizeT>* GetVectorPointerOrEmpty(voffset_t field) const {
auto* ptr = GetPointer<const Vector<P, SizeT>*, OffsetSize>(field);
return ptr ? ptr : EmptyVector<P, SizeT>();
}

template <typename P, typename SizeT = uoffset_t>
const Vector<P, SizeT>* GetVectorPointer64OrEmpty(voffset_t field) const {
return GetVectorPointerOrEmpty<P, SizeT, uoffset64_t>(field);
}

template <typename P, typename SizeT = uoffset_t,
typename OffsetSize = uoffset_t>
Vector<P, SizeT>* GetMutableVectorPointerOrEmpty(voffset_t field) {
auto* ptr = GetPointer<Vector<P, SizeT>*, OffsetSize>(field);
// This is a const_cast, but safe, since all mutable operations on an
// empty vector are NOPs.
return ptr ? ptr : const_cast<Vector<P, SizeT>*>(EmptyVector<P, SizeT>());
}

template <typename P, typename SizeT = uoffset_t>
Vector<P, SizeT>* GetMutableVectorPointer64OrEmpty(voffset_t field) {
return GetMutableVectorPointerOrEmpty<P, SizeT, uoffset64_t>(field);
}

template <typename P>
P GetStruct(voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field);
Expand Down Expand Up @@ -177,6 +204,39 @@ class Table {
return VerifyOffsetRequired<uoffset64_t>(verifier, field);
}

// Verify a string that may have a default value.
template <typename OffsetT = uoffset_t>
bool VerifyStringWithDefault(const Verifier& verifier,
voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field);
return field_offset == 0 ||
verifier.VerifyString(GetPointer<const String*, OffsetT>(field));
}

// Verify a vector that has a default empty value.
template <typename P, typename SizeT = uoffset_t,
typename OffsetSize = uoffset_t>
bool VerifyVectorWithDefault(const Verifier& verifier,
voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field);
return field_offset == 0 ||
verifier.VerifyVector(
GetPointer<const Vector<P, SizeT>*, OffsetSize>(field));
}

template <typename P, typename SizeT = uoffset_t>
bool VerifyVector64WithDefault(const Verifier& verifier,
voffset_t field) const {
return VerifyVectorWithDefault<P, SizeT, uoffset64_t>(verifier, field);
}

protected:
template <typename T, typename SizeT = uoffset_t>
static const Vector<T, SizeT>* EmptyVector() {
static const SizeT empty_vector_length = 0;
return reinterpret_cast<const Vector<T, SizeT>*>(&empty_vector_length);
}

private:
// private constructor & copy constructor: you obtain instances of this
// class by pointing to existing data only
Expand Down
4 changes: 4 additions & 0 deletions scripts/generate_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ def glob(path, pattern):
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"
Expand Down
131 changes: 111 additions & 20 deletions src/idl_gen_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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<const ::flatbuffers::String "
" *>(&bfbs_string);";
} else {
code_ += " return {{FIELD_VALUE}};";
}
code_ += " }";
} else {
auto wire_type = GenTypeBasic(type, false);
Expand Down Expand Up @@ -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_ += " }";
}
}
}

Expand Down Expand Up @@ -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()");
Expand Down
3 changes: 2 additions & 1 deletion src/idl_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Loading
Loading