Skip to content

[ntuple] Add GetView<void> with type name string or std::type_info argument #18185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 11, 2025
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
36 changes: 36 additions & 0 deletions tree/ntuple/v7/inc/ROOT/RNTupleReader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,22 @@ public:
return GetView<T>(RetrieveFieldId(fieldName), rawPtr);
}

/// Provides access to an individual (sub)field, reading its values into `rawPtr` as the type provided by `typeName`.
///
/// \sa GetView(std::string_view, std::shared_ptr<T>)
ROOT::RNTupleView<void> GetView(std::string_view fieldName, void *rawPtr, std::string_view typeName)
{
return GetView(RetrieveFieldId(fieldName), rawPtr, typeName);
}

/// Provides access to an individual (sub)field, reading its values into `rawPtr` as the type provided by `ti`.
///
/// \sa GetView(std::string_view, std::shared_ptr<T>)
ROOT::RNTupleView<void> GetView(std::string_view fieldName, void *rawPtr, const std::type_info &ti)
{
return GetView(RetrieveFieldId(fieldName), rawPtr, ROOT::Internal::GetRenormalizedDemangledTypeName(ti));
}

/// Provides access to an individual (sub)field from its on-disk ID.
///
/// \sa GetView(std::string_view)
Expand Down Expand Up @@ -338,6 +354,26 @@ public:
return ROOT::RNTupleView<T>(std::move(field), range, rawPtr);
}

/// Provides access to an individual (sub)field from its on-disk ID, reading its values into `rawPtr` as the type
/// provided by `typeName`.
///
/// \sa GetView(std::string_view, std::shared_ptr<T>)
ROOT::RNTupleView<void> GetView(ROOT::DescriptorId_t fieldId, void *rawPtr, std::string_view typeName)
{
auto field = RNTupleView<void>::CreateField(fieldId, *fSource, typeName);
auto range = ROOT::Internal::GetFieldRange(*field, *fSource);
return RNTupleView<void>(std::move(field), range, rawPtr);
}

/// Provides access to an individual (sub)field from its on-disk ID, reading its values into `objPtr` as the type
/// provided by `ti`.
///
/// \sa GetView(std::string_view, std::shared_ptr<T>)
ROOT::RNTupleView<void> GetView(ROOT::DescriptorId_t fieldId, void *rawPtr, const std::type_info &ti)
{
return GetView(fieldId, rawPtr, ROOT::Internal::GetRenormalizedDemangledTypeName(ti));
}

/// Provides direct access to the I/O buffers of a **mappable** (sub)field.
///
/// Raises an exception if there is no field with the given name.
Expand Down
10 changes: 7 additions & 3 deletions tree/ntuple/v7/inc/ROOT/RNTupleView.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,19 @@ protected:
ROOT::RNTupleGlobalRange fFieldRange;
ROOT::RFieldBase::RValue fValue;

static std::unique_ptr<ROOT::RFieldBase>
CreateField(ROOT::DescriptorId_t fieldId, ROOT::Experimental::Internal::RPageSource &pageSource)
static std::unique_ptr<ROOT::RFieldBase> CreateField(ROOT::DescriptorId_t fieldId,
Experimental::Internal::RPageSource &pageSource,
std::string_view typeName = "")
{
std::unique_ptr<ROOT::RFieldBase> field;
{
const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
if constexpr (std::is_void_v<T>) {
field = fieldDesc.CreateField(desc);
if (typeName.empty())
field = fieldDesc.CreateField(desc);
else
field = ROOT::RFieldBase::Create(fieldDesc.GetFieldName(), std::string(typeName)).Unwrap();
} else {
field = std::make_unique<ROOT::RField<T>>(fieldDesc.GetFieldName());
}
Expand Down
80 changes: 80 additions & 0 deletions tree/ntuple/v7/test/ntuple_view.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,83 @@ TEST(RNTuple, ViewFieldIteration)
EXPECT_THAT(err.what(), testing::HasSubstr("field iteration over empty fields is unsupported"));
}
}

TEST(RNTuple, VoidWithExternalAddressAndTypeName)
{
FileRaii fileGuard("test_ntuple_voidwithexternaladdressandtypename.root");

{
auto model = RNTupleModel::Create();
auto fldFoo = model->MakeField<std::uint64_t>("foo");
auto fldBar = model->MakeField<std::uint64_t>("bar");
auto fldBaz = model->MakeField<std::vector<float>>("baz");

auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
*fldFoo = 42;
*fldBar = std::numeric_limits<std::uint64_t>::max();
*fldBaz = {1.f, 2.f, 3.f};
writer->Fill();
}

auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
ASSERT_EQ(1u, reader->GetNEntries());

// Read "foo" as std::int32_t (instead of its on-disk type std::uint64_t)
auto int32SharedPtr = std::make_shared<std::int32_t>();

// Raw pointer interface from type name string
{
auto viewFoo = reader->GetView("foo", int32SharedPtr.get(), "std::int32_t");
viewFoo(0);
EXPECT_EQ(42, *int32SharedPtr);
EXPECT_STREQ("std::int32_t", viewFoo.GetField().GetTypeName().c_str());
}

// Raw pointer interface from std::type_info
{
auto viewFoo = reader->GetView("foo", int32SharedPtr.get(), typeid(std::int32_t));
viewFoo(0);
EXPECT_EQ(42, *int32SharedPtr);
EXPECT_STREQ("std::int32_t", viewFoo.GetField().GetTypeName().c_str());
}

// Reading as a type when the on-disk value doesn't fit in this type is not possible
try {
auto viewBar = reader->GetView("bar", int32SharedPtr.get(), "std::int32_t");
viewBar(0);
} catch (const ROOT::RException &err) {
EXPECT_THAT(err.what(), testing::HasSubstr("value out of range: 18446744073709551615 for type i"));
}

// Read "baz" as std::vector<double> (instead of its on-disk type std::vector<float>)
std::vector<double> doubleVec;
void *doubleVecPtr = &doubleVec;
std::vector<double> expDoubleVec{1., 2., 3.};

// From type name string
{
auto viewBaz = reader->GetView("baz", doubleVecPtr, "std::vector<double>");
viewBaz(0);
EXPECT_EQ(expDoubleVec, viewBaz.GetValue().GetRef<std::vector<double>>());
EXPECT_STREQ("baz", viewBaz.GetField().GetFieldName().c_str());
EXPECT_STREQ("std::vector<double>", viewBaz.GetField().GetTypeName().c_str());
}

// From std::type_info
{
auto viewBaz = reader->GetView("baz", doubleVecPtr, typeid(std::vector<double>));
viewBaz(0);
EXPECT_EQ(expDoubleVec, viewBaz.GetValue().GetRef<std::vector<double>>());
EXPECT_STREQ("baz", viewBaz.GetField().GetFieldName().c_str());
EXPECT_STREQ("std::vector<double>", viewBaz.GetField().GetTypeName().c_str());
}

try {
std::vector<int> intVec;
void *bazAsIntsPtr = &intVec;
reader->GetView("baz", bazAsIntsPtr, "std::vector<int>");
} catch (const ROOT::RException &err) {
EXPECT_THAT(err.what(), testing::HasSubstr("On-disk column types {`SplitReal32`} for field `baz._0` cannot be "
"matched to its in-memory type `std::int32_t`"));
}
}
Loading