Skip to content
1 change: 0 additions & 1 deletion tree/ntuple/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ SOURCES
v7/src/RColumnElement.cxx
v7/src/RField.cxx
v7/src/RFieldVisitor.cxx
v7/src/REntry.cxx
v7/src/RMiniFile.cxx
v7/src/RNTuple.cxx
v7/src/RNTupleAnchor.cxx
Expand Down
13 changes: 5 additions & 8 deletions tree/ntuple/v7/inc/ROOT/REntry.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,20 @@ class REntry {
std::uint64_t fModelId = 0;
/// Corresponds to the top-level fields of the linked model
std::vector<Detail::RFieldBase::RValue> fValues;
/// The objects involed in serialization and deserialization might be used long after the entry is gone:
/// hence the shared pointer
std::vector<std::shared_ptr<void>> fValuePtrs;

// Creation of entries is done by the RNTupleModel class

REntry() = default;
explicit REntry(std::uint64_t modelId) : fModelId(modelId) {}

void AddValue(Detail::RFieldBase::RValue &&value);
void AddValue(Detail::RFieldBase::RValue &&value) { fValues.emplace_back(std::move(value)); }

/// While building the entry, adds a new value to the list and return the value's shared pointer
template<typename T, typename... ArgsT>
std::shared_ptr<T> AddValue(RField<T>* field, ArgsT&&... args) {
template <typename T, typename... ArgsT>
std::shared_ptr<T> AddValue(RField<T> &field, ArgsT &&...args)
{
auto ptr = std::make_shared<T>(std::forward<ArgsT>(args)...);
fValues.emplace_back(field->BindValue(ptr));
fValuePtrs.emplace_back(ptr);
fValues.emplace_back(field.BindValue(ptr));
return ptr;
}

Expand Down
8 changes: 4 additions & 4 deletions tree/ntuple/v7/inc/ROOT/RNTuple.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,12 @@ public:
std::unique_ptr<RNTupleReader> Clone() { return std::make_unique<RNTupleReader>(fSource->Clone()); }
~RNTupleReader();

RNTupleModel *GetModel();
NTupleSize_t GetNEntries() const { return fSource->GetNEntries(); }
const RNTupleModel &GetModel();

/// Returns a cached copy of the page source descriptor. The returned pointer remains valid until the next call
/// to LoadEntry or to any of the views returned from the reader.
const RNTupleDescriptor *GetDescriptor();
const RNTupleDescriptor &GetDescriptor();

/// Prints a detailed summary of the ntuple, including a list of fields.
///
Expand Down Expand Up @@ -250,7 +250,7 @@ public:
fModel = fSource->GetSharedDescriptorGuard()->GenerateModel();
ConnectModel(*fModel);
}
LoadEntry(index, *fModel->GetDefaultEntry());
LoadEntry(index, fModel->GetDefaultEntry());
}
/// Fills a user provided entry after checking that the entry has been instantiated from the ntuple model
void LoadEntry(NTupleSize_t index, REntry &entry) { entry.Read(index); }
Expand Down Expand Up @@ -463,7 +463,7 @@ public:

/// The simplest user interface if the default entry that comes with the ntuple model is used.
/// \return The number of uncompressed bytes written.
std::size_t Fill() { return fFillContext.Fill(*fFillContext.fModel->GetDefaultEntry()); }
std::size_t Fill() { return fFillContext.Fill(fFillContext.fModel->GetDefaultEntry()); }
/// Multiple entries can have been instantiated from the ntuple model. This method will perform
/// a light check whether the entry comes from the ntuple's own model.
/// \return The number of uncompressed bytes written.
Expand Down
44 changes: 13 additions & 31 deletions tree/ntuple/v7/inc/ROOT/RNTupleModel.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,20 @@ public:
/// Upon completion, `BeginUpdate()` can be called again to begin a new set of changes.
void CommitUpdate();

void AddField(std::unique_ptr<Detail::RFieldBase> field);
template <typename T>
void AddField(const NameWithDescription_t &fieldNameDesc, T *fromWhere)
template <typename T, typename... ArgsT>
std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
{
fOpenChangeset.fModel.AddField<T>(fieldNameDesc, fromWhere);
auto objPtr = fOpenChangeset.fModel.MakeField<T>(fieldNameDesc, std::forward<ArgsT>(args)...);
auto fieldZero = fOpenChangeset.fModel.fFieldZero.get();
auto it = std::find_if(fieldZero->begin(), fieldZero->end(),
[&](const auto &f) { return f.GetName() == fieldNameDesc.fName; });
R__ASSERT(it != fieldZero->end());
fOpenChangeset.fAddedFields.emplace_back(&(*it));
return objPtr;
}

void AddField(std::unique_ptr<Detail::RFieldBase> field);

RResult<void> AddProjectedField(std::unique_ptr<Detail::RFieldBase> field,
std::function<std::string(const std::string &)> mapping);
};
Expand Down Expand Up @@ -260,16 +262,15 @@ public:
/// });
/// ~~~
template <typename T, typename... ArgsT>
std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc,
ArgsT&&... args)
std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
{
EnsureNotFrozen();
EnsureValidFieldName(fieldNameDesc.fName);
auto field = std::make_unique<RField<T>>(fieldNameDesc.fName);
field->SetDescription(fieldNameDesc.fDescription);
std::shared_ptr<T> ptr;
if (fDefaultEntry)
ptr = fDefaultEntry->AddValue<T>(field.get(), std::forward<ArgsT>(args)...);
ptr = fDefaultEntry->AddValue<T>(*field, std::forward<ArgsT>(args)...);
fFieldZero->Attach(std::move(field));
return ptr;
}
Expand All @@ -279,34 +280,11 @@ public:
/// Throws an exception if the field is null.
void AddField(std::unique_ptr<Detail::RFieldBase> field);

/// Throws an exception if fromWhere is null.
template <typename T>
void AddField(const NameWithDescription_t &fieldNameDesc, T* fromWhere) {
EnsureNotFrozen();
EnsureNotBare();
if (!fromWhere)
throw RException(R__FAIL("null field fromWhere"));
EnsureValidFieldName(fieldNameDesc.fName);

auto field = std::make_unique<RField<T>>(fieldNameDesc.fName);
field->SetDescription(fieldNameDesc.fDescription);
fDefaultEntry->AddValue(field->BindValue(std::shared_ptr<void>(fromWhere, [](void *) {})));
fFieldZero->Attach(std::move(field));
}

/// Adds a top-level field based on existing fields. The mapping function is called with the qualified field names
/// of the provided field and the subfields. It should return the qualified field names used as a mapping source.
/// Projected fields can only be used for models used to write data.
RResult<void> AddProjectedField(std::unique_ptr<Detail::RFieldBase> field,
std::function<std::string(const std::string &)> mapping);

template <typename T>
T *Get(std::string_view fieldName) const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be an idea to have a Get<T> (perhaps named differently) that returns a shared pointer to the object? The use case would be for type-erased fields. Right now the (only) way to do it is through ntuple->GetModel().GetDefaultEntry()->GetPtr<T>("fieldname"), which feels a bit unwieldy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a possibility, a getter acting as a shortcut for GetModel().GetDefaultEntry()->GetPtr<T>. On the other hand, callers can could also first get the entry and then call (multiple times) entry.GetPtr<T>(). How about we see if users stumble across it and defer the addition of the method until then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes fair enough, sounds good!

{
EnsureNotBare();
return fDefaultEntry->GetPtr<T>(fieldName).get();
}

const RProjectedFields &GetProjectedFields() const { return *fProjectedFields; }

void Freeze();
Expand All @@ -325,7 +303,11 @@ public:
/// In a bare entry, all values point to nullptr. The resulting entry shall use BindValue() in order
/// set memory addresses to be serialized / deserialized
std::unique_ptr<REntry> CreateBareEntry() const;
REntry *GetDefaultEntry() const;
/// Calls the given field's GenerateBulk() method. Throws an exception if no field with the given name exists.
Detail::RFieldBase::RBulk GenerateBulk(std::string_view fieldName) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add some documentation


REntry &GetDefaultEntry();
const REntry &GetDefaultEntry() const;

/// Non-const access to the root field is used to commit clusters during writing
/// and to set the on-disk field IDs when connecting a model to a page source or sink.
Expand Down
24 changes: 0 additions & 24 deletions tree/ntuple/v7/src/REntry.cxx

This file was deleted.

14 changes: 7 additions & 7 deletions tree/ntuple/v7/src/RNTuple.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,13 @@ ROOT::Experimental::RNTupleReader::OpenFriends(std::span<ROpenSpec> ntuples)
return std::make_unique<RNTupleReader>(std::make_unique<Detail::RPageSourceFriends>("_friends", sources));
}

ROOT::Experimental::RNTupleModel *ROOT::Experimental::RNTupleReader::GetModel()
const ROOT::Experimental::RNTupleModel &ROOT::Experimental::RNTupleReader::GetModel()
{
if (!fModel) {
fModel = fSource->GetSharedDescriptorGuard()->GenerateModel();
ConnectModel(*fModel);
}
return fModel.get();
return *fModel;
}

void ROOT::Experimental::RNTupleReader::PrintInfo(const ENTupleInfo what, std::ostream &output)
Expand Down Expand Up @@ -235,16 +235,16 @@ ROOT::Experimental::RNTupleReader *ROOT::Experimental::RNTupleReader::GetDisplay
void ROOT::Experimental::RNTupleReader::Show(NTupleSize_t index, std::ostream &output)
{
auto reader = GetDisplayReader();
auto entry = reader->GetModel()->GetDefaultEntry();
const auto &entry = reader->GetModel().GetDefaultEntry();

reader->LoadEntry(index);
output << "{";
for (auto iValue = entry->begin(); iValue != entry->end();) {
for (auto iValue = entry.begin(); iValue != entry.end();) {
output << std::endl;
RPrintValueVisitor visitor(*iValue, output, 1 /* level */);
iValue->GetField().AcceptVisitor(visitor);

if (++iValue == entry->end()) {
if (++iValue == entry.end()) {
output << std::endl;
break;
} else {
Expand All @@ -254,12 +254,12 @@ void ROOT::Experimental::RNTupleReader::Show(NTupleSize_t index, std::ostream &o
output << "}" << std::endl;
}

const ROOT::Experimental::RNTupleDescriptor *ROOT::Experimental::RNTupleReader::GetDescriptor()
const ROOT::Experimental::RNTupleDescriptor &ROOT::Experimental::RNTupleReader::GetDescriptor()
{
auto descriptorGuard = fSource->GetSharedDescriptorGuard();
if (!fCachedDescriptor || fCachedDescriptor->GetGeneration() != descriptorGuard->GetGeneration())
fCachedDescriptor = descriptorGuard->Clone();
return fCachedDescriptor.get();
return *fCachedDescriptor;
}

//------------------------------------------------------------------------------
Expand Down
24 changes: 22 additions & 2 deletions tree/ntuple/v7/src/RNTupleModel.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,20 @@ ROOT::Experimental::RNTupleModel::GetField(std::string_view fieldName) const
return *f;
}

ROOT::Experimental::REntry *ROOT::Experimental::RNTupleModel::GetDefaultEntry() const
ROOT::Experimental::REntry &ROOT::Experimental::RNTupleModel::GetDefaultEntry()
{
if (!IsFrozen())
throw RException(R__FAIL("invalid attempt to get default entry of unfrozen model"));
EnsureNotBare();
return fDefaultEntry.get();
return *fDefaultEntry;
}

const ROOT::Experimental::REntry &ROOT::Experimental::RNTupleModel::GetDefaultEntry() const
{
if (!IsFrozen())
throw RException(R__FAIL("invalid attempt to get default entry of unfrozen model"));
EnsureNotBare();
return *fDefaultEntry;
}

std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::CreateEntry() const
Expand All @@ -362,6 +370,18 @@ std::unique_ptr<ROOT::Experimental::REntry> ROOT::Experimental::RNTupleModel::Cr
return entry;
}

ROOT::Experimental::Detail::RFieldBase::RBulk
ROOT::Experimental::RNTupleModel::GenerateBulk(std::string_view fieldName) const
{
if (!IsFrozen())
throw RException(R__FAIL("invalid attempt to create bulk of unfrozen model"));

auto f = FindField(fieldName);
if (!f)
throw RException(R__FAIL("no such field: " + std::string(fieldName)));
return f->GenerateBulk();
}

void ROOT::Experimental::RNTupleModel::Unfreeze()
{
if (!IsFrozen())
Expand Down
Loading