Skip to content
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
15 changes: 11 additions & 4 deletions tree/ntuple/inc/ROOT/RField.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -355,17 +355,24 @@ public:

template <typename T>
class RSimpleField : public RFieldBase {
void ReconcileIntegralField(const RNTupleDescriptor &desc);
void ReconcileFloatingPointField(const RNTupleDescriptor &desc);

protected:
void GenerateColumns() override { GenerateColumnsImpl<T>(); }
void GenerateColumns(const ROOT::RNTupleDescriptor &desc) override { GenerateColumnsImpl<T>(desc); }

void ConstructValue(void *where) const final { new (where) T{0}; }

void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
{
// Differences in the type name don't matter for simple fields; the valid column representations take
// care of (allowed) schema differences.
EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName);
if constexpr (std::is_integral_v<T>) {
ReconcileIntegralField(desc);
} else if constexpr (std::is_floating_point_v<T>) {
ReconcileFloatingPointField(desc);
} else {
RFieldBase::ReconcileOnDiskField(desc);
}
}

RSimpleField(std::string_view name, std::string_view type)
Expand Down
4 changes: 4 additions & 0 deletions tree/ntuple/inc/ROOT/RNTupleDescriptor.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ public:
/// natively supported stdlib classes.
/// The dictionary does not need to be available for this method.
bool IsCustomClass() const;
/// Tells if the field describes a user-defined enum type.
/// The dictionary does not need to be available for this method.
/// Needs the full descriptor to look up sub fields.
bool IsCustomEnum(const RNTupleDescriptor &desc) const;
};

// clang-format off
Expand Down
35 changes: 35 additions & 0 deletions tree/ntuple/src/RField.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,41 @@ const ROOT::RField<ROOT::RNTupleCardinality<std::uint64_t>> *ROOT::RCardinalityF

//------------------------------------------------------------------------------

template <typename T>
void ROOT::RSimpleField<T>::ReconcileIntegralField(const RNTupleDescriptor &desc)
{
const RFieldDescriptor &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);

if (fieldDesc.IsCustomEnum(desc)) {
SetOnDiskId(desc.FindFieldId("_0", GetOnDiskId()));
return;
}

static const std::string gIntegralTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
"std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
"std::int64_t", "std::uint64_t"};
if (std::find(std::begin(gIntegralTypeNames), std::end(gIntegralTypeNames), fieldDesc.GetTypeName()) ==
std::end(gIntegralTypeNames)) {
throw RException(R__FAIL("unexpected on-disk type name '" + fieldDesc.GetTypeName() + "' for field of type '" +
GetTypeName() + "'"));
}
}

template <typename T>
void ROOT::RSimpleField<T>::ReconcileFloatingPointField(const RNTupleDescriptor &desc)
{
const RFieldDescriptor &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);

if (!(fieldDesc.GetTypeName() == "float" || fieldDesc.GetTypeName() == "double")) {
throw RException(R__FAIL("unexpected on-disk type name '" + fieldDesc.GetTypeName() + "' for field of type '" +
GetTypeName() + "'"));
}
}

//------------------------------------------------------------------------------

template class ROOT::RSimpleField<char>;

const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<char>::GetColumnRepresentations() const
Expand Down
18 changes: 18 additions & 0 deletions tree/ntuple/src/RNTupleDescriptor.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,24 @@ bool ROOT::RFieldDescriptor::IsCustomClass() const
return true;
}

bool ROOT::RFieldDescriptor::IsCustomEnum(const RNTupleDescriptor &desc) const
{
if (fStructure != ROOT::ENTupleStructure::kPlain)
return false;
if (fTypeName.rfind("std::", 0) == 0)
return false;

auto subFieldId = desc.FindFieldId("_0", fFieldId);
if (subFieldId == kInvalidDescriptorId)
return false;

static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
"std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
"std::int64_t", "std::uint64_t"};
return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
}

////////////////////////////////////////////////////////////////////////////////

bool ROOT::RColumnDescriptor::operator==(const RColumnDescriptor &other) const
Expand Down
22 changes: 22 additions & 0 deletions tree/ntuple/test/ntuple_evolution_type.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,25 @@ TEST(RNTupleEvolution, CheckPairTuple)
EXPECT_EQ(1, std::get<0>(p(0)));
EXPECT_DOUBLE_EQ(2.0, std::get<1>(p(0)));
}

TEST(RNTupleEvolution, Enum)
{
FileRaii fileGuard("test_ntuple_evolution_check_pair_tuple.root");
{
auto model = ROOT::RNTupleModel::Create();
auto e1 = model->MakeField<CustomEnumInt8>("e1");
auto e2 = model->MakeField<CustomEnum>("e2");
auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());

*e1 = static_cast<CustomEnumInt8>(42);
*e2 = static_cast<CustomEnum>(137);

writer->Fill();
}

auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
auto ve1 = reader->GetView<int>("e1");
auto ve2 = reader->GetView<int>("e2");
EXPECT_EQ(42, ve1(0));
EXPECT_EQ(137, ve2(0));
}
3 changes: 1 addition & 2 deletions tree/ntuple/test/ntuple_types.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1669,8 +1669,7 @@ TEST(RNTuple, Casting)
auto reader = RNTupleReader::Open(std::move(modelB), "ntuple", fileGuard.GetPath());
FAIL() << "should not be able to cast int to float";
} catch (const ROOT::RException &err) {
EXPECT_THAT(err.what(), testing::HasSubstr("On-disk column types"));
EXPECT_THAT(err.what(), testing::HasSubstr("cannot be matched"));
EXPECT_THAT(err.what(), testing::HasSubstr("unexpected on-disk type name"));
}

auto modelC = RNTupleModel::Create();
Expand Down
3 changes: 1 addition & 2 deletions tree/ntuple/test/ntuple_view.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,6 @@ TEST(RNTuple, VoidWithExternalAddressAndTypeName)
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`"));
EXPECT_THAT(err.what(), testing::HasSubstr("unexpected on-disk type"));
}
}
Loading