diff --git a/tree/ntuple/inc/ROOT/RField/RFieldSoA.hxx b/tree/ntuple/inc/ROOT/RField/RFieldSoA.hxx index a0eee042b89c0..07c5e87906dfb 100644 --- a/tree/ntuple/inc/ROOT/RField/RFieldSoA.hxx +++ b/tree/ntuple/inc/ROOT/RField/RFieldSoA.hxx @@ -63,9 +63,10 @@ class RSoAField : public RFieldBase { }; TClass *fSoAClass = nullptr; - std::vector fSoAMemberOffsets; ///< The offset of the RVec members in the SoA type - std::vector fRecordMemberIndexes; ///< Maps the SoA members to the members of the underlying record std::vector fRecordMemberFields; ///< Direct access to the member fields of the underlying record + /// The offset of the RVec members in the SoA type in the order of subfields of the underlying record type. + /// In particular, the order is not necessarily the same then the order of RVec members in the SoA class. + std::vector fSoAMemberOffsets; std::size_t fMaxAlignment = 1; ROOT::Internal::RColumnIndex fNWritten; diff --git a/tree/ntuple/src/RFieldMeta.cxx b/tree/ntuple/src/RFieldMeta.cxx index c8e331f30a3f5..0364db32becdb 100644 --- a/tree/ntuple/src/RFieldMeta.cxx +++ b/tree/ntuple/src/RFieldMeta.cxx @@ -658,7 +658,6 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, const RSoAF : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kCollection, false /* isSimple */), fSoAClass(source.fSoAClass), fSoAMemberOffsets(source.fSoAMemberOffsets), - fRecordMemberIndexes(source.fRecordMemberIndexes), fMaxAlignment(source.fMaxAlignment) { fTraits = source.GetTraits(); @@ -716,6 +715,8 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, TClass *clS throw RException(R__FAIL("SoA fields with inheritance are currently unsupported")); } + fSoAMemberOffsets.resize(fRecordMemberFields.size()); + unsigned int nMembers = 0; for (auto dataMember : ROOT::Detail::TRangeStaticCast(*fSoAClass->GetListOfDataMembers())) { if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent()) continue; @@ -752,13 +753,13 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, TClass *clS fMaxAlignment = std::max(fMaxAlignment, vecField->GetAlignment()); - fSoAMemberOffsets.emplace_back(dataMember->GetOffset()); - fRecordMemberIndexes.emplace_back(itr->second); + assert(itr->second < fSoAMemberOffsets.size()); + fSoAMemberOffsets[itr->second] = dataMember->GetOffset(); + nMembers++; } - if (recordFieldNameToIdx.size() != fSoAMemberOffsets.size()) { + if (recordFieldNameToIdx.size() != nMembers) { throw RException(R__FAIL("missing SoA members")); } - assert(fRecordMemberFields.size() == fSoAMemberOffsets.size()); std::string renormalizedAlias; if (ROOT::Internal::NeedsMetaNameAsAlias(fSoAClass->GetName(), renormalizedAlias)) diff --git a/tree/ntuple/test/SoAField.hxx b/tree/ntuple/test/SoAField.hxx index d31e5f04241ba..51eae69e38967 100644 --- a/tree/ntuple/test/SoAField.hxx +++ b/tree/ntuple/test/SoAField.hxx @@ -51,6 +51,12 @@ struct SoASimple { ClassDefNV(SoASimple, 2); }; +struct SoASimpleSwapped { + ROOT::RVec fY; + ROOT::RVec fX; + ClassDefNV(SoASimpleSwapped, 2); +}; + struct SoASimpleBadArray { ROOT::RVec fX; float fY[3]; diff --git a/tree/ntuple/test/SoAFieldLinkDef.h b/tree/ntuple/test/SoAFieldLinkDef.h index 79041223dbacc..5ea63c02b9043 100644 --- a/tree/ntuple/test/SoAFieldLinkDef.h +++ b/tree/ntuple/test/SoAFieldLinkDef.h @@ -13,6 +13,7 @@ #pragma link C++ class RecordSimple+; #pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimple+; +#pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimpleSwapped+; #pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimpleBadArray+; #pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimpleBadType+; #pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimpleUnexpectedMember+; diff --git a/tree/ntuple/test/ntuple_soa.cxx b/tree/ntuple/test/ntuple_soa.cxx index f32c17c6d4a31..edbcda63ac523 100644 --- a/tree/ntuple/test/ntuple_soa.cxx +++ b/tree/ntuple/test/ntuple_soa.cxx @@ -204,3 +204,25 @@ TEST(RNTuple, SoASimple) EXPECT_FLOAT_EQ(5.0, v(2).at(1).fX); EXPECT_FLOAT_EQ(6.0, v(2).at(1).fY); } + +TEST(RNTuple, SoASimpleSwapped) +{ + ROOT::TestSupport::FileRaii fileGuard("test_rntuple_soa_simple_swapped.root"); + + { + auto model = ROOT::RNTupleModel::Create(); + model->AddField(std::make_unique("f", "SoASimpleSwapped")); + auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath()); + auto soa = writer->GetModel().GetDefaultEntry().GetPtr("f"); + soa->fX.push_back(1.0); + soa->fY.push_back(2.0); + writer->Fill(); + } + + auto reader = ROOT::RNTupleReader::Open("ntpl", fileGuard.GetPath()); + EXPECT_EQ(1u, reader->GetNEntries()); + auto v = reader->GetView>("f"); + EXPECT_EQ(1u, v(0).size()); + EXPECT_FLOAT_EQ(1.0, v(0).at(0).fX); + EXPECT_FLOAT_EQ(2.0, v(0).at(0).fY); +}