Skip to content

Commit 5822c1c

Browse files
authored
Fix dereference operator of VectorIterator to structures (#8425)
For Vector or Array of structures the dereference operator of an iterator returns the pointer to the structure. However, IndirectHelper, which is used in the implementation of this operator, is instantiated in the way that the IndirectHelper::Read returns structure by value. This is because, Vector and Array instantiate IndirectHelper with const T*, but VectorIterator instantiates IndirectHelper with T. There are three IndirectHelper template definition: first for T, second for Offset<T> and the last one for const T*. Those have different IndirectHelper:Read implementations and (more importantly) return type. This is the reason of mismatch in VectorIterator::operator* between return type declaration and what was exactly returned. That is, for Array<T,...> where T is scalar the VectorIterator is instantiated as VectorIterator<T, T>, dereference operator returns T and its implementation uses IndirectHelper<T> which Read function returns T. When T is not scalar, then VectorIterator is instantiated as VectorIterator<T, const T *>, dereference operator returns const T * and its implementation uses IndirectHelper<T> which Read function returns T. The fix is done as follows: * implement type trait is_specialization_of_Offset and is_specialization_of_Offset64, * change partial specialization of IndirectHelper with const T * that it is instantiated by T and enabled only if T is not scalar and not specialization of Offset or Offset64, * remove type differentiation (due to scalar) from Array.. The above makes the IndirectHelper able to correctly instantiate itself basing only on T. Thus, the instantiation in VectorIterator correctly instantiate IndirectHelper::Read function, especially the return type.
1 parent 609c72c commit 5822c1c

File tree

3 files changed

+44
-10
lines changed

3 files changed

+44
-10
lines changed

include/flatbuffers/array.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,10 @@ template<typename T, uint16_t length> class Array {
3131
// Array<T> can carry only POD data types (scalars or structs).
3232
typedef typename flatbuffers::bool_constant<flatbuffers::is_scalar<T>::value>
3333
scalar_tag;
34-
typedef
35-
typename flatbuffers::conditional<scalar_tag::value, T, const T *>::type
36-
IndirectHelperType;
3734

3835
public:
3936
typedef uint16_t size_type;
40-
typedef typename IndirectHelper<IndirectHelperType>::return_type return_type;
37+
typedef typename IndirectHelper<T>::return_type return_type;
4138
typedef VectorConstIterator<T, return_type, uoffset_t> const_iterator;
4239
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;
4340

@@ -50,7 +47,7 @@ template<typename T, uint16_t length> class Array {
5047

5148
return_type Get(uoffset_t i) const {
5249
FLATBUFFERS_ASSERT(i < size());
53-
return IndirectHelper<IndirectHelperType>::Read(Data(), i);
50+
return IndirectHelper<T>::Read(Data(), i);
5451
}
5552

5653
return_type operator[](uoffset_t i) const { return Get(i); }

include/flatbuffers/buffer.h

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <algorithm>
2121

2222
#include "flatbuffers/base.h"
23+
#include "flatbuffers/stl_emulation.h"
2324

2425
namespace flatbuffers {
2526

@@ -36,6 +37,10 @@ template<typename T = void> struct Offset {
3637
bool IsNull() const { return !o; }
3738
};
3839

40+
template<typename T> struct is_specialisation_of_Offset : false_type {};
41+
template<typename T>
42+
struct is_specialisation_of_Offset<Offset<T>> : true_type {};
43+
3944
// Wrapper for uoffset64_t Offsets.
4045
template<typename T = void> struct Offset64 {
4146
// The type of offset to use.
@@ -48,6 +53,10 @@ template<typename T = void> struct Offset64 {
4853
bool IsNull() const { return !o; }
4954
};
5055

56+
template<typename T> struct is_specialisation_of_Offset64 : false_type {};
57+
template<typename T>
58+
struct is_specialisation_of_Offset64<Offset64<T>> : true_type {};
59+
5160
// Litmus check for ensuring the Offsets are the expected size.
5261
static_assert(sizeof(Offset<>) == 4, "Offset has wrong size");
5362
static_assert(sizeof(Offset64<>) == 8, "Offset64 has wrong size");
@@ -90,7 +99,7 @@ static inline bool StringLessThan(const char *a_data, uoffset_t a_size,
9099
// return type like this.
91100
// The typedef is for the convenience of callers of this function
92101
// (avoiding the need for a trailing return decltype)
93-
template<typename T> struct IndirectHelper {
102+
template<typename T, typename Enable = void> struct IndirectHelper {
94103
typedef T return_type;
95104
typedef T mutable_return_type;
96105
static const size_t element_stride = sizeof(T);
@@ -135,10 +144,20 @@ struct IndirectHelper<OffsetT<T>> {
135144
};
136145

137146
// For vector of structs.
138-
template<typename T> struct IndirectHelper<const T *> {
139-
typedef const T *return_type;
140-
typedef T *mutable_return_type;
141-
static const size_t element_stride = sizeof(T);
147+
template<typename T>
148+
struct IndirectHelper<
149+
T, typename std::enable_if<
150+
!std::is_scalar<typename std::remove_pointer<T>::type>::value &&
151+
!is_specialisation_of_Offset<T>::value &&
152+
!is_specialisation_of_Offset64<T>::value>::type> {
153+
private:
154+
typedef typename std::remove_pointer<typename std::remove_cv<T>::type>::type
155+
pointee_type;
156+
157+
public:
158+
typedef const pointee_type *return_type;
159+
typedef pointee_type *mutable_return_type;
160+
static const size_t element_stride = sizeof(pointee_type);
142161

143162
static return_type Read(const uint8_t *const p, const size_t i) {
144163
// Structs are stored inline, relative to the first struct pointer.

tests/test.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,24 @@ void FixedLengthArrayConstructorTest() {
834834
TEST_EQ(arr_struct.e(), 10);
835835
TEST_EQ(arr_struct.f()->Get(0), -2);
836836
TEST_EQ(arr_struct.f()->Get(1), -1);
837+
838+
// Test for each loop over NestedStruct entries
839+
for (auto i : *arr_struct.d()) {
840+
for (auto a : *i->a()) {
841+
TEST_EQ(a, 1);
842+
break; // one iteration is enough, just testing compilation
843+
}
844+
TEST_EQ(i->b(), MyGame::Example::TestEnum::B);
845+
for (auto c : *i->c()) {
846+
TEST_EQ(c, MyGame::Example::TestEnum::A);
847+
break; // one iteration is enough, just testing compilation
848+
}
849+
for (auto d : *i->d()) {
850+
TEST_EQ(d, -2);
851+
break; // one iteration is enough, just testing compilation
852+
}
853+
break; // one iteration is enough, just testing compilation
854+
}
837855
}
838856
#else
839857
void FixedLengthArrayConstructorTest() {}

0 commit comments

Comments
 (0)