From bdcb912aec6921b3356ae3c00dfe3e0958ca5db8 Mon Sep 17 00:00:00 2001 From: Florine de Geus Date: Tue, 26 May 2026 09:38:52 +0200 Subject: [PATCH] [ntuple] Add option to request field using type name string --- tree/ntuple/inc/ROOT/RNTupleProcessor.hxx | 25 +++++++++++++++++- tree/ntuple/test/ntuple_processor.cxx | 32 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx b/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx index 9064c4d091a4a..5598260ad3ae5 100644 --- a/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx +++ b/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx @@ -377,7 +377,7 @@ public: /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be /// created. /// - /// \return An RNTupleProcessorOptionalPtr, which provides access to the field's value. + /// \return An RNTupleProcessorOptionalPtr of type `T`, which provides access to the field's value. /// /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for /// example when a field is not present in one of the chained processors or when during a join operation, no matching @@ -396,6 +396,29 @@ public: return RNTupleProcessorOptionalPtr(fEntry.get(), fieldIdx); } + ///////////////////////////////////////////////////////////////////////////// + /// \brief Request access to a field for reading during processing. + /// + /// \param[in] fieldName Name of the requested field. + /// \param[in] typeName Type of the requested field. + /// \param[in] valuePtr Pointer to bind to the field's value in the entry. If this is a `nullptr`, a pointer will be + /// created. + /// + /// \return An void-type RNTupleProcessorOptionalPtr, which provides access to the field's value. + /// + /// \warning Provide a `valuePtr` with care! Values may not always be valid for every entry during processing, for + /// example when a field is not present in one of the chained processors or when during a join operation, no matching + /// entry in the auxiliary processor can be found. Reading `valuePtr` as-is therefore comes with the risk of reading + /// invalid data. After passing a pointer to `RequestField`, we *strongly* recommend only accessing its data through + /// the interface of the returned `RNTupleProcessorOptionalPtr`, to ensure that only valid data can be read. + RNTupleProcessorOptionalPtr + RequestField(const std::string &fieldName, const std::string &typeName, void *valuePtr = nullptr) + { + Initialize(fEntry); + auto fieldIdx = AddFieldToEntry(fieldName, typeName, valuePtr, Internal::RNTupleProcessorProvenance()); + return RNTupleProcessorOptionalPtr(fEntry.get(), fieldIdx); + } + ///////////////////////////////////////////////////////////////////////////// /// \brief Print a graphical representation of the processor composition. /// diff --git a/tree/ntuple/test/ntuple_processor.cxx b/tree/ntuple/test/ntuple_processor.cxx index 61660081ec8c3..e96c6a6b3f9c6 100644 --- a/tree/ntuple/test/ntuple_processor.cxx +++ b/tree/ntuple/test/ntuple_processor.cxx @@ -231,6 +231,38 @@ TEST_F(RNTupleProcessorTest, RequestFieldWithVoidPtr) } } +TEST_F(RNTupleProcessorTest, RequestFieldWithTypeString) +{ + { + auto proc = RNTupleProcessor::Create({fNTupleNames[0], fFileNames[0]}); + EXPECT_NO_THROW(proc->RequestField("y", "std::vector")); + } + { + auto proc = RNTupleProcessor::Create({fNTupleNames[0], fFileNames[0]}); + EXPECT_NO_THROW(proc->RequestField("y", "std::vector")); + } + { + auto proc = RNTupleProcessor::Create({fNTupleNames[0], fFileNames[0]}); + EXPECT_THROW(proc->RequestField("y", "std::vetor"), ROOT::RException); + } + + auto proc = RNTupleProcessor::Create({fNTupleNames[0], fFileNames[0]}); + auto x = proc->RequestField("x", "float"); + auto yPtr = std::make_shared>(); + auto y = proc->RequestField("y", "std::vector", yPtr.get()); + + for (auto idx : *proc) { + EXPECT_EQ(idx, proc->GetCurrentEntryNumber()); + EXPECT_EQ(idx + 1, proc->GetNEntriesProcessed()); + + EXPECT_FLOAT_EQ(static_cast(idx), *std::static_pointer_cast(x.GetPtr())); + + std::vector yExp{static_cast(idx), static_cast((idx) * 2)}; + EXPECT_EQ(yExp, *std::static_pointer_cast>(y.GetPtr())); + } + EXPECT_EQ(5, proc->GetNEntriesProcessed()); +} + TEST_F(RNTupleProcessorTest, AlternativeTypes) { auto proc = RNTupleProcessor::Create({fNTupleNames[0], fFileNames[0]});