Skip to content
Draft
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
8 changes: 8 additions & 0 deletions tree/ntuple/inc/ROOT/RFieldUtils.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ std::string GetRenormalizedTypeName(const std::string &metaNormalizedName);
/// ensure that e.g. fundamental types are normalized to the type used by RNTuple (e.g. int -> std::int32_t).
std::string GetRenormalizedTypeName(const std::type_info &ti);

/// Checks if the meta normalized name is different from the RNTuple normalized name in a way that would cause
/// the RNTuple normalized name to request different streamer info. This can happen, e.g., if the type name has
/// Long64_t as a template parameter. In this case, RNTuple should use the meta normalized name as a type alias
/// to ensure correct reconstruction of objects from disk.
/// If the function returns true, renormalizedAlias contains the RNTuple normalized name that should be used as
/// type alias.
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias);

/// Applies all RNTuple type normalization rules except typedef resolution.
std::string GetNormalizedUnresolvedTypeName(const std::string &origName);

Expand Down
4 changes: 4 additions & 0 deletions tree/ntuple/src/RFieldMeta.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ ROOT::RClassField::RClassField(std::string_view fieldName, TClass *classp)
if (!(fClass->ClassProperty() & kClassHasExplicitDtor))
fTraits |= kTraitTriviallyDestructible;

std::string renormalizedAlias;
if (Internal::NeedsMetaNameAsAlias(classp->GetName(), renormalizedAlias))
fTypeAlias = renormalizedAlias;

int i = 0;
const auto *bases = fClass->GetListOfBases();
assert(bases);
Expand Down
36 changes: 35 additions & 1 deletion tree/ntuple/src/RFieldUtils.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ const std::unordered_map<std::string_view, std::string_view> typeTranslationMap{
{"ULong64_t", "unsigned long long"},
{"uint64_t", "std::uint64_t"}};

// Natively supported types drop the default template arguments and the CV qualifiers in template arguments.
// Natively supported types drop the default template arguments and the CV qualifiers in template arguments;
// the also keep Long64_t as template argument untouched.
bool IsUserClass(const std::string &typeName)
{
return typeName.rfind("std::", 0) != 0 && typeName.rfind("ROOT::VecOps::RVec<", 0) != 0;
Expand Down Expand Up @@ -531,6 +532,39 @@ std::string ROOT::Internal::GetRenormalizedTypeName(const std::string &metaNorma
return GetRenormalizedMetaTypeName(metaNormalizedName);
}

bool ROOT::Internal::NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias)
{
const auto canonicalTypePrefix = ROOT::Internal::GetCanonicalTypePrefix(metaNormalizedName);
if (canonicalTypePrefix.find('<') == std::string::npos) {
// If there are no templates, the function is done.
return false;
}

bool result = false;
bool isTemplatedUserClass = IsUserClass(canonicalTypePrefix);
auto fnCheckLong64 = [&](const std::string &arg) -> std::string {
if ((arg == "Long64_t" || arg == "ULong64_t") && isTemplatedUserClass) {
result = true;
return arg;
}

std::string renormalizedArgAlias;
if (NeedsMetaNameAsAlias(arg, renormalizedArgAlias)) {
result = true;
return renormalizedArgAlias;
}

const auto renormalizedArg = GetRenormalizedMetaTypeName(arg);
isTemplatedUserClass = (renormalizedArg.find('<') == std::string::npos) && IsUserClass(renormalizedArg);
return renormalizedArg;
};

renormalizedAlias = canonicalTypePrefix;
NormalizeTemplateArguments(renormalizedAlias, 0 /* maxTemplateArgs */, fnCheckLong64);

return result;
}

std::string ROOT::Internal::GetNormalizedUnresolvedTypeName(const std::string &origName)
{
const TClassEdit::EModType modType = static_cast<TClassEdit::EModType>(
Expand Down
4 changes: 4 additions & 0 deletions tree/ntuple/test/CustomStruct.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ struct alignas(std::uint64_t) TestEBO : public EmptyStruct {
template <typename T>
class EdmWrapper {
public:
struct Inner {
T fX;
};

bool fIsPresent = true;
T fMember;
};
Expand Down
16 changes: 16 additions & 0 deletions tree/ntuple/test/ntuple_type_name.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,19 @@ TEST(RNTuple, ContextDependentTypeNames)
}
}
}

TEST(RNTuple, NeedsMetaNameAsAlias)
{
using ROOT::Internal::NeedsMetaNameAsAlias;

std::string renormalizedAlias;
EXPECT_FALSE(NeedsMetaNameAsAlias("bool", renormalizedAlias));
EXPECT_FALSE(NeedsMetaNameAsAlias("std::vector<long>", renormalizedAlias));
EXPECT_FALSE(NeedsMetaNameAsAlias("std::vector<Long64_t>", renormalizedAlias));
EXPECT_TRUE(NeedsMetaNameAsAlias("::MyClass<Long64_t>", renormalizedAlias));
EXPECT_EQ("MyClass<Long64_t>", renormalizedAlias);
EXPECT_TRUE(NeedsMetaNameAsAlias("MyClass<ULong64_t>", renormalizedAlias));
EXPECT_TRUE(NeedsMetaNameAsAlias("std::vector<MyClass<Long64_t> >", renormalizedAlias));
EXPECT_EQ("std::vector<MyClass<Long64_t>>", renormalizedAlias);
EXPECT_FALSE(NeedsMetaNameAsAlias("MyClass<ROOT::RVec<Long64_t>>", renormalizedAlias));
}
8 changes: 8 additions & 0 deletions tree/ntuple/test/rfield_class.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,8 @@ TEST(RNTuple, StreamerInfoRecords)
{"DerivedA", {"DerivedA", "CustomStruct"}},
{"std::pair<CustomStruct, DerivedA>", {"DerivedA", "CustomStruct"}},
{"EdmWrapper<long long>", {"EdmWrapper<Long64_t>"}},
{"EdmContainer", {"EdmContainer", "EdmWrapper<Long64_t>"}},
{"EdmWrapper<long long>::Inner", {"EdmWrapper<Long64_t>::Inner"}},
{"TRotation", {"TRotation"}}};

for (const auto &t : testees) {
Expand Down Expand Up @@ -436,5 +438,11 @@ TEST(RNTuple, StreamerInfoRecords)
expectedInfos.erase(itr);
}
EXPECT_TRUE(expectedInfos.empty());

// Make sure we can reconstruct the fields
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
if (auto field = dynamic_cast<const ROOT::RClassField *>(&reader->GetModel().GetConstField("f"))) {
EXPECT_EQ(t.second[0], field->GetClass()->GetName());
}
}
}
Loading