Add Array initialization from struct constructor (#5865) (#6147)

- add flatbuffers::span
- add new constructor for `struct` with `array`
- add some test for flatbuffers::span and 'arrays_test.fbs'
This commit is contained in:
Vladimir Glavnyy
2020-10-13 02:24:18 +07:00
committed by GitHub
parent 77d57fd075
commit 04bec23a37
10 changed files with 608 additions and 89 deletions

View File

@@ -177,10 +177,9 @@ namespace flatbuffers {
#define FLATBUFFERS_CONSTEXPR_CPP11
#endif
// This macro is never used in code!
#if (defined(__cplusplus) && __cplusplus >= 201402L) || \
(defined(__cpp_constexpr) && __cpp_constexpr >= 201304)
#define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR
#define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11
#else
#define FLATBUFFERS_CONSTEXPR_CPP14
#endif

View File

@@ -435,6 +435,7 @@ template<typename T, uint16_t length> class Array {
IndirectHelperType;
public:
typedef uint16_t size_type;
typedef typename IndirectHelper<IndirectHelperType>::return_type return_type;
typedef VectorIterator<T, return_type> const_iterator;
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;
@@ -492,6 +493,22 @@ template<typename T, uint16_t length> class Array {
const T *data() const { return reinterpret_cast<const T *>(Data()); }
T *data() { return reinterpret_cast<T *>(Data()); }
// Copy data from a span with endian conversion.
// If this Array and the span overlap, the behavior is undefined.
void CopyFromSpan(flatbuffers::span<const T, length> src) {
const auto p1 = reinterpret_cast<const uint8_t *>(src.data());
const auto p2 = Data();
FLATBUFFERS_ASSERT(!(p1 >= p2 && p1 < (p2 + length)) &&
!(p2 >= p1 && p2 < (p1 + length)));
(void)p1;
(void)p2;
CopyFromSpanImpl(
flatbuffers::integral_constant<bool,
!scalar_tag::value || sizeof(T) == 1 || FLATBUFFERS_LITTLEENDIAN>(),
src);
}
protected:
void MutateImpl(flatbuffers::integral_constant<bool, true>, uoffset_t i,
const T &val) {
@@ -504,6 +521,20 @@ template<typename T, uint16_t length> class Array {
*(GetMutablePointer(i)) = val;
}
void CopyFromSpanImpl(flatbuffers::integral_constant<bool, true>,
flatbuffers::span<const T, length> src) {
// Use std::memcpy() instead of std::copy() to avoid preformance degradation
// due to aliasing if T is char or unsigned char.
// The size is known at compile time, so memcpy would be inlined.
std::memcpy(data(), src.data(), length * sizeof(T));
}
// Copy data from flatbuffers::span with endian conversion.
void CopyFromSpanImpl(flatbuffers::integral_constant<bool, false>,
flatbuffers::span<const T, length> src) {
for (size_type k = 0; k < length; k++) { Mutate(k, src[k]); }
}
// This class is only used to access pre-existing data. Don't ever
// try to construct these manually.
// 'constexpr' allows us to use 'size()' at compile time.
@@ -549,6 +580,30 @@ template<typename T, uint16_t length> class Array<Offset<T>, length> {
uint8_t data_[1];
};
// Cast a raw T[length] to a raw flatbuffers::Array<T, length>
// without endian conversion. Use with care.
template<typename T, uint16_t length>
Array<T, length>& CastToArray(T (&arr)[length]) {
return *reinterpret_cast<Array<T, length> *>(arr);
}
template<typename T, uint16_t length>
const Array<T, length>& CastToArray(const T (&arr)[length]) {
return *reinterpret_cast<const Array<T, length> *>(arr);
}
template<typename E, typename T, uint16_t length>
Array<E, length> &CastToArrayOfEnum(T (&arr)[length]) {
static_assert(sizeof(E) == sizeof(T), "invalid enum type E");
return *reinterpret_cast<Array<E, length> *>(arr);
}
template<typename E, typename T, uint16_t length>
const Array<E, length> &CastToArrayOfEnum(const T (&arr)[length]) {
static_assert(sizeof(E) == sizeof(T), "invalid enum type E");
return *reinterpret_cast<const Array<E, length> *>(arr);
}
// Lexicographically compare two strings (possibly containing nulls), and
// return true if the first is less than the second.
static inline bool StringLessThan(const char *a_data, uoffset_t a_size,

View File

@@ -26,6 +26,14 @@
#include <memory>
#include <limits>
#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
#define FLATBUFFERS_CPP98_STL
#endif // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
#if defined(FLATBUFFERS_CPP98_STL)
#include <cctype>
#endif // defined(FLATBUFFERS_CPP98_STL)
// Detect C++17 compatible compiler.
// __cplusplus >= 201703L - a compiler has support of 'static inline' variables.
#if defined(FLATBUFFERS_USE_STD_OPTIONAL) \
@@ -35,15 +43,25 @@
#ifndef FLATBUFFERS_USE_STD_OPTIONAL
#define FLATBUFFERS_USE_STD_OPTIONAL
#endif
#endif
#endif // defined(FLATBUFFERS_USE_STD_OPTIONAL) ...
#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
#define FLATBUFFERS_CPP98_STL
#endif // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
#if defined(FLATBUFFERS_CPP98_STL)
#include <cctype>
#endif // defined(FLATBUFFERS_CPP98_STL)
// The __cpp_lib_span is the predefined feature macro.
#if defined(FLATBUFFERS_USE_STD_SPAN)
#include <span>
#elif defined(__cpp_lib_span) && defined(__has_include)
#if __has_include(<span>)
#include <span>
#define FLATBUFFERS_USE_STD_SPAN
#endif
#else
// Disable non-trivial ctors if FLATBUFFERS_SPAN_MINIMAL defined.
#if !defined(FLATBUFFERS_TEMPLATES_ALIASES) || defined(FLATBUFFERS_CPP98_STL)
#define FLATBUFFERS_SPAN_MINIMAL
#else
// Enable implicit construction of a span<T,N> from a std::array<T,N>.
#include <array>
#endif
#endif // defined(FLATBUFFERS_USE_STD_SPAN)
// This header provides backwards compatibility for C++98 STLs like stlport.
namespace flatbuffers {
@@ -444,6 +462,206 @@ FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& lhs, const Option
}
#endif // FLATBUFFERS_USE_STD_OPTIONAL
// Very limited and naive partial implementation of C++20 std::span<T,Extent>.
#if defined(FLATBUFFERS_USE_STD_SPAN)
inline constexpr std::size_t dynamic_extent = std::dynamic_extent;
template<class T, std::size_t Extent = std::dynamic_extent>
using Span = std::span<T, Extent>;
#else // !defined(FLATBUFFERS_USE_STD_SPAN)
FLATBUFFERS_CONSTEXPR std::size_t dynamic_extent = static_cast<std::size_t>(-1);
// Exclude this code if MSVC2010 or non-STL Android is active.
// The non-STL Android doesn't have `std::is_convertible` required for SFINAE.
#if !defined(FLATBUFFERS_SPAN_MINIMAL)
namespace internal {
// This is SFINAE helper class for checking of a common condition:
// > This overload only participates in overload resolution
// > Check whether a pointer to an array of U can be converted
// > to a pointer to an array of E.
// This helper is used for checking of 'U -> const U'.
template<class E, std::size_t Extent, class U, std::size_t N>
struct is_span_convertable {
using type =
typename std::conditional<std::is_convertible<U (*)[], E (*)[]>::value
&& (Extent == dynamic_extent || N == Extent),
int, void>::type;
};
} // namespace internal
#endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
// T - element type; must be a complete type that is not an abstract
// class type.
// Extent - the number of elements in the sequence, or dynamic.
template<class T, std::size_t Extent = dynamic_extent>
class span FLATBUFFERS_FINAL_CLASS {
public:
typedef T element_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::size_t size_type;
static FLATBUFFERS_CONSTEXPR size_type extent = Extent;
// Returns the number of elements in the span.
FLATBUFFERS_CONSTEXPR_CPP11 size_type size() const FLATBUFFERS_NOEXCEPT {
return count_;
}
// Returns the size of the sequence in bytes.
FLATBUFFERS_CONSTEXPR_CPP11
size_type size_bytes() const FLATBUFFERS_NOEXCEPT {
return size() * sizeof(element_type);
}
// Checks if the span is empty.
FLATBUFFERS_CONSTEXPR_CPP11 bool empty() const FLATBUFFERS_NOEXCEPT {
return size() == 0;
}
// Returns a pointer to the beginning of the sequence.
FLATBUFFERS_CONSTEXPR_CPP11 pointer data() const FLATBUFFERS_NOEXCEPT {
return data_;
}
// Returns a reference to the idx-th element of the sequence.
// The behavior is undefined if the idx is greater than or equal to size().
FLATBUFFERS_CONSTEXPR_CPP11 reference operator[](size_type idx) const {
return data()[idx];
}
FLATBUFFERS_CONSTEXPR_CPP11 span(const span &other) FLATBUFFERS_NOEXCEPT
: data_(other.data_), count_(other.count_) {}
FLATBUFFERS_CONSTEXPR_CPP14 span &operator=(const span &other)
FLATBUFFERS_NOEXCEPT {
data_ = other.data_;
count_ = other.count_;
}
// Limited implementation of
// `template <class It> constexpr std::span(It first, size_type count);`.
//
// Constructs a span that is a view over the range [first, first + count);
// the resulting span has: data() == first and size() == count.
// The behavior is undefined if [first, first + count) is not a valid range,
// or if (extent != flatbuffers::dynamic_extent && count != extent).
FLATBUFFERS_CONSTEXPR_CPP11
explicit span(pointer first, size_type count) FLATBUFFERS_NOEXCEPT
: data_ (Extent == dynamic_extent ? first : (Extent == count ? first : nullptr)),
count_(Extent == dynamic_extent ? count : (Extent == count ? Extent : 0)) {
// Make span empty if the count argument is incompatible with span<T,N>.
}
// Exclude this code if MSVC2010 is active. The MSVC2010 isn't C++11
// compliant, it doesn't support default template arguments for functions.
#if defined(FLATBUFFERS_SPAN_MINIMAL)
FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr),
count_(0) {
static_assert(extent == 0 || extent == dynamic_extent, "invalid span");
}
#else
// Constructs an empty span whose data() == nullptr and size() == 0.
// This overload only participates in overload resolution if
// extent == 0 || extent == flatbuffers::dynamic_extent.
// A dummy template argument N is need dependency for SFINAE.
template<std::size_t N = 0,
typename internal::is_span_convertable<element_type, Extent, element_type, (N - N)>::type = 0>
FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr),
count_(0) {
static_assert(extent == 0 || extent == dynamic_extent, "invalid span");
}
// Constructs a span that is a view over the array arr; the resulting span
// has size() == N and data() == std::data(arr). These overloads only
// participate in overload resolution if
// extent == std::dynamic_extent || N == extent is true and
// std::remove_pointer_t<decltype(std::data(arr))>(*)[]
// is convertible to element_type (*)[].
template<std::size_t N,
typename internal::is_span_convertable<element_type, Extent, element_type, N>::type = 0>
FLATBUFFERS_CONSTEXPR_CPP11 span(element_type (&arr)[N]) FLATBUFFERS_NOEXCEPT
: data_(arr), count_(N) {}
template<class U, std::size_t N,
typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
FLATBUFFERS_CONSTEXPR_CPP11 span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
: data_(arr.data()), count_(N) {}
//template<class U, std::size_t N,
// int = 0>
//FLATBUFFERS_CONSTEXPR_CPP11 span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
// : data_(arr.data()), count_(N) {}
template<class U, std::size_t N,
typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
FLATBUFFERS_CONSTEXPR_CPP11 span(const std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
: data_(arr.data()), count_(N) {}
// Converting constructor from another span s;
// the resulting span has size() == s.size() and data() == s.data().
// This overload only participates in overload resolution
// if extent == std::dynamic_extent || N == extent is true and U (*)[]
// is convertible to element_type (*)[].
template<class U, std::size_t N,
typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
FLATBUFFERS_CONSTEXPR_CPP11 span(const flatbuffers::span<U, N> &s) FLATBUFFERS_NOEXCEPT
: span(s.data(), s.size()) {
}
#endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
private:
// This is a naive implementation with 'count_' member even if (Extent != dynamic_extent).
pointer const data_;
const size_type count_;
};
#if !defined(FLATBUFFERS_SPAN_MINIMAL)
template<class U, std::size_t N>
FLATBUFFERS_CONSTEXPR_CPP11
flatbuffers::span<U, N> make_span(U(&arr)[N]) FLATBUFFERS_NOEXCEPT {
return span<U, N>(arr);
}
template<class U, std::size_t N>
FLATBUFFERS_CONSTEXPR_CPP11
flatbuffers::span<const U, N> make_span(const U(&arr)[N]) FLATBUFFERS_NOEXCEPT {
return span<const U, N>(arr);
}
template<class U, std::size_t N>
FLATBUFFERS_CONSTEXPR_CPP11
flatbuffers::span<U, N> make_span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT {
return span<U, N>(arr);
}
template<class U, std::size_t N>
FLATBUFFERS_CONSTEXPR_CPP11
flatbuffers::span<const U, N> make_span(const std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT {
return span<const U, N>(arr);
}
template<class U, std::size_t N>
FLATBUFFERS_CONSTEXPR_CPP11
flatbuffers::span<U, dynamic_extent> make_span(U *first, std::size_t count) FLATBUFFERS_NOEXCEPT {
return span<U, dynamic_extent>(first, count);
}
template<class U, std::size_t N>
FLATBUFFERS_CONSTEXPR_CPP11
flatbuffers::span<const U, dynamic_extent> make_span(const U *first, std::size_t count) FLATBUFFERS_NOEXCEPT {
return span<const U, dynamic_extent>(first, count);
}
#endif
#endif // defined(FLATBUFFERS_USE_STD_SPAN)
} // namespace flatbuffers
#endif // FLATBUFFERS_STL_EMULATION_H_

View File

@@ -72,6 +72,12 @@ namespace cpp {
enum CppStandard { CPP_STD_X0 = 0, CPP_STD_11, CPP_STD_17 };
// Define a style of 'struct' constructor if it has 'Array' fields.
enum GenArrayArgMode {
kArrayArgModeNone, // don't generate initialization args
kArrayArgModeSpanStatic, // generate flatbuffers::span<T,N>
};
// Extension of IDLOptions for cpp-generator.
struct IDLOptionsCpp : public IDLOptions {
// All fields start with 'g_' prefix to distinguish from the base IDLOptions.
@@ -820,6 +826,38 @@ class CppGenerator : public BaseGenerator {
}
}
std::string GenTypeSpan(const Type &type, bool immutable, size_t extent) {
// Generate "flatbuffers::span<const U, extent>".
FLATBUFFERS_ASSERT(IsSeries(type) && "unexpected type");
auto element_type = type.VectorType();
std::string text = "flatbuffers::span<";
text += immutable ? "const " : "";
if (IsScalar(element_type.base_type)) {
text += GenTypeBasic(element_type, IsEnum(element_type));
} else {
switch (element_type.base_type) {
case BASE_TYPE_STRING: {
text += "char";
break;
}
case BASE_TYPE_STRUCT: {
FLATBUFFERS_ASSERT(type.struct_def);
text += WrapInNameSpace(*type.struct_def);
break;
}
default:
FLATBUFFERS_ASSERT(false && "unexpected element's type");
break;
}
}
if (extent != flatbuffers::dynamic_extent) {
text += ", ";
text += NumToString(extent);
}
text += "> ";
return text;
}
std::string GenEnumValDecl(const EnumDef &enum_def,
const std::string &enum_val) const {
return opts_.prefixed_enums ? Name(enum_def) + "_" + enum_val : enum_val;
@@ -2938,13 +2976,14 @@ class CppGenerator : public BaseGenerator {
static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
(void)bits;
if (*code_ptr != "") *code_ptr += ",\n ";
if (!code_ptr->empty()) *code_ptr += ",\n ";
*code_ptr += "padding" + NumToString((*id)++) + "__(0)";
}
static void PaddingNoop(int bits, std::string *code_ptr, int *id) {
(void)bits;
*code_ptr += " (void)padding" + NumToString((*id)++) + "__;\n";
if (!code_ptr->empty()) *code_ptr += '\n';
*code_ptr += " (void)padding" + NumToString((*id)++) + "__;";
}
void GenStructDefaultConstructor(const StructDef &struct_def) {
@@ -2989,58 +3028,50 @@ class CppGenerator : public BaseGenerator {
code_ += " {}";
} else {
code_.SetValue("INIT_LIST", init_list);
code_.SetValue("DEFAULT_CONSTRUCTOR_BODY", body);
code_ += " {{STRUCT_NAME}}()";
code_ += " : {{INIT_LIST}} {";
code_ += "{{DEFAULT_CONSTRUCTOR_BODY}} }";
if (!body.empty()) { code_ += body; }
code_ += " }";
}
}
void GenStructConstructor(const StructDef &struct_def) {
void GenStructConstructor(const StructDef &struct_def,
GenArrayArgMode array_mode) {
std::string arg_list;
std::string init_list;
int padding_id = 0;
bool first_arg = true;
bool first_init = true;
auto first = struct_def.fields.vec.begin();
// skip arrays if generate ctor without array assignment
const auto init_arrays = (array_mode != kArrayArgModeNone);
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
const auto &field_type = field.value.type;
const auto member_name = Name(field) + "_";
const auto &type = field.value.type;
const auto is_array = IsArray(type);
const auto arg_name = "_" + Name(field);
const auto arg_type = GenTypeGet(field_type, " ", "const ", " &", true);
if (!IsArray(field_type)) {
if (first_arg) {
first_arg = false;
} else {
arg_list += ", ";
}
arg_list += arg_type;
if (!is_array || init_arrays) {
if (it != first && !arg_list.empty()) { arg_list += ", "; }
arg_list += !is_array ? GenTypeGet(type, " ", "const ", " &", true)
: GenTypeSpan(type, true, type.fixed_length);
arg_list += arg_name;
}
if (first_init) {
first_init = false;
} else {
init_list += ",";
init_list += "\n ";
// skip an array with initialization from span
if (false == (is_array && init_arrays)) {
if (it != first && !init_list.empty()) { init_list += ",\n "; }
init_list += Name(field) + "_";
if (IsScalar(type.base_type)) {
auto scalar_type = GenUnderlyingCast(field, false, arg_name);
init_list += "(flatbuffers::EndianScalar(" + scalar_type + "))";
} else {
FLATBUFFERS_ASSERT((is_array && !init_arrays) || IsStruct(type));
if (!is_array)
init_list += "(" + arg_name + ")";
else
init_list += "()";
}
}
init_list += member_name;
if (IsScalar(field_type.base_type)) {
auto type = GenUnderlyingCast(field, false, arg_name);
init_list += "(flatbuffers::EndianScalar(" + type + "))";
} else if (IsArray(field_type)) {
// implicit initialization of array
// for each object in array it:
// * sets it as zeros for POD types (integral, floating point, etc)
// * calls default constructor for classes/structs
init_list += "()";
} else {
init_list += "(" + arg_name + ")";
}
if (field.padding) {
if (field.padding)
GenPadding(field, &init_list, &padding_id, PaddingInitializer);
}
}
if (!arg_list.empty()) {
@@ -3052,10 +3083,55 @@ class CppGenerator : public BaseGenerator {
} else {
code_ += " {{STRUCT_NAME}}({{ARG_LIST}}) {";
}
padding_id = 0;
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
const auto &type = field.value.type;
if (IsArray(type) && init_arrays) {
const auto &element_type = type.VectorType();
const auto is_enum = IsEnum(element_type);
FLATBUFFERS_ASSERT(
(IsScalar(element_type.base_type) || IsStruct(element_type)) &&
"invalid declaration");
const auto face_type = GenTypeGet(type, " ", "", "", is_enum);
std::string get_array =
is_enum ? "CastToArrayOfEnum<" + face_type + ">" : "CastToArray";
const auto field_name = Name(field) + "_";
const auto arg_name = "_" + Name(field);
code_ += " flatbuffers::" + get_array + "(" + field_name +
").CopyFromSpan(" + arg_name + ");";
}
if (field.padding) {
std::string padding;
GenPadding(field, &padding, &padding_id, PaddingNoop);
code_ += padding;
}
}
code_ += " }";
}
}
void GenArrayAccessor(const Type &type, bool mutable_accessor) {
FLATBUFFERS_ASSERT(IsArray(type));
const auto is_enum = IsEnum(type.VectorType());
// The Array<bool,N> is a tricky case, like std::vector<bool>.
// It requires a specialization of Array class.
// Generate Array<uint8_t> for Array<bool>.
const auto face_type = GenTypeGet(type, " ", "", "", is_enum);
std::string ret_type = "flatbuffers::Array<" + face_type + ", " +
NumToString(type.fixed_length) + ">";
if (mutable_accessor)
code_ += " " + ret_type + " *mutable_{{FIELD_NAME}}() {";
else
code_ += " const " + ret_type + " *{{FIELD_NAME}}() const {";
std::string get_array =
is_enum ? "CastToArrayOfEnum<" + face_type + ">" : "CastToArray";
code_ += " return &flatbuffers::" + get_array + "({{FIELD_VALUE}});";
code_ += " }";
}
// Generate an accessor struct with constructor for a flatbuffers struct.
void GenStruct(const StructDef &struct_def) {
// Generate an accessor struct, with private variables of the form:
@@ -3110,19 +3186,29 @@ class CppGenerator : public BaseGenerator {
GenStructDefaultConstructor(struct_def);
// Generate a constructor that takes all fields as arguments,
// excluding arrays
GenStructConstructor(struct_def);
// excluding arrays.
GenStructConstructor(struct_def, kArrayArgModeNone);
auto arrays_num = std::count_if(struct_def.fields.vec.begin(),
struct_def.fields.vec.end(),
[](const flatbuffers::FieldDef *fd) {
return IsArray(fd->value.type);
});
if (arrays_num > 0) {
GenStructConstructor(struct_def, kArrayArgModeSpanStatic);
}
// Generate accessor methods of the form:
// type name() const { return flatbuffers::EndianScalar(name_); }
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
const auto &type = field.value.type;
const auto is_scalar = IsScalar(type.base_type);
const auto is_array = IsArray(type);
auto field_type = GenTypeGet(field.value.type, " ",
IsArray(field.value.type) ? "" : "const ",
IsArray(field.value.type) ? "" : " &", true);
auto is_scalar = IsScalar(field.value.type.base_type);
const auto field_type = GenTypeGet(type, " ", is_array ? "" : "const ",
is_array ? "" : " &", true);
auto member = Name(field) + "_";
auto value =
is_scalar ? "flatbuffers::EndianScalar(" + member + ")" : member;
@@ -3134,16 +3220,8 @@ class CppGenerator : public BaseGenerator {
GenComment(field.doc_comment, " ");
// Generate a const accessor function.
if (IsArray(field.value.type)) {
auto underlying = GenTypeGet(field.value.type, "", "", "", false);
code_ += " const flatbuffers::Array<" + field_type + ", " +
NumToString(field.value.type.fixed_length) + "> *" +
"{{FIELD_NAME}}() const {";
code_ += " return reinterpret_cast<const flatbuffers::Array<" +
field_type + ", " +
NumToString(field.value.type.fixed_length) +
"> *>({{FIELD_VALUE}});";
code_ += " }";
if (is_array) {
GenArrayAccessor(type, false);
} else {
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
code_ += " return {{FIELD_VALUE}};";
@@ -3153,11 +3231,10 @@ class CppGenerator : public BaseGenerator {
// Generate a mutable accessor function.
if (opts_.mutable_buffer) {
auto mut_field_type =
GenTypeGet(field.value.type, " ", "",
IsArray(field.value.type) ? "" : " &", true);
GenTypeGet(type, " ", "", is_array ? "" : " &", true);
code_.SetValue("FIELD_TYPE", mut_field_type);
if (is_scalar) {
code_.SetValue("ARG", GenTypeBasic(field.value.type, true));
code_.SetValue("ARG", GenTypeBasic(type, true));
code_.SetValue("FIELD_VALUE",
GenUnderlyingCast(field, false, "_" + Name(field)));
@@ -3166,16 +3243,8 @@ class CppGenerator : public BaseGenerator {
" flatbuffers::WriteScalar(&{{FIELD_NAME}}_, "
"{{FIELD_VALUE}});";
code_ += " }";
} else if (IsArray(field.value.type)) {
auto underlying = GenTypeGet(field.value.type, "", "", "", false);
code_ += " flatbuffers::Array<" + mut_field_type + ", " +
NumToString(field.value.type.fixed_length) + "> *" +
"mutable_{{FIELD_NAME}}() {";
code_ += " return reinterpret_cast<flatbuffers::Array<" +
mut_field_type + ", " +
NumToString(field.value.type.fixed_length) +
"> *>({{FIELD_VALUE}});";
code_ += " }";
} else if (is_array) {
GenArrayAccessor(type, true);
} else {
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
code_ += " return {{FIELD_VALUE}};";

View File

@@ -92,12 +92,24 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) NestedStruct FLATBUFFERS_FINAL_CLASS {
padding0__(0),
padding1__(0),
d_() {
(void)padding0__;
(void)padding1__;
}
NestedStruct(flatbuffers::span<const int32_t, 2> _a, MyGame::Example::TestEnum _b, flatbuffers::span<const MyGame::Example::TestEnum, 2> _c, flatbuffers::span<const int64_t, 2> _d)
: b_(flatbuffers::EndianScalar(static_cast<int8_t>(_b))),
padding0__(0),
padding1__(0) {
flatbuffers::CastToArray(a_).CopyFromSpan(_a);
flatbuffers::CastToArrayOfEnum<MyGame::Example::TestEnum>(c_).CopyFromSpan(_c);
(void)padding0__;
(void)padding1__;
flatbuffers::CastToArray(d_).CopyFromSpan(_d);
}
const flatbuffers::Array<int32_t, 2> *a() const {
return reinterpret_cast<const flatbuffers::Array<int32_t, 2> *>(a_);
return &flatbuffers::CastToArray(a_);
}
flatbuffers::Array<int32_t, 2> *mutable_a() {
return reinterpret_cast<flatbuffers::Array<int32_t, 2> *>(a_);
return &flatbuffers::CastToArray(a_);
}
MyGame::Example::TestEnum b() const {
return static_cast<MyGame::Example::TestEnum>(flatbuffers::EndianScalar(b_));
@@ -106,16 +118,16 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) NestedStruct FLATBUFFERS_FINAL_CLASS {
flatbuffers::WriteScalar(&b_, static_cast<int8_t>(_b));
}
const flatbuffers::Array<MyGame::Example::TestEnum, 2> *c() const {
return reinterpret_cast<const flatbuffers::Array<MyGame::Example::TestEnum, 2> *>(c_);
return &flatbuffers::CastToArrayOfEnum<MyGame::Example::TestEnum>(c_);
}
flatbuffers::Array<MyGame::Example::TestEnum, 2> *mutable_c() {
return reinterpret_cast<flatbuffers::Array<MyGame::Example::TestEnum, 2> *>(c_);
return &flatbuffers::CastToArrayOfEnum<MyGame::Example::TestEnum>(c_);
}
const flatbuffers::Array<int64_t, 2> *d() const {
return reinterpret_cast<const flatbuffers::Array<int64_t, 2> *>(d_);
return &flatbuffers::CastToArray(d_);
}
flatbuffers::Array<int64_t, 2> *mutable_d() {
return reinterpret_cast<flatbuffers::Array<int64_t, 2> *>(d_);
return &flatbuffers::CastToArray(d_);
}
};
FLATBUFFERS_STRUCT_END(NestedStruct, 32);
@@ -175,6 +187,26 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ArrayStruct FLATBUFFERS_FINAL_CLASS {
e_(flatbuffers::EndianScalar(_e)),
padding3__(0),
f_() {
(void)padding0__;
(void)padding1__;
(void)padding2__;
(void)padding3__;
}
ArrayStruct(float _a, flatbuffers::span<const int32_t, 15> _b, int8_t _c, flatbuffers::span<const MyGame::Example::NestedStruct, 2> _d, int32_t _e, flatbuffers::span<const int64_t, 2> _f)
: a_(flatbuffers::EndianScalar(_a)),
c_(flatbuffers::EndianScalar(_c)),
padding0__(0),
padding1__(0),
padding2__(0),
e_(flatbuffers::EndianScalar(_e)),
padding3__(0) {
flatbuffers::CastToArray(b_).CopyFromSpan(_b);
(void)padding0__;
(void)padding1__;
(void)padding2__;
flatbuffers::CastToArray(d_).CopyFromSpan(_d);
(void)padding3__;
flatbuffers::CastToArray(f_).CopyFromSpan(_f);
}
float a() const {
return flatbuffers::EndianScalar(a_);
@@ -183,10 +215,10 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ArrayStruct FLATBUFFERS_FINAL_CLASS {
flatbuffers::WriteScalar(&a_, _a);
}
const flatbuffers::Array<int32_t, 15> *b() const {
return reinterpret_cast<const flatbuffers::Array<int32_t, 15> *>(b_);
return &flatbuffers::CastToArray(b_);
}
flatbuffers::Array<int32_t, 15> *mutable_b() {
return reinterpret_cast<flatbuffers::Array<int32_t, 15> *>(b_);
return &flatbuffers::CastToArray(b_);
}
int8_t c() const {
return flatbuffers::EndianScalar(c_);
@@ -195,10 +227,10 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ArrayStruct FLATBUFFERS_FINAL_CLASS {
flatbuffers::WriteScalar(&c_, _c);
}
const flatbuffers::Array<MyGame::Example::NestedStruct, 2> *d() const {
return reinterpret_cast<const flatbuffers::Array<MyGame::Example::NestedStruct, 2> *>(d_);
return &flatbuffers::CastToArray(d_);
}
flatbuffers::Array<MyGame::Example::NestedStruct, 2> *mutable_d() {
return reinterpret_cast<flatbuffers::Array<MyGame::Example::NestedStruct, 2> *>(d_);
return &flatbuffers::CastToArray(d_);
}
int32_t e() const {
return flatbuffers::EndianScalar(e_);
@@ -207,10 +239,10 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ArrayStruct FLATBUFFERS_FINAL_CLASS {
flatbuffers::WriteScalar(&e_, _e);
}
const flatbuffers::Array<int64_t, 2> *f() const {
return reinterpret_cast<const flatbuffers::Array<int64_t, 2> *>(f_);
return &flatbuffers::CastToArray(f_);
}
flatbuffers::Array<int64_t, 2> *mutable_f() {
return reinterpret_cast<flatbuffers::Array<int64_t, 2> *>(f_);
return &flatbuffers::CastToArray(f_);
}
};
FLATBUFFERS_STRUCT_END(ArrayStruct, 160);

View File

@@ -487,6 +487,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS {
: a_(flatbuffers::EndianScalar(_a)),
b_(flatbuffers::EndianScalar(_b)),
padding0__(0) {
(void)padding0__;
}
int16_t a() const {
return flatbuffers::EndianScalar(a_);
@@ -543,6 +544,9 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS {
padding1__(0),
test3_(_test3),
padding2__(0) {
(void)padding0__;
(void)padding1__;
(void)padding2__;
}
float x() const {
return flatbuffers::EndianScalar(x_);

View File

@@ -115,6 +115,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Struct FLATBUFFERS_FINAL_CLASS {
: a_(flatbuffers::EndianScalar(_a)),
padding0__(0),
b_(flatbuffers::EndianScalar(_b)) {
(void)padding0__;
}
int32_t a() const {
return flatbuffers::EndianScalar(a_);

View File

@@ -131,6 +131,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Struct FLATBUFFERS_FINAL_CLASS {
: a_(flatbuffers::EndianScalar(_a)),
padding0__(0),
b_(flatbuffers::EndianScalar(_b)) {
(void)padding0__;
}
int32_t a() const {
return flatbuffers::EndianScalar(a_);

View File

@@ -602,6 +602,7 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS {
: a_(flatbuffers::EndianScalar(_a)),
b_(flatbuffers::EndianScalar(_b)),
padding0__(0) {
(void)padding0__;
}
int16_t a() const {
return flatbuffers::EndianScalar(a_);
@@ -669,6 +670,9 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS {
padding1__(0),
test3_(_test3),
padding2__(0) {
(void)padding0__;
(void)padding1__;
(void)padding2__;
}
float x() const {
return flatbuffers::EndianScalar(x_);

View File

@@ -3214,6 +3214,89 @@ void CreateSharedStringTest() {
TEST_EQ((*a[6]) < (*a[5]), true);
}
#if !defined(FLATBUFFERS_SPAN_MINIMAL)
void FlatbuffersSpanTest() {
// Compile-time checking of non-const [] to const [] conversions.
using flatbuffers::internal::is_span_convertable;
(void)is_span_convertable<int, 1, int, 1>::type(123);
(void)is_span_convertable<const int, 1, int, 1>::type(123);
(void)is_span_convertable<const int64_t, 1, int64_t, 1>::type(123);
(void)is_span_convertable<const uint64_t, 1, uint64_t, 1>::type(123);
(void)is_span_convertable<const int, 1, const int, 1>::type(123);
(void)is_span_convertable<const int64_t, 1, const int64_t, 1>::type(123);
(void)is_span_convertable<const uint64_t, 1, const uint64_t, 1>::type(123);
using flatbuffers::span;
span<char, 0> c1;
TEST_EQ(c1.size(), 0);
span<char, flatbuffers::dynamic_extent> c2;
TEST_EQ(c2.size(), 0);
span<char> c3;
TEST_EQ(c3.size(), 0);
TEST_ASSERT(c1.empty() && c2.empty() && c3.empty());
int i_data7[7] = { 0, 1, 2, 3, 4, 5, 6 };
span<int, 7> i1(&i_data7[0], 7);
span<int> i2(i1); // make dynamic from static
TEST_EQ(i1.size(), 7);
TEST_EQ(i1.empty(), false);
TEST_EQ(i1.size(), i2.size());
TEST_EQ(i1.data(), i_data7);
TEST_EQ(i1[2], 2);
// Make const span from a non-const one.
span<const int, 7> i3(i1);
// Construct from a C-array.
span<int, 7> i4(i_data7);
span<const int, 7> i5(i_data7);
span<int> i6(i_data7);
span<const int> i7(i_data7);
TEST_EQ(i7.size(), 7);
// Check construction from a const array.
const int i_cdata5[5] = { 4, 3, 2, 1, 0 };
span<const int, 5> i8(i_cdata5);
span<const int> i9(i_cdata5);
TEST_EQ(i9.size(), 5);
// Construction from a (ptr, size) pair.
span<int, 7> i10(i_data7, 7);
span<int> i11(i_data7, 7);
TEST_EQ(i11.size(), 7);
span<const int, 5> i12(i_cdata5, 5);
span<const int> i13(i_cdata5, 5);
TEST_EQ(i13.size(), 5);
// Construction from std::array.
std::array<int, 6> i_arr6 = { { 0, 1, 2, 3, 4, 5 } };
span<int, 6> i14(i_arr6);
span<const int, 6> i15(i_arr6);
span<int> i16(i_arr6);
span<const int> i17(i_arr6);
TEST_EQ(i17.size(), 6);
const std::array<int, 8> i_carr8 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
span<const int, 8> i18(i_carr8);
span<const int> i19(i_carr8);
TEST_EQ(i18.size(), 8);
TEST_EQ(i19.size(), 8);
TEST_EQ(i19[7], 7);
// Check compatibility with flatbuffers::Array.
int fbs_int3_underlaying[3] = { 0 };
int fbs_int3_data[3] = { 1, 2, 3 };
auto &fbs_int3 = flatbuffers::CastToArray(fbs_int3_underlaying);
fbs_int3.CopyFromSpan(fbs_int3_data);
TEST_EQ(fbs_int3.Get(1), 2);
const int fbs_cint3_data[3] = { 2, 3, 4 };
fbs_int3.CopyFromSpan(fbs_cint3_data);
TEST_EQ(fbs_int3.Get(1), 3);
// Check with Array<Enum, N>
enum class Dummy : uint16_t { Zero = 0, One, Two };
Dummy fbs_dummy3_underlaying[3] = {};
Dummy fbs_dummy3_data[3] = { Dummy::One, Dummy::Two, Dummy::Two };
auto &fbs_dummy3 = flatbuffers::CastToArray(fbs_dummy3_underlaying);
fbs_dummy3.CopyFromSpan(fbs_dummy3_data);
TEST_EQ(fbs_dummy3.Get(1), Dummy::Two);
}
#else
void FlatbuffersSpanTest() {}
#endif
void FixedLengthArrayTest() {
// VS10 does not support typed enums, exclude from tests
#if !defined(_MSC_VER) || _MSC_VER >= 1700
@@ -3322,6 +3405,57 @@ void FixedLengthArrayTest() {
#endif
}
#if !defined(FLATBUFFERS_SPAN_MINIMAL) && (!defined(_MSC_VER) || _MSC_VER >= 1700)
void FixedLengthArrayConstructorTest() {
const int32_t nested_a[2] = { 1, 2 };
MyGame::Example::TestEnum nested_c[2] = { MyGame::Example::TestEnum::A,
MyGame::Example::TestEnum::B };
const int64_t int64_2[2] = { -2, -1 };
std::array<MyGame::Example::NestedStruct, 2> init_d = {
{ MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::B,
nested_c, int64_2),
MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::A,
nested_c,
std::array<int64_t, 2>{ { 12, 13 } }) }
};
MyGame::Example::ArrayStruct arr_struct(
8.125,
std::array<int32_t, 0xF>{
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
-17, init_d, 10, int64_2);
TEST_EQ(arr_struct.a(), 8.125);
TEST_EQ(arr_struct.b()->Get(2), 3);
TEST_EQ(arr_struct.c(), -17);
TEST_NOTNULL(arr_struct.d());
const auto &arr_d_0 = *arr_struct.d()->Get(0);
TEST_EQ(arr_d_0.a()->Get(0), 1);
TEST_EQ(arr_d_0.a()->Get(1), 2);
TEST_EQ(arr_d_0.b(), MyGame::Example::TestEnum::B);
TEST_EQ(arr_d_0.c()->Get(0), MyGame::Example::TestEnum::A);
TEST_EQ(arr_d_0.c()->Get(1), MyGame::Example::TestEnum::B);
TEST_EQ(arr_d_0.d()->Get(0), -2);
TEST_EQ(arr_d_0.d()->Get(1), -1);
const auto &arr_d_1 = *arr_struct.d()->Get(1);
TEST_EQ(arr_d_1.a()->Get(0), 1);
TEST_EQ(arr_d_1.a()->Get(1), 2);
TEST_EQ(arr_d_1.b(), MyGame::Example::TestEnum::A);
TEST_EQ(arr_d_1.c()->Get(0), MyGame::Example::TestEnum::A);
TEST_EQ(arr_d_1.c()->Get(1), MyGame::Example::TestEnum::B);
TEST_EQ(arr_d_1.d()->Get(0), 12);
TEST_EQ(arr_d_1.d()->Get(1), 13);
TEST_EQ(arr_struct.e(), 10);
TEST_EQ(arr_struct.f()->Get(0), -2);
TEST_EQ(arr_struct.f()->Get(1), -1);
}
#else
void FixedLengthArrayConstructorTest() {
}
#endif
void NativeTypeTest() {
const int N = 3;
@@ -3666,6 +3800,8 @@ int FlatBufferTests() {
NativeTypeTest();
OptionalScalarsTest();
ParseFlexbuffersFromJsonWithNullTest();
FlatbuffersSpanTest();
FixedLengthArrayConstructorTest();
return 0;
}