Skip to content

Commit

Permalink
[ntuple] don't try to construct an Emulated TClass as a RClassField
Browse files Browse the repository at this point in the history
When constructing a RClassField we need some information that we don't
have when the TClass is Emulated (or less), therefore we now check
that we have at least an Interpreted TClass in RFieldBase::Create().
If not, we follow the same logic as when we have no TClass at all
(i.e. either create an emulated field or fail, depending on the user
options).
  • Loading branch information
silverweed committed Feb 7, 2025
1 parent ac2e0b8 commit 6c10350
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 4 deletions.
5 changes: 4 additions & 1 deletion tree/ntuple/v7/src/RFieldBase.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,10 @@ ROOT::Experimental::RFieldBase::Create(const std::string &fieldName, const std::

if (!result) {
auto cl = TClass::GetClass(canonicalType.c_str());
if (cl != nullptr) {
// NOTE: if the class is not at least "Interpreted" we don't have enough information to
// properly construct the RClassField (e.g. we are missing the list of bases), so in that
// situation we rely on field emulation instead.
if (cl != nullptr && cl->GetState() >= TClass::kInterpreted) {
createContextGuard.AddClassToStack(canonicalType);
if (cl->GetCollectionProxy()) {
result = std::make_unique<RProxiedCollectionField>(fieldName, canonicalType);
Expand Down
10 changes: 9 additions & 1 deletion tree/ntuple/v7/src/RFieldMeta.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::st
if (fClass == nullptr) {
throw RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
}
if (fClass->GetState() < TClass::kInterpreted) {
throw RException(R__FAIL(std::string("RField: we don't have enough information about class '") +
std::string(className) +
" to construct a RClassField based on it (maybe it's an emulated class, or it was just"
" forward declared)."));
}
// Avoid accidentally supporting std types through TClass.
if (fClass->Property() & kIsDefinedInStd) {
throw RException(R__FAIL(std::string(className) + " is not supported"));
Expand Down Expand Up @@ -101,7 +107,9 @@ ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::st
fTraits |= kTraitTriviallyDestructible;

int i = 0;
for (auto baseClass : ROOT::Detail::TRangeStaticCast<TBaseClass>(*fClass->GetListOfBases())) {
const auto *bases = fClass->GetListOfBases();
assert(bases);
for (auto baseClass : ROOT::Detail::TRangeStaticCast<TBaseClass>(*bases)) {
if (baseClass->GetDelta() < 0) {
throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + std::string(className) +
" virtually inherits from " + baseClass->GetName()));
Expand Down
13 changes: 11 additions & 2 deletions tree/ntuple/v7/test/ntuple_emulated.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,19 @@ struct Outer_Simple {
ASSERT_EQ(inner->GetSubFields()[0]->GetFieldName(), "fInt1");
}

// Now test loading entries with a reader
// Now test loading entries with a reader.
// NOTE: using a TFile-based reader exercises the code path where the user-defined type
// gets loaded as an Emulated TClass, which we must make sure we handle properly.
RNTupleDescriptor::RCreateModelOptions cmOpts;
cmOpts.fEmulateUnknownTypes = true;
reader = RNTupleReader::Open(cmOpts, "ntpl", fileGuard.GetPath());

ROOT::TestSupport::CheckDiagsRAII diagRAII;
diagRAII.optionalDiag(kWarning, "TClass::Init", "no dictionary for class",
/*matchFullMessage=*/false);
std::unique_ptr<TFile> file(TFile::Open(fileGuard.GetPath().c_str()));
std::unique_ptr<ROOT::RNTuple> ntpl(file->Get<ROOT::RNTuple>("ntpl"));
reader = RNTupleReader::Open(cmOpts, *ntpl);

reader->LoadEntry(0);
}

Expand Down

0 comments on commit 6c10350

Please sign in to comment.