Skip to content

Commit

Permalink
[treereader] Allow reading Foo<Double32_t> into Foo<double>:
Browse files Browse the repository at this point in the history
As the TClass corresponding to the template parameter of TTreeReaderValue is
found through the typeid => TClass map, make sure that we ignore mismatches
of `Foo<Double32_t>` vs `Foo<double>` - they should not be relevant for reading.

This fixes reading a LorentzVector<Double32_t>.

Fixes root-project#12334
  • Loading branch information
Axel-Naumann committed Jun 22, 2023
1 parent 71674a5 commit bc09cd6
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 5 deletions.
24 changes: 21 additions & 3 deletions core/meta/src/TClass.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -492,14 +492,32 @@ DeclIdMap_t *TClass::GetDeclIdMap() {
namespace {

////////////////////////////////////////////////////////////////////////////////
/// Count the number of occurrences of needle in haystack.
/// Check whether c is a character that can be part of an identifier.
bool isIdentifierChar(char c) {
return isalnum(c) || c == '_';
}

////////////////////////////////////////////////////////////////////////////////
/// Count the number of occurrences of needle in typename haystack.

static int CountStringOccurrences(TString needle, TString haystack) {
static int CountStringOccurrences(const TString &needle, const TString &haystack) {
Ssiz_t currStart = 0;
int numOccurrences = 0;
Ssiz_t posFound = haystack.Index(needle, currStart);
while (posFound != TString::kNPOS) {
++numOccurrences;
// Ensure it's neither FooNeedle nor NeedleFoo, but Needle is surrounded
// by delimiters:
auto hasDelimLeft = [&]() {
return posFound == 0
|| !isIdentifierChar(haystack[posFound - 1]);
};
auto hasDelimRight = [&]() {
return posFound + needle.Length() == haystack.Length()
|| !isIdentifierChar(haystack[posFound + needle.Length()]);
};

if (hasDelimLeft() && hasDelimRight())
++numOccurrences;
currStart = posFound + needle.Length();
posFound = haystack.Index(needle, currStart);
}
Expand Down
3 changes: 2 additions & 1 deletion tree/treeplayer/src/TTreeReaderValue.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,9 @@ void ROOT::Internal::TTreeReaderValueBase::CreateProxy() {
auto dictAsClass = dynamic_cast<TClass*>(fDict);
auto branchActualTypeAsClass = dynamic_cast<TClass*>(branchActualType);
auto inheritance = dictAsClass && branchActualTypeAsClass && branchActualTypeAsClass->InheritsFrom(dictAsClass);
bool typeinfoMatch = dictAsClass && dictAsClass->GetTypeInfo() && dictAsClass->GetTypeInfo() == branchActualTypeAsClass->GetTypeInfo();

if (fDict != branchActualType && !inheritance) {
if (!inheritance && !typeinfoMatch && fDict != branchActualType) {
TDataType *dictdt = dynamic_cast<TDataType*>(fDict);
TDataType *actualdt = dynamic_cast<TDataType*>(branchActualType);
TEnum *dictenum = dynamic_cast<TEnum*>(fDict);
Expand Down
3 changes: 2 additions & 1 deletion tree/treeplayer/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
if(NOT MSVC OR win_broken_tests)
ROOT_ADD_UNITTEST_DIR(TreePlayer)
add_custom_command(TARGET treetreeplayertestUnit POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/data.h data.h)
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/data.h data.h
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/branchobject.root branchobject.root)
endif()

if(imt)
Expand Down
35 changes: 35 additions & 0 deletions tree/treeplayer/test/branchobject.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <TTreeReader.h>
#include <TTreeReaderValue.h>
#include <TH1D.h>
#include <TInterpreter.h>

#include "gtest/gtest.h"

Expand Down Expand Up @@ -38,3 +39,37 @@ TEST(TTreeReaderBasic, BranchObject)
EXPECT_STREQ(rv->ClassName(), "TH1D");
EXPECT_DOUBLE_EQ(static_cast<TH1D *>(rv.Get())->GetMean(), 42.);
}

// Issue #12334
// branchobject.root created as
//
// ~~~ {.cpp}
// f = new TFile("branchobject.root", "RECREATE");
// TTree *t = new TTree("branchobject", "test tree for branchobject.cxx");
// ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<Double32_t>> lv(1., 2., 3., 4.);
// t->Branch("lv32", &lv);
// t->Fill();
// t->Write();
// ~~~
TEST(TTreeReaderBasic, LorentzVector32)
{
// Ensure that the mismatching `<double>` specialization is available, i.e. will
// be chosen given the typeid of the TTreeReaderValue template argument.
TClass::GetClass("ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >");

TFile file("branchobject.root");
TTreeReader reader("branchobject");
std::string code;
{
std::stringstream sstr;
sstr << "TTreeReaderValue<ROOT::Math::PtEtaPhiMVector> lv32(*(TTreeReader*)"
<< &reader << ", \"lv32\");";
code = sstr.str();
}
gInterpreter->Declare(code.c_str());
ASSERT_TRUE(reader.Next());
EXPECT_EQ(gInterpreter->Calc("int(lv32->pt() + 0.5)"), 1);
EXPECT_EQ(gInterpreter->Calc("int(lv32->eta() + 0.5)"), 2);
EXPECT_EQ(gInterpreter->Calc("int(lv32->phi() + 0.5)"), 3);
EXPECT_EQ(gInterpreter->Calc("int(lv32->M() + 0.5)"), 4);
}
Binary file added tree/treeplayer/test/branchobject.root
Binary file not shown.

0 comments on commit bc09cd6

Please sign in to comment.