Skip to content

Commit 0639f67

Browse files
committed
tree: add test for partial map read (#19963).
This directly test the use pattern applied by TTree::Scan/Draw
1 parent e0f3611 commit 0639f67

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed

roottest/root/tree/stl/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,10 @@ ROOTTEST_ADD_TEST(simple
4545
OUTREF simple.ref
4646
FIXTURES_REQUIRED root-tree-stl-copylogon-fixture root-tree-stl-runsimple-fixture)
4747

48+
ROOTTEST_COMPILE_MACRO(execPartialMap.cxx
49+
FIXTURES_SETUP root-tree-stl-execPartialMap-fixture)
50+
51+
ROOTTEST_ADD_TEST(PartialMap
52+
MACRO execPartialMap.cxx+
53+
OUTREF execPartialMap.ref
54+
FIXTURES_REQUIRED root-tree-stl-execPartialMap-fixture)
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// This is testing the issue reported at https://github.com/root-project/root/issues/19963
2+
3+
#include <iostream>
4+
#include <string>
5+
#include <map>
6+
#include <memory>
7+
8+
#include "Rtypes.h"
9+
#include "TBuffer.h"
10+
11+
class AddressKey {
12+
public:
13+
int id;
14+
std::string name;
15+
16+
AddressKey(int i = 0, const std::string &n = "") : id(i), name(n) {}
17+
18+
// Comparison operator for std::map
19+
bool operator<(const AddressKey &other) const { return (id < other.id) || (id == other.id && name < other.name); }
20+
21+
static int fgStreamerCalled;
22+
23+
ClassDef(AddressKey, 1) // Example class with custom Streamer
24+
};
25+
26+
inline void AddressKey::Streamer(TBuffer &b)
27+
{
28+
++fgStreamerCalled;
29+
// Default StreamerInfo-based ROOT I/O for a class with one int member
30+
// This is the standard pattern for custom Streamer functions
31+
if (b.IsReading()) {
32+
b.ReadClassBuffer(AddressKey::Class(), this);
33+
} else {
34+
b.WriteClassBuffer(AddressKey::Class(), this);
35+
}
36+
}
37+
38+
class AddressValue {
39+
public:
40+
int fData = -1;
41+
42+
static int fgStreamerCalled;
43+
44+
ClassDef(AddressValue, 1) // Example class with custom Streamer
45+
};
46+
47+
inline void AddressValue::Streamer(TBuffer &b)
48+
{
49+
++fgStreamerCalled;
50+
// Default StreamerInfo-based ROOT I/O for a class with one int member
51+
// This is the standard pattern for custom Streamer functions
52+
if (b.IsReading()) {
53+
b.ReadClassBuffer(AddressValue::Class(), this);
54+
} else {
55+
b.WriteClassBuffer(AddressValue::Class(), this);
56+
}
57+
}
58+
59+
// Static member definitions
60+
int AddressKey::fgStreamerCalled = 0;
61+
int AddressValue::fgStreamerCalled = 0;
62+
63+
#ifdef __ROOTCLING__
64+
#pragma link C++ class AddressKey-;
65+
#pragma link C++ class AddressValue-;
66+
#pragma link C++ class std::map<AddressKey,AddressValue>+;
67+
#pragma link C++ class std::pair<const AddressKey,AddressValue>+;
68+
#pragma link C++ class std::vector<std::pair<const AddressKey,AddressValue> >+;
69+
#endif
70+
71+
#include "TFile.h"
72+
#include "TTree.h"
73+
#include "TBranch.h"
74+
75+
TTree *CreateTree()
76+
{
77+
TTree *tree = new TTree("tree", "A tree with AddressPrinter objects");
78+
std::map<AddressKey, AddressValue> vec;
79+
tree->Branch("map", &vec);
80+
81+
// Fill with one element
82+
vec[0] = {AddressValue()};
83+
tree->Fill();
84+
85+
// Fill with two elements
86+
vec[1] = {AddressValue()};
87+
tree->Fill();
88+
89+
// Fill with one element again
90+
vec[2] = {AddressValue()};
91+
tree->Fill();
92+
93+
tree->ResetBranchAddresses();
94+
return tree;
95+
}
96+
97+
template <typename T>
98+
struct References;
99+
100+
template <>
101+
struct References<std::map<AddressKey, AddressValue>> {
102+
static constexpr std::array<int, 3> KeyCount = {1, 3, 6};
103+
static constexpr std::array<int, 3> ValueCount = {1, 3, 6};
104+
};
105+
106+
template <>
107+
struct References<std::vector<std::pair<const AddressKey, AddressValue>>> {
108+
static constexpr std::array<int, 3> KeyCount = {0, 0, 0};
109+
static constexpr std::array<int, 3> ValueCount = {1, 3, 6};
110+
};
111+
112+
template <typename Collection = std::map<AddressKey, AddressValue>>
113+
bool ReadTree(TTree *tree)
114+
{
115+
constexpr std::array<int, 3> expectedReadKeyCountValues = References<Collection>::KeyCount;
116+
constexpr std::array<int, 3> expectedReadValueCountValues = References<Collection>::ValueCount;
117+
118+
AddressKey::fgStreamerCalled = 0;
119+
AddressValue::fgStreamerCalled = 0;
120+
121+
bool result = true;
122+
123+
// std::vector<std::pair<const AddressKey,AddressValue> *map = nullptr;
124+
// std::map<AddressKey, AddressValue> *m = nullptr;
125+
Collection *m = nullptr;
126+
tree->SetBranchAddress("map", &m);
127+
128+
ULong64_t nentries = tree->GetEntries();
129+
for (ULong64_t i = 0; i < nentries; ++i) {
130+
std::cout << "Entry " << i << ":\n";
131+
// tree->GetEntry(i);
132+
tree->GetBranch("map")->TBranch::GetEntry(i);
133+
tree->GetBranch("map.second")->GetEntry(i);
134+
if (!m) {
135+
std::cerr << "ERROR: nullptr branch\n";
136+
continue;
137+
}
138+
std::cout << "AddressKey::Streamer called " << AddressKey::fgStreamerCalled << " times\n";
139+
std::cout << "AddressValue::Streamer called " << AddressValue::fgStreamerCalled << " times\n";
140+
int expectedKeyCount = (i < expectedReadKeyCountValues.size()) ? expectedReadKeyCountValues[i] : -1;
141+
if (AddressKey::fgStreamerCalled != expectedKeyCount) {
142+
std::cerr << "ERROR: AddressKey::fgStreamerCalled=" << AddressKey::fgStreamerCalled
143+
<< ", expected=" << expectedKeyCount << std::endl;
144+
result = false;
145+
}
146+
int expectedValueCount = (i < expectedReadValueCountValues.size()) ? expectedReadValueCountValues[i] : -1;
147+
if (AddressValue::fgStreamerCalled != expectedValueCount) {
148+
std::cerr << "ERROR: AddressValue::fgStreamerCalled=" << AddressValue::fgStreamerCalled
149+
<< ", expected=" << expectedValueCount << std::endl;
150+
result = false;
151+
}
152+
}
153+
tree->ResetBranchAddresses();
154+
delete m;
155+
return result;
156+
}
157+
158+
int readwrite()
159+
{
160+
std::cout << "Creating tree\n";
161+
std::unique_ptr<TFile> f(TFile::Open("objects.root", "RECREATE"));
162+
163+
TTree *tree = CreateTree();
164+
f->Write();
165+
std::cout << "Reading tree\n";
166+
auto result = ReadTree(tree);
167+
std::cout << "Deleting tree\n";
168+
delete tree;
169+
std::cout << "done\n";
170+
return result ? 0 : 1;
171+
}
172+
173+
template <typename Collection>
174+
int justread()
175+
{
176+
std::cout << "Open file\n";
177+
std::unique_ptr<TFile> f(TFile::Open("objects.root", "READ"));
178+
auto tree = f->Get<TTree>("tree");
179+
std::cout << "Reading tree\n";
180+
auto result = ReadTree<Collection>(tree);
181+
std::cout << "Deleting tree\n";
182+
delete tree;
183+
std::cout << "done\n";
184+
return result ? 0 : 2;
185+
}
186+
187+
int execPartialMap()
188+
{
189+
int ret1 = readwrite();
190+
AddressKey::fgStreamerCalled = 0;
191+
AddressValue::fgStreamerCalled = 0;
192+
int ret2 = justread<std::vector<std::pair<const AddressKey, AddressValue>>>();
193+
return ret1 + ret2;
194+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Automatic building of dictionaries now off
2+
3+
Processing /uscms_data/d2/pcanal/eaf_root_working/code/quick-devel/roottest/root/tree/stl/execPartialMap.cxx+...
4+
Creating tree
5+
Reading tree
6+
Entry 0:
7+
AddressKey::Streamer called 1 times
8+
AddressValue::Streamer called 1 times
9+
Entry 1:
10+
AddressKey::Streamer called 3 times
11+
AddressValue::Streamer called 3 times
12+
Entry 2:
13+
AddressKey::Streamer called 6 times
14+
AddressValue::Streamer called 6 times
15+
Deleting tree
16+
done
17+
Open file
18+
Reading tree
19+
Entry 0:
20+
AddressKey::Streamer called 0 times
21+
AddressValue::Streamer called 1 times
22+
Entry 1:
23+
AddressKey::Streamer called 0 times
24+
AddressValue::Streamer called 3 times
25+
Entry 2:
26+
AddressKey::Streamer called 0 times
27+
AddressValue::Streamer called 6 times
28+
Deleting tree
29+
done
30+
(int) 0

0 commit comments

Comments
 (0)