EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dfe_io_root.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file dfe_io_root.hpp
1 // SPDX-License-Identifier: MIT
2 // Copyright 2019 Moritz Kiehn
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 // SOFTWARE.
21 
26 
27 #pragma once
28 
29 #include <array>
30 #include <stdexcept>
31 #include <string>
32 #include <tuple>
33 #include <type_traits>
34 
35 #include <TFile.h>
36 #include <TTree.h>
37 
38 namespace dfe {
39 
41 template<typename NamedTuple>
43 public:
44  NamedTupleRootWriter() = delete;
49 
54  NamedTupleRootWriter(const std::string& path, const std::string& tree_name);
62  NamedTupleRootWriter(TDirectory* dir, const std::string& tree_name);
65 
67  void append(const NamedTuple& record);
68 
69 private:
70  // the equivalent std::tuple-like type
71  using Tuple = typename NamedTuple::Tuple;
72 
73  TFile* m_file;
74  TTree* m_tree;
76 
77  template<std::size_t... I>
78  void setup_branches(std::index_sequence<I...>);
79 };
80 
82 template<typename NamedTuple>
84 public:
85  NamedTupleRootReader() = delete;
90 
95  NamedTupleRootReader(const std::string& path, const std::string& tree_name);
103  NamedTupleRootReader(TDirectory* dir, const std::string& tree_name);
106 
111  bool read(NamedTuple& record);
112 
113 private:
114  // the equivalent std::tuple-like type
115  using Tuple = typename NamedTuple::Tuple;
116 
117  TFile* m_file;
118  TTree* m_tree;
119  int64_t m_next;
121 
122  template<std::size_t... I>
123  void setup_branches(std::index_sequence<I...>);
124 };
125 
126 // implementation writer
127 
128 template<typename NamedTuple>
130  const std::string& path, const std::string& tree_name)
131  : m_file(new TFile(path.c_str(), "RECREATE"))
132  , m_tree(new TTree(tree_name.c_str(), "", 99, m_file)) {
133  if (not m_file) {
134  throw std::runtime_error("Could not create file");
135  }
136  if (not m_file->IsOpen()) {
137  throw std::runtime_error("Could not open file");
138  }
139  if (not m_tree) {
140  throw std::runtime_error("Could not create tree");
141  }
142  setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
143 }
144 
145 template<typename NamedTuple>
147  TDirectory* dir, const std::string& tree_name)
148  : m_file(nullptr) // no file since it is not owned by the writer
149  , m_tree(new TTree(tree_name.c_str(), "", 99, dir)) {
150  if (not dir) {
151  throw std::runtime_error("Invalid output directory given");
152  }
153  if (not m_tree) {
154  throw std::runtime_error("Could not create tree");
155  }
156  setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
157 }
158 
159 namespace namedtuple_root_impl {
160 
161 // see: https://cpppatterns.com/patterns/class-template-sfinae.html
162 template<typename T, typename Enable = void>
163 struct TypeCode;
164 // non-integer types
165 template<char Code>
167  static constexpr char value = Code;
168 };
169 template<>
170 struct TypeCode<bool> : TypeCodePlainImpl<'O'> {};
171 template<>
172 struct TypeCode<float> : TypeCodePlainImpl<'F'> {};
173 template<>
174 struct TypeCode<double> : TypeCodePlainImpl<'D'> {};
175 // integer types
176 // you might think that you could just define this for all the stdint types;
177 // but no, this breaks because ROOT [U]Long64_t might not be the same type as
178 // [u]int64_t depending on the machine, os, moon phase, ... .
179 // Why you ask? Because the universe hates you.
180 template<typename T, char Unsigned, char Signed>
182  static constexpr char value = std::is_unsigned<T>::value ? Unsigned : Signed;
183 };
184 template<typename T, std::size_t S>
186  and (sizeof(T) == S);
187 template<typename T>
188 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 1>>>
189  : TypeCodeIntImpl<T, 'b', 'B'> {};
190 template<typename T>
191 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 2>>>
192  : TypeCodeIntImpl<T, 's', 'S'> {};
193 template<typename T>
194 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 4>>>
195  : TypeCodeIntImpl<T, 'i', 'I'> {};
196 template<typename T>
197 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 8>>>
198  : TypeCodeIntImpl<T, 'l', 'L'> {};
199 
200 } // namespace namedtuple_root_impl
201 
202 template<typename NamedTuple>
203 template<std::size_t... I>
204 inline void
206  static_assert(
207  sizeof...(I) == std::tuple_size<Tuple>::value, "Something is very wrong");
208 
209  // construct leaf names w/ type info
210  std::array<std::string, sizeof...(I)> names = NamedTuple::names();
211  std::array<std::string, sizeof...(I)> leafs = {
212  (names[I] + '/'
213  + namedtuple_root_impl::TypeCode<
214  std::tuple_element_t<I, Tuple>>::value)...};
215  // construct branches
216  // NOTE 2019-05-13 msmk:
217  // the documentation suggests that ROOT can figure out the branch types on
218  // its own, but doing so seems to break for {u}int64_t. do it manually for
219  // now.
220  (void)std::array<TBranch*, sizeof...(I)>{m_tree->Branch(
221  names[I].c_str(), &std::get<I>(m_data), leafs[I].c_str())...};
222 }
223 
224 template<typename NamedTuple>
226  // alway overwrite old data
227  if (m_tree) {
228  m_tree->Write(nullptr, TObject::kOverwrite);
229  }
230  // writer owns the file
231  if (m_file) {
232  m_file->Close();
233  delete m_file;
234  }
235 }
236 
237 template<typename NamedTuple>
238 inline void
239 NamedTupleRootWriter<NamedTuple>::append(const NamedTuple& record) {
240  m_data = record;
241  if (m_tree->Fill() == -1) {
242  throw std::runtime_error("Could not fill an entry");
243  }
244 }
245 
246 // implementation reader
247 
248 template<typename NamedTuple>
250  const std::string& path, const std::string& tree_name)
251  : m_file(new TFile(path.c_str(), "READ")), m_tree(nullptr), m_next(0) {
252  if (not m_file) {
253  throw std::runtime_error("Could not open file");
254  }
255  if (not m_file->IsOpen()) {
256  throw std::runtime_error("Could not open file");
257  }
258  m_tree = static_cast<TTree*>(m_file->Get(tree_name.c_str()));
259  if (not m_tree) {
260  throw std::runtime_error("Could not read tree");
261  }
262  setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
263 }
264 
265 template<typename NamedTuple>
267  TDirectory* dir, const std::string& tree_name)
268  : m_file(nullptr) // no file since it is not owned by the writer
269  , m_tree(nullptr)
270  , m_next(0) {
271  if (not dir) {
272  throw std::runtime_error("Invalid input directory given");
273  }
274  m_tree = static_cast<TTree*>(dir->Get(tree_name.c_str()));
275  if (not m_tree) {
276  throw std::runtime_error("Could not read tree");
277  }
278  setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
279 }
280 
281 namespace io_root_impl {
282 
283 // WARNING this is a hack to get around inconsistent ROOT types for 8bit chars
284 // and 64bit intengers compared to the stdint types.
285 __attribute__((unused)) inline ULong64_t*
286 get_address(uint64_t& x) {
287  static_assert(
288  sizeof(ULong64_t) == sizeof(uint64_t), "Inconsistent type sizes");
289  return reinterpret_cast<ULong64_t*>(&x);
290 }
291 __attribute__((unused)) inline char*
292 get_address(int8_t& x) {
293  static_assert(sizeof(char) == sizeof(int8_t), "Inconsistent type sizes");
294  return reinterpret_cast<char*>(&x);
295 }
296 __attribute__((unused)) inline Long64_t*
297 get_address(int64_t& x) {
298  static_assert(sizeof(Long64_t) == sizeof(int64_t), "Inconsistent type sizes");
299  return reinterpret_cast<Long64_t*>(&x);
300 }
301 template<typename T>
302 inline T*
304  return &x;
305 }
306 
307 } // namespace io_root_impl
308 
309 template<typename NamedTuple>
310 template<std::size_t... I>
311 inline void
313  static_assert(
314  sizeof...(I) == std::tuple_size<Tuple>::value, "Something is very wrong");
315 
316  using std::get;
317 
318  // construct leaf names w/ type info
319  std::array<std::string, sizeof...(I)> names = NamedTuple::names();
320  // construct branches
321  (void)std::array<Int_t, sizeof...(I)>{m_tree->SetBranchAddress(
322  names[I].c_str(), io_root_impl::get_address(get<I>(m_data)))...};
323 }
324 
325 template<typename NamedTuple>
327  // reader owns the file
328  if (m_file) {
329  m_file->Close();
330  delete m_file;
331  }
332 }
333 
334 template<typename NamedTuple>
335 inline bool
337  auto ret = m_tree->GetEntry(m_next);
338  // i/o error occured
339  if (ret < 0) {
340  throw std::runtime_error("Could not read entry");
341  }
342  // the entry does not exist, probably end-of-file reached
343  if (ret == 0) {
344  return false;
345  }
346  // GetEntry(...) has already filled the local buffer
347  record = m_data;
348  m_next += 1;
349  return true;
350 }
351 
352 } // namespace dfe