Skip to content
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

[ntuple] Fix RClassField not resolving context-dependent typedefs #17783

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
26 changes: 21 additions & 5 deletions tree/ntuple/v7/src/RFieldMeta.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -151,16 +151,32 @@ ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, TClass
continue;
}

std::string typeName{dataMember->GetFullTypeName()};
// NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
// context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
// the string returned by dataMember->GetFullTypeName())
std::string typeName{dataMember->GetTrueTypeName()};
// RFieldBase::Create() set subField->fTypeAlias based on the assumption that the user specified typeName, which
// already went through one round of type resolution.
std::string origTypeName{dataMember->GetFullTypeName()};

// For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
if (dataMember->Property() & kIsArray) {
for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim)
typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
const auto addedStr = "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
typeName += addedStr;
origTypeName += addedStr;
}
}

std::unique_ptr<RFieldBase> subField;
auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();

const auto normTypeName = Internal::GetNormalizedUnresolvedTypeName(origTypeName);
if (normTypeName == subField->GetTypeName()) {
subField->fTypeAlias = "";
} else {
subField->fTypeAlias = normTypeName;
}

subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
fTraits &= subField->GetTraits();
Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
}
Expand Down
7 changes: 7 additions & 0 deletions tree/ntuple/v7/test/CustomStruct.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ enum class CustomEnumInt64 : long int {};
enum class CustomEnumUInt64 : unsigned long int {};

struct CustomStruct {
template <typename T>
using MyVec = std::vector<T>;

float a = 0.0;
std::vector<float> v1;
std::vector<std::vector<float>> v2;
Expand All @@ -57,6 +60,10 @@ struct DerivedA2 : public CustomStruct {
float a2_f{};
};

struct DerivedWithTypedef : public CustomStruct {
MyVec<int> m;
};

struct DerivedB : public DerivedA {
float b_f1 = 0.0;
float b_f2 = 0.0; //!
Expand Down
1 change: 1 addition & 0 deletions tree/ntuple/v7/test/CustomStructLinkDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#pragma link C++ class CustomStruct+;
#pragma link C++ class DerivedA+;
#pragma link C++ class DerivedA2+;
#pragma link C++ class DerivedWithTypedef + ;
#pragma link C++ class DerivedB+;
#pragma link C++ class DerivedC+;
#pragma link C++ class StructWithArrays + ;
Expand Down
62 changes: 62 additions & 0 deletions tree/ntuple/v7/test/ntuple_type_name.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,65 @@ TEST(RNTuple, TemplateArgIntegerNormalization)
EXPECT_THROW(RFieldBase::Create("f", "IntegerTemplates<-1u,0u>").Unwrap(), ROOT::RException);
EXPECT_THROW(RFieldBase::Create("f", "IntegerTemplates<1u,0x>").Unwrap(), ROOT::RException);
}

TEST(RNTuple, ContextDependentTypes)
{
// Adapted from https://gitlab.cern.ch/amete/rntuple-bug-report-20250219, reproducer of
// https://github.com/root-project/root/issues/17774

FileRaii fileGuard("test_ntuple_types_contextdep.root");

{
auto model = RNTupleModel::Create();
auto fieldBase = RFieldBase::Create("foo", "DerivedWithTypedef").Unwrap();
model->AddField(std::move(fieldBase));
auto ntuple = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
auto entry = ntuple->GetModel().CreateBareEntry();
auto ptr = std::make_unique<DerivedWithTypedef>();
entry->BindRawPtr("foo", ptr.get());
for (auto i = 0; i < 10; ++i) {
ptr->m.push_back(i);
ntuple->Fill(*entry);
ptr->m.clear();
}
}

{
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
EXPECT_EQ(reader->GetNEntries(), 10);

const auto &desc = reader->GetDescriptor();
const auto fooId = desc.FindFieldId("foo");
const auto baseId = desc.GetFieldDescriptor(fooId).GetLinkIds()[0];
{
const auto &fdesc = desc.GetFieldDescriptor(desc.FindFieldId("m", fooId));
EXPECT_EQ(fdesc.GetTypeName(), "std::vector<std::int32_t>");
EXPECT_EQ(fdesc.GetTypeAlias(), "MyVec<std::int32_t>");
}
{
const auto &fdesc = desc.GetFieldDescriptor(desc.FindFieldId("a", baseId));
EXPECT_EQ(fdesc.GetTypeName(), "float");
EXPECT_EQ(fdesc.GetTypeAlias(), "");
}
{
const auto &fdesc = desc.GetFieldDescriptor(desc.FindFieldId("v1", baseId));
EXPECT_EQ(fdesc.GetTypeName(), "std::vector<float>");
EXPECT_EQ(fdesc.GetTypeAlias(), "");
}
{
const auto &fdesc = desc.GetFieldDescriptor(desc.FindFieldId("v2", baseId));
EXPECT_EQ(fdesc.GetTypeName(), "std::vector<std::vector<float>>");
EXPECT_EQ(fdesc.GetTypeAlias(), "");
}
{
const auto &fdesc = desc.GetFieldDescriptor(desc.FindFieldId("s", baseId));
EXPECT_EQ(fdesc.GetTypeName(), "std::string");
EXPECT_EQ(fdesc.GetTypeAlias(), "");
}
{
const auto &fdesc = desc.GetFieldDescriptor(desc.FindFieldId("b", baseId));
EXPECT_EQ(fdesc.GetTypeName(), "std::byte");
EXPECT_EQ(fdesc.GetTypeAlias(), "");
}
}
}
32 changes: 15 additions & 17 deletions tree/ntuple/v7/test/ntuple_types.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ TEST(RNTuple, ArrayField)
auto array1_field = ntuple->GetModel().GetDefaultEntry().GetPtr<float[2]>("array1");
auto array2_field = ntuple->GetModel().GetDefaultEntry().GetPtr<unsigned char[4]>("array2");
for (int i = 0; i < 2; i++) {
new (struct_field.get()) StructWithArrays({{'n', 't', 'p', 'l'}, {1.0, 42.0}, {{2*i}, {2*i + 1}}});
new (struct_field.get()) StructWithArrays({{'n', 't', 'p', 'l'}, {1.0, 42.0}, {{2 * i}, {2 * i + 1}}});
new (array1_field.get()) float[2]{0.0f, static_cast<float>(i)};
memcpy(array2_field.get(), charArray, sizeof(charArray));
ntuple->Fill();
Expand All @@ -167,8 +167,8 @@ TEST(RNTuple, ArrayField)
EXPECT_EQ(0, memcmp(viewStruct(i).c, "ntpl", 4));
EXPECT_EQ(1.0f, viewStruct(i).f[0]);
EXPECT_EQ(42.0f, viewStruct(i).f[1]);
EXPECT_EQ(2*i, viewStruct(i).i[0][0]);
EXPECT_EQ(2*i + 1, viewStruct(i).i[1][0]);
EXPECT_EQ(2 * i, viewStruct(i).i[0][0]);
EXPECT_EQ(2 * i + 1, viewStruct(i).i[1][0]);

float fs[] = {0.0f, static_cast<float>(i)};
EXPECT_EQ(0, memcmp(viewArray1(i), fs, sizeof(fs)));
Expand Down Expand Up @@ -235,17 +235,15 @@ TEST(RNTuple, StdPair)
EXPECT_EQ((alignof(std::pair<int64_t, float>)), field.GetAlignment());
EXPECT_EQ((alignof(std::pair<int64_t, float>)), otherField->GetAlignment());

auto pairPairField = RField<std::pair<std::pair<int64_t, float>,
std::vector<std::pair<CustomStruct, double>>>>("pairPairField");
auto pairPairField =
RField<std::pair<std::pair<int64_t, float>, std::vector<std::pair<CustomStruct, double>>>>("pairPairField");
EXPECT_STREQ("std::pair<std::pair<std::int64_t,float>,std::vector<std::pair<CustomStruct,double>>>",
pairPairField.GetTypeName().c_str());

FileRaii fileGuard("test_ntuple_rfield_stdpair.root");
{
auto model = RNTupleModel::Create();
auto pair_field = model->MakeField<std::pair<double, std::string>>(
{"myPair", "a very cool field"}
);
auto pair_field = model->MakeField<std::pair<double, std::string>>({"myPair", "a very cool field"});
auto myPair2 = RFieldBase::Create("myPair2", "std::pair<double, std::string>").Unwrap();
model->AddField(std::move(myPair2));

Expand Down Expand Up @@ -2029,13 +2027,14 @@ TEST(RNTuple, TClassStlDerived)
RNTupleWriteOptions options;
auto ntuple = RNTupleWriter::Recreate(std::move(model), "f", fileGuard.GetPath(), options);
for (int i = 0; i < 10000; i++) {
new (fieldKlass.get()) PackedContainer<int>({i + 2, i + 3},
{/*m_nbits=*/ (uint8_t)i,
/*m_nmantissa=*/ (uint8_t)i,
/*m_scale=*/ static_cast<float>(i + 1),
/*m_flags=*/ 0,
/*m_sgkey=*/ (uint32_t)(i + 1),
/*c_uint=*/ (uint8_t)i});
if (fieldKlass)
fieldKlass->~PackedContainer<int>();
new (fieldKlass.get()) PackedContainer<int>({i + 2, i + 3}, {/*m_nbits=*/(uint8_t)i,
/*m_nmantissa=*/(uint8_t)i,
/*m_scale=*/static_cast<float>(i + 1),
/*m_flags=*/0,
/*m_sgkey=*/(uint32_t)(i + 1),
/*c_uint=*/(uint8_t)i});
ntuple->Fill();
}
}
Expand All @@ -2052,8 +2051,7 @@ TEST(RNTuple, TClassStlDerived)
EXPECT_EQ(((uint8_t)i), viewKlass(i).m_params.c_uint);
EXPECT_EQ(((uint32_t)(i + 1)), viewKlass(i).m_params.m_sgkey);

EXPECT_EQ((std::vector<int>{static_cast<int>(i + 2),
static_cast<int>(i + 3)}), viewKlass(i));
EXPECT_EQ((std::vector<int>{static_cast<int>(i + 2), static_cast<int>(i + 3)}), viewKlass(i));
}
}

Expand Down
Loading