Project
Loading...
Searching...
No Matches
RootTreeWriter.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11#ifndef FRAMEWORK_ROOTTREEWRITER_H
12#define FRAMEWORK_ROOTTREEWRITER_H
13
18
21#include "Framework/DataRef.h"
22#include "Framework/Logger.h"
23#include <TFile.h>
24#include <TTree.h>
25#include <TBranch.h>
26#include <TClass.h>
27#include <vector>
28#include <functional>
29#include <string>
30#include <stdexcept> // std::runtime_error
31#include <type_traits>
32#include <typeinfo>
33#include <memory> // std::make_unique
34#include <functional> // std::function
35#include <utility> // std::forward
36#include <algorithm> // std::generate
37#include <variant>
38
39namespace o2
40{
41namespace framework
42{
43
46template <typename T>
48 using type = void;
49};
50
53template <typename Ret, typename Class, typename... Args>
54struct get_function<Ret (Class::*)(Args...) const> {
55 using type = std::function<Ret(Args...)>;
56};
57
60template <typename From, typename To, typename _ = void>
61struct can_assign : public std::false_type {
62};
63
67template <typename From, typename To>
69 From, To,
70 std::conditional_t<
71 false,
72 class_member_checker<
73 decltype(&From::operator())>,
74 void>> : public std::is_same<typename get_function<decltype(&From::operator())>::type, To> {
75};
76
121{
122 public:
123 // use string as input binding to be used with DPL input API
124 using key_type = std::string;
125 // extract the branch index from DataRef, ~(size_t)) indicates "no data", then nothing
126 // is extracted from input and writing is skipped
127 using IndexExtractor = std::function<size_t(o2::framework::DataRef const&)>;
128 // mapper between branch name and base/index
129 using BranchNameMapper = std::function<std::string(std::string, size_t)>;
130 // a custom callback to close the writer
131 // writing of tree and closing of file need to be implemented, but the pointers
132 // remain owned by the tool
133 using CustomClose = std::function<void(TFile* file, TTree* tree)>;
134
138 template <typename T>
139 static key_type asString(T const& arg)
140 {
141 return arg;
142 }
143 };
144
145 enum struct BranchVerbosity {
146 Quiet,
147 Medium,
148 High,
149 };
150
153
181 template <typename T, typename KeyType = key_type, typename KeyExtractor = DefaultKeyExtractor>
182 struct BranchDef {
183 using type = T;
184 using key_type = KeyType;
185 using key_extractor = KeyExtractor;
186 std::vector<key_type> keys;
187 std::string branchName;
189 size_t nofBranches = 1;
194
195 using Fill = std::function<void(TBranch& branch, T const&)>;
196 using FillExt = std::function<void(TBranch& branch, T const&, DataRef const&)>;
197 using Spectator = std::function<void(T const&)>;
198 using SpectatorExt = std::function<void(T const&, DataRef const&)>;
199 using BranchCallback = std::variant<std::monostate, Fill, FillExt, Spectator, SpectatorExt>;
201
207 BranchDef(key_type key, std::string _branchName, size_t _nofBranches = 1) : keys({key}), branchName(_branchName), nofBranches(_nofBranches) {}
208
219 template <typename... Args>
220 BranchDef(key_type key, std::string _branchName, Args&&... args) : keys({key}), branchName(_branchName), nofBranches(1)
221 {
222 init(std::forward<Args>(args)...);
223 }
224
235 template <typename... Args>
236 BranchDef(std::vector<key_type> vec, std::string _branchName, Args&&... args) : keys(vec), branchName(_branchName), nofBranches(1)
237 {
238 init(std::forward<Args>(args)...);
239 }
240
242 template <typename Arg, typename... Args>
243 void init(Arg&& arg, Args&&... args)
244 {
245 using Type = std::decay_t<Arg>;
247 getIndex = arg;
248 } else if constexpr (can_assign<Type, BranchNameMapper>::value) {
249 getName = arg;
250 } else if constexpr (can_assign<Type, Fill>::value) {
251 callback = arg;
252 } else if constexpr (can_assign<Type, FillExt>::value) {
253 callback = arg;
254 } else if constexpr (can_assign<Type, Spectator>::value) {
255 callback = arg;
256 } else if constexpr (can_assign<Type, SpectatorExt>::value) {
257 callback = arg;
258 } else if constexpr (std::is_integral<Type>::value) {
259 nofBranches = arg;
260 } else {
261 assertNoMatchingType(std::forward<Arg>(arg));
262 }
263 if constexpr (sizeof...(args) > 0) {
264 init(std::forward<Args>(args)...);
265 }
266 }
267
268 // wrap the non matching type assert into a function to better see type of argument
269 // in the compiler error
270 template <typename Arg>
271 void assertNoMatchingType(Arg&& arg)
272 {
273 static_assert(always_static_assert_v<Arg>, "no matching function signature for passed object. Please check:\n- Is it a callable object?\n- Does it have correct parameters and return type?\n- Are all type correctly qualified");
274 }
275 };
276
278 RootTreeWriter() = delete;
279
282 {
283 close();
284 }
285
290 template <typename... Args>
291 RootTreeWriter(const char* filename, // file name
292 const char* treename, // name of the tree to write to
293 Args&&... args) // followed by branch definition
294 {
295 parseConstructorArgs(std::forward<Args>(args)...);
296 if (!mTreeStructure) {
297 std::runtime_error("Failed to create the branch configuration");
298 }
299 if (filename && treename) {
300 init(filename, treename);
301 }
302 }
303
310 void init(const char* filename, const char* treename, const char* treetitle = nullptr)
311 {
312 mFile = std::make_unique<TFile>(filename, "RECREATE", "", 505);
313 mTree = std::make_unique<TTree>(treename, treetitle != nullptr ? treetitle : treename);
314 mTree->SetDirectory(mFile.get());
315 mTreeStructure->setup(mBranchSpecs, mTree.get());
316 }
317
324 void setBranchName(size_t index, const char* branchName)
325 {
326 auto& spec = mBranchSpecs.at(index);
327 if (spec.getName) {
328 // set the branch names for this group, we also amend if there is only one branch but
329 // the callback is configured
330 size_t idx = 0;
331 std::generate(spec.names.begin(), spec.names.end(), [&]() { return spec.getName(branchName, idx++); });
332 } else {
333 // single branch for this definition
334 spec.names.at(0) = branchName;
335 }
336 }
337
342 template <typename ContextType>
343 void operator()(ContextType&& context)
344 {
345 if (!mTree || !mFile || mFile->IsZombie()) {
346 throw std::runtime_error("Writer is invalid state, probably closed previously");
347 }
348 // execute tree structure handlers and fill the individual branches
349 mTreeStructure->exec(std::forward<ContextType>(context), mBranchSpecs);
350 // Note: number of entries will be set when closing the writer
351 }
352
355 void close()
356 {
357 if (!mIsClosed) {
358 mIsClosed = true;
359 if (!mFile) {
360 return;
361 }
362 if (mCustomClose) {
363 mCustomClose(mFile.get(), mTree.get());
364 } else {
365 // set the number of elements according to branch content and write tree
366 mTree->SetEntries();
367 // use TFile::Write rather then TTree::Write since latter writes to default gDirectory
368 mFile->Write();
369 mFile->Close();
370 }
371 // this is a feature of ROOT, the tree belongs to the file and will be deleted
372 // automatically
373 mTree.release();
374 mFile.reset(nullptr);
375 }
376 }
377
379 void autoSave()
380 {
381 if (mIsClosed || !mFile) {
382 return;
383 }
384 mTree->SetEntries();
385 LOG(info) << "Autosaving " << mTree->GetName() << " at entry " << mTree->GetEntries();
386 mTree->AutoSave("overwrite");
387 }
388
389 bool isClosed() const
390 {
391 return mIsClosed;
392 }
393
394 size_t getStoreSize() const
395 {
396 return (mTreeStructure != nullptr ? mTreeStructure->size() : 0);
397 }
398
405 template <typename T>
406 static TBranch* remapBranch(TBranch& branchRef, T** newdata)
407 {
408 auto tree = branchRef.GetTree();
409 auto name = branchRef.GetName();
410 auto branch = tree->GetBranch(name); // the input branch might actually no belong to the tree but to TreeWriter cache
411 assert(branch);
412 if (branch->GetEntries()) { // if it has entries, then it was already remapped/filled at prevous event
413 branch->SetAddress(newdata);
414 return branch;
415 }
416 auto branchleaves = branch->GetListOfLeaves();
417 branch->DropBaskets("all");
418 branch->DeleteBaskets("all");
419 tree->GetListOfBranches()->Remove(branch);
420 for (auto entry : *branchleaves) {
421 tree->GetListOfLeaves()->Remove(entry);
422 }
423 // ROOT leaves holes in the arrays so we need to compress as well
424 tree->GetListOfBranches()->Compress();
425 tree->GetListOfLeaves()->Compress();
426 // create a new branch with the same original name but attached to the new data
427 return tree->Branch(name, newdata);
428 }
429
430 private:
432 struct BranchSpec {
433 std::vector<key_type> keys;
434 std::vector<std::string> names;
435 std::vector<TBranch*> branches;
436 TClass* classinfo = nullptr;
437 IndexExtractor getIndex = nullptr;
438 BranchNameMapper getName = nullptr;
439 };
440
441 using InputContext = InputRecord;
442
445 class TreeStructureInterface
446 {
447 public:
448 static const size_t STAGE = 0;
449 TreeStructureInterface() = default;
450 virtual ~TreeStructureInterface() = default;
451
454 virtual void setup(std::vector<BranchSpec>&, TTree*) {}
459 virtual void exec(InputContext&, std::vector<BranchSpec>&) {}
462 virtual size_t size() const { return STAGE; }
463
464 // a dummy method called in the recursive processing
465 void setupInstance(std::vector<BranchSpec>&, TTree*) {}
466 // a dummy method called in the recursive processing
467 void process(InputContext&, std::vector<BranchSpec>&) {}
468 };
469
470 template <typename T = char>
471 using BinaryBranchStoreType = std::tuple<std::vector<T>, TBranch*, size_t>;
472
473 // type trait to determine the type of the storage
474 // the storage type is different depending on the type, because
475 // - binary branches are supported
476 // - the ROOT TTree API makes a difference between fundamental types where the pointer to variable is
477 // passed and complex objects with dictionary where the pointer to a pointer variable needs to be
478 // passed. The former case will allways include a copy of the value, the latter uses the deserialized
479 // object by pointer
480 // It appears, that also vectors of fundamental and also messageable tyapes can be treated in the same
481 // way as fundamental types, as long as there is a corresponding TClass implementation. ROOT creates those
482 // for vectors of fundamental types, for all other types, ClassDefNV macro is needed in the class
483 // declaration together with entry in LinkDef.
484 template <typename T, typename _ = void>
485 struct StructureElementTypeTrait {
486 };
487
488 using BinaryBranchSpecialization = std::integral_constant<char, 0>;
489 using MessageableTypeSpecialization = std::integral_constant<char, 1>;
490 using MessageableVectorSpecialization = std::integral_constant<char, 2>;
491 using ROOTTypeSpecialization = std::integral_constant<char, 3>;
492
493 // the binary branch format is chosen for const char*
494 // using internally a vector<char>, this involves for the moment a copy, investigate if ROOT
495 // can write a simple array of chars and use a pointer
496 template <typename T>
497 struct StructureElementTypeTrait<T, std::enable_if_t<std::is_same<T, const char*>::value>> {
498 using value_type = T;
499 using store_type = BinaryBranchStoreType<char>;
500 using specialization_id = BinaryBranchSpecialization;
501 };
502
503 // all messageable types
504 // using an internal variable of the given type, involves a copy
505 template <typename T>
506 struct StructureElementTypeTrait<T, std::enable_if_t<is_messageable<T>::value>> {
507 using value_type = T;
508 using store_type = value_type;
509 using specialization_id = MessageableTypeSpecialization;
510 };
511
512 // vectors of messageable types
513 template <typename T>
514 struct StructureElementTypeTrait<T, std::enable_if_t<has_messageable_value_type<T>::value &&
515 is_specialization_v<T, std::vector>>> {
516 using value_type = T;
517 using store_type = value_type*;
518 using specialization_id = MessageableVectorSpecialization;
519 };
520
521 // types with root dictionary but non-messageable and not vectors of messageable type
522 template <typename T>
523 struct StructureElementTypeTrait<T, std::enable_if_t<has_root_dictionary<T>::value &&
524 is_messageable<T>::value == false &&
525 has_messageable_value_type<T>::value == false>> {
526 using value_type = T;
527 using store_type = value_type*;
528 using specialization_id = ROOTTypeSpecialization;
529 };
530
531 // types marked as ROOT serialized
532 template <typename T>
533 struct StructureElementTypeTrait<T,
534 std::enable_if_t<is_specialization_v<T, ROOTSerialized> == true>> {
535 using value_type = typename T::wrapped_type;
536 using store_type = value_type*;
537 using specialization_id = ROOTTypeSpecialization;
538 };
539
542 template <typename DataT, typename BASE>
543 class TreeStructureElement : public BASE
544 {
545 public:
546 using PrevT = BASE;
547 using value_type = typename StructureElementTypeTrait<DataT>::value_type;
548 using store_type = typename StructureElementTypeTrait<DataT>::store_type;
549 using specialization_id = typename StructureElementTypeTrait<DataT>::specialization_id;
550 static const size_t STAGE = BASE::STAGE + 1;
551 TreeStructureElement() = default;
552 ~TreeStructureElement() override = default;
553
554 void setup(std::vector<BranchSpec>& specs, TTree* tree) override
555 {
556 setupInstance(specs, tree);
557 }
558
559 // this is the polymorphic entry point for processing of branch specs
560 // recursive processing starting from the highest instance
561 void exec(InputContext& context, std::vector<BranchSpec>& specs) override
562 {
563 process(context, specs);
564 }
565 size_t size() const override { return STAGE; }
566
567 // the default method creates branch using address to store variable
568 // Note: the type of the store variable is pointer to object for objects with a ROOT TClass
569 // interface, or plain type for all others
570 // specialized on specialization_id
571 template <typename S, typename std::enable_if_t<!std::is_same<S, BinaryBranchSpecialization>::value, int> = 0>
572 TBranch* createBranch(TTree* tree, const char* name, size_t branchIdx)
573 {
574 return tree->Branch(name, &(mStore.at(branchIdx)));
575 }
576
577 // specialization for binary buffers indicated by const char*
578 // a variable binary data branch is written, the size of each entry is stored in a size branch
579 // specialized on specialization_id
580 template <typename S, typename std::enable_if_t<std::is_same<S, BinaryBranchSpecialization>::value, int> = 0>
581 TBranch* createBranch(TTree* tree, const char* name, size_t branchIdx)
582 {
583 // size variable to write the size of the data branch
584 std::get<2>(mStore.at(branchIdx)) = 1;
585 // branch name of the size branch
586 std::string sizeBranchName = std::string(name) + "Size";
587 // leaf list: one leaf of type unsinged int
588 std::string leafList = sizeBranchName + "/i";
589 std::get<1>(mStore.at(branchIdx)) = tree->Branch(sizeBranchName.c_str(), &(std::get<2>(mStore.at(branchIdx))), leafList.c_str());
590 return tree->Branch(name, &(std::get<0>(mStore.at(branchIdx))));
591 }
592
594 void setupInstance(std::vector<BranchSpec>& specs, TTree* tree)
595 {
596 // recursing through the tree structure by simply using method of the previous type,
597 // i.e. the base class method.
598 PrevT::setupInstance(specs, tree);
599 constexpr size_t SpecIndex = STAGE - 1;
600 if (specs[SpecIndex].branches.size() == 0) {
601 // this definition is disabled
602 return;
603 }
604 specs[SpecIndex].classinfo = TClass::GetClass(typeid(value_type));
605 if (std::is_same<value_type, const char*>::value == false && std::is_fundamental<value_type>::value == false &&
606 specs[SpecIndex].classinfo == nullptr) {
607 // for all non-fundamental types but the special case for binary chunks, a dictionary is required
608 // FIXME: find a reliable way to check that the type has been specified in the LinkDef
609 // Only then the required functionality for streaming the type to the branch is available.
610 // If e.g. a standard container of some ROOT serializable type has not been specified in
611 // LinkDef, the functionality is not available and the attempt to stream will simply crash.
612 // Unfortunately, a class info object can be extracted for the type, so this check does not help
613 throw std::runtime_error(std::to_string(SpecIndex) + ": no dictionary available for non-fundamental type " + typeid(value_type).name());
614 }
615 size_t branchIdx = 0;
616 mStore.resize(specs[SpecIndex].names.size());
617 for (auto const& name : specs[SpecIndex].names) {
618 specs[SpecIndex].branches.at(branchIdx) = createBranch<specialization_id>(tree, name.c_str(), branchIdx);
619 if (specs[SpecIndex].branches.at(branchIdx) == nullptr) {
620 throw std::runtime_error(std::to_string(SpecIndex) + ": can not create branch " + name + " for type " + typeid(value_type).name() + " - LinkDef entry missing?");
621 }
622 LOG(info) << SpecIndex << ": branch " << name << " set up";
623 branchIdx++;
624 }
625 }
626
629 template <typename DataType>
630 bool runCallback(TBranch* branch, DataType const& data, DataRef const& ref)
631 {
632 if (std::holds_alternative<typename BranchDef<value_type>::Spectator>(mCallback)) {
633 std::get<typename BranchDef<value_type>::Spectator>(mCallback)(data);
634 }
635 if (std::holds_alternative<typename BranchDef<value_type>::SpectatorExt>(mCallback)) {
636 std::get<typename BranchDef<value_type>::SpectatorExt>(mCallback)(data, ref);
637 }
638 if (std::holds_alternative<typename BranchDef<value_type>::Fill>(mCallback)) {
639 std::get<typename BranchDef<value_type>::Fill>(mCallback)(*branch, data);
640 return true;
641 }
642 if (std::holds_alternative<typename BranchDef<value_type>::FillExt>(mCallback)) {
643 std::get<typename BranchDef<value_type>::FillExt>(mCallback)(*branch, data, ref);
644 return true;
645 }
646 return false;
647 }
648
649 // specialization for trivial structs or serialized objects without a TClass interface
650 // the extracted object is copied to store variable
651 template <typename S, typename std::enable_if_t<std::is_same<S, MessageableTypeSpecialization>::value, int> = 0>
652 void fillData(InputContext& context, DataRef const& ref, TBranch* branch, size_t branchIdx)
653 {
654 auto data = context.get<value_type>(ref);
655 if (!runCallback(branch, data, ref)) {
656 mStore[branchIdx] = data;
657 branch->Fill();
658 }
659 }
660
661 // specialization for non-messageable types with ROOT dictionary
662 // for non-trivial structs, the address of the pointer to the objects needs to be used
663 // in order to directly use the pointer to extracted object
664 // store is a pointer to object
665 template <typename S, typename std::enable_if_t<std::is_same<S, ROOTTypeSpecialization>::value, int> = 0>
666 void fillData(InputContext& context, DataRef const& ref, TBranch* branch, size_t branchIdx)
667 {
668 auto data = context.get<typename std::add_pointer<value_type>::type>(ref);
669 if (!runCallback(branch, *data, ref)) {
670 // this is ugly but necessary because of the TTree API does not allow a const
671 // object as input. Have to rely on that ROOT treats the object as const
672 mStore[branchIdx] = const_cast<value_type*>(data.get());
673 branch->Fill();
674 }
675 }
676
677 // specialization for binary buffers using const char*
678 // this writes both the data branch and a size branch
679 template <typename S, typename std::enable_if_t<std::is_same<S, BinaryBranchSpecialization>::value, int> = 0>
680 void fillData(InputContext& context, DataRef const& ref, TBranch* branch, size_t branchIdx)
681 {
682 auto data = context.get<gsl::span<char>>(ref);
683 std::get<2>(mStore.at(branchIdx)) = data.size();
684 std::get<1>(mStore.at(branchIdx))->Fill();
685 std::get<0>(mStore.at(branchIdx)).resize(data.size());
686 memcpy(std::get<0>(mStore.at(branchIdx)).data(), data.data(), data.size());
687 branch->Fill();
688 }
689
690 // specialization for vectors of messageable types
691 template <typename S, typename std::enable_if_t<std::is_same<S, MessageableVectorSpecialization>::value, int> = 0>
692 void fillData(InputContext& context, DataRef const& ref, TBranch* branch, size_t branchIdx)
693 {
694 using ElementType = typename value_type::value_type;
695 static_assert(is_messageable<ElementType>::value, "logical error: should be correctly selected by StructureElementTypeTrait");
696
697 // A helper struct mimicking data layout of std::vector containers
698 // We assume a standard layout of begin, end, end_capacity
699 struct VecBase {
700 VecBase() = default;
701 const ElementType* start = nullptr;
702 const ElementType* end = nullptr;
703 const ElementType* cap = nullptr;
704 };
705
706 // a low level hack to make a gsl::span appear as a std::vector so that ROOT serializes the correct type
707 // but without the need for an extra copy
708 auto adopt = [](auto const& data, value_type& v) {
709 static_assert(sizeof(v) == 24);
710 if (data.size() == 0) {
711 return;
712 }
713 VecBase impl;
714 impl.start = &(data[0]);
715 impl.end = &(data[data.size() - 1]) + 1; // end pointer (beyond last element)
716 impl.cap = impl.end;
717 std::memcpy(&v, &impl, sizeof(VecBase));
718 };
719
720 // if the value type is messagable and has a ROOT dictionary, two serialization methods are possible
721 // for the moment, the InputRecord API can not handle both with the same call
722 try {
723 // try extracting from message with serialization method NONE, throw runtime error
724 // if message is serialized
725 auto data = context.get<gsl::span<ElementType>>(ref);
726 // take an ordinary std::vector "view" on the data
727 auto* dataview = new value_type;
728 adopt(data, *dataview);
729 if (!runCallback(branch, *dataview, ref)) {
730 mStore[branchIdx] = dataview;
731 branch->Fill();
732 }
733 // we delete JUST the view without deleting the data (which is handled by DPL)
734 auto ptr = (VecBase*)dataview;
735 if (ptr) {
736 delete ptr;
737 }
738 } catch (RuntimeErrorRef e) {
739 if constexpr (has_root_dictionary<value_type>::value == true) {
740 // try extracting from message with serialization method ROOT
741 auto data = context.get<typename std::add_pointer<value_type>::type>(ref);
742 if (!runCallback(branch, *data, ref)) {
743 mStore[branchIdx] = const_cast<value_type*>(data.get());
744 branch->Fill();
745 }
746 } else {
747 // the type has no ROOT dictionary, re-throw exception
748 throw e;
749 }
750 }
751 }
752
753 // process previous stage and this stage
754 void process(InputContext& context, std::vector<BranchSpec>& specs)
755 {
756 // recursing through the tree structure by simply using method of the previous type,
757 // i.e. the base class method.
758 PrevT::process(context, specs);
759 constexpr size_t SpecIndex = STAGE - 1;
760 BranchSpec const& spec = specs[SpecIndex];
761 if (spec.branches.size() == 0) {
762 // this definition is disabled
763 return;
764 }
765 // loop over all defined inputs
766 for (auto const& key : spec.keys) {
767 auto keypos = context.getPos(key.c_str());
768 auto parts = context.getNofParts(keypos);
769 for (decltype(parts) part = 0; part < parts; part++) {
770 auto dataref = context.get(key.c_str(), part);
771 size_t branchIdx = 0;
772 if (spec.getIndex) {
773 branchIdx = spec.getIndex(dataref);
774 if (branchIdx == ~(size_t)0) {
775 // this indicates skipping
776 continue;
777 }
778 }
779 fillData<specialization_id>(context, dataref, spec.branches.at(branchIdx), branchIdx);
780 }
781 }
782 }
783
784 // helper function to get Nth argument from the argument pack
785 template <size_t N, typename Arg, typename... Args>
786 auto getArg(Arg&& arg, Args&&... args)
787 {
788 if constexpr (N == 0) {
789 return arg;
790 } else if constexpr (sizeof...(Args) > 0) {
791 return getArg<N - 1>(std::forward<Args>(args)...);
792 }
793 }
794
795 // recursively set optional callback from argument pack and recurse to all folded types
796 template <typename... Args>
797 void setCallback(Args&&... args)
798 {
799 // recursing through the tree structure by simply using method of the previous type,
800 // i.e. the base class method.
801 if constexpr (STAGE > 1) {
802 PrevT::setCallback(std::forward<Args>(args)...);
803 }
804
805 // now set this instance, the argument position is determined by the STAGE counter
806 auto arg = getArg<STAGE - 1>(std::forward<Args>(args)...);
807 if constexpr (std::is_same<value_type, typename decltype(arg)::type>::value) {
808 if (not std::holds_alternative<std::monostate>(arg.callback)) {
809 mCallback = std::move(arg.callback);
810 }
811 }
812 }
813
814 private:
816 std::vector<store_type> mStore;
818 typename BranchDef<value_type>::BranchCallback mCallback;
819 };
820
822 template <typename Arg, typename... Args>
823 void parseConstructorArgs(Arg&& arg, Args&&... args)
824 {
825 using Type = std::decay_t<Arg>;
826 if constexpr (can_assign<Type, CustomClose>::value) {
827 mCustomClose = arg;
828 } else {
829 mTreeStructure = createTreeStructure<0, TreeStructureInterface>(std::forward<Arg>(arg), std::forward<Args>(args)...);
830 return;
831 }
832 if constexpr (sizeof...(Args) > 0) {
833 parseConstructorArgs(std::forward<Args>(args)...);
834 }
835 }
836
838 template <size_t N, typename BASE, typename T, typename... Args>
839 auto createTreeStructure(T&& def, Args&&... args)
840 {
841 // Note: a branch definition can be disabled by setting nofBranches to zero
842 // an entry of the definition is kept in the registry, but the branches vector
843 // is of size zero, which will make the writer to always skip this definition
844 mBranchSpecs.push_back({{}, {def.branchName}});
845 auto& spec = mBranchSpecs.back();
846
847 // extract the internal keys for the list of provided input definitions
848 size_t idx = 0;
849 spec.keys.resize(def.keys.size());
850 std::generate(spec.keys.begin(), spec.keys.end(), [&def, &idx] { return T::key_extractor::asString(def.keys[idx++]); });
851 mBranchSpecs.back().branches.resize(def.nofBranches, nullptr);
852 // a branch definition might be disabled by setting number of branches to 0; if enabled,
853 // the number of branches has to match the number of inputs but can be larger depending
854 // on the exact functionality provided with the getIndex callback. In any case, the
855 // callbacks only need to be propagated if multiple branches are defined
856 assert(def.nofBranches == 0 || def.nofBranches >= spec.keys.size());
857 // a getIndex function makes only sense if there are multiple branches
858 assert(def.nofBranches <= 1 || def.getIndex);
859 if (def.nofBranches > 1) {
860 // FIXME: should that be an exception since assert is disabled in the normal build?
861 assert(def.getIndex && def.getName);
862 mBranchSpecs.back().getIndex = def.getIndex;
863 mBranchSpecs.back().names.resize(def.nofBranches);
864 }
865
866 // always amend the branch name(s) if the callback is configured
867 if (def.getName) {
868 mBranchSpecs.back().getName = def.getName;
869 idx = 0;
870 std::generate(mBranchSpecs.back().names.begin(), mBranchSpecs.back().names.end(),
871 [&def, &idx]() { return def.getName(def.branchName, idx++); });
872 }
873 using type = TreeStructureElement<typename T::type, BASE>;
874 if constexpr (N == 0) {
875 // in the upper most call we have the concrete type of the mixin object
876 // and call its method to set the callbacks from the argument pack, it
877 // recurses to all folded types
878 auto instance = createTreeStructure<N + 1, type>(std::forward<Args>(args)...);
879 instance->setCallback(std::move(def), std::forward<Args>(args)...);
880 // the upper most call returns the unique pointer of the virtual interface
881 std::unique_ptr<TreeStructureInterface> ret(instance.release());
882 return ret;
883 } else {
884 return std::move(createTreeStructure<N + 1, type>(std::forward<Args>(args)...));
885 }
886 }
887
888 // last iteration, returning unique pointer of the concrete mixin
889 template <size_t N, typename T>
890 std::unique_ptr<T> createTreeStructure()
891 {
892 static_assert(N > 0, "The writer does not make sense without branch definitions");
893 return std::make_unique<T>();
894 }
895
897 std::unique_ptr<TFile> mFile;
899 std::unique_ptr<TTree> mTree;
901 std::vector<BranchSpec> mBranchSpecs;
903 std::unique_ptr<TreeStructureInterface> mTreeStructure;
905 bool mIsClosed = false;
907 CustomClose mCustomClose;
908};
909
910} // namespace framework
911} // namespace o2
912#endif
std::string getName(const TDataMember *dm, int index, int size)
TBranch * ptr
StringRef key
The input API of the Data Processing Layer This class holds the inputs which are valid for processing...
A generic writer interface for ROOT TTree objects.
void operator()(ContextType &&context)
RootTreeWriter(const char *filename, const char *treename, Args &&... args)
void init(const char *filename, const char *treename, const char *treetitle=nullptr)
std::function< std::string(std::string, size_t)> BranchNameMapper
std::function< void(TFile *file, TTree *tree)> CustomClose
~RootTreeWriter()
a destructor making sure that the Writer is closed (if didn't happen before)
static TBranch * remapBranch(TBranch &branchRef, T **newdata)
std::function< size_t(o2::framework::DataRef const &)> IndexExtractor
RootTreeWriter()=delete
default constructor forbidden
void autoSave()
autosave the tree
void setBranchName(size_t index, const char *branchName)
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLboolean * data
Definition glcorearb.h:298
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint start
Definition glcorearb.h:469
GLint ref
Definition glcorearb.h:291
typename std::enable_if< B, T >::type enable_if_t
Definition json.h:281
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
Defining DataPointCompositeObject explicitly as copiable.
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
BranchDef is used to define the mapping between inputs and branches.
BranchDef(std::vector< key_type > vec, std::string _branchName, Args &&... args)
std::function< void(T const &, DataRef const &)> SpectatorExt
std::variant< std::monostate, Fill, FillExt, Spectator, SpectatorExt > BranchCallback
BranchDef(key_type key, std::string _branchName, size_t _nofBranches=1)
BranchNameMapper getName
get name of branch from base name and index
std::function< void(TBranch &branch, T const &)> Fill
std::function< void(TBranch &branch, T const &, DataRef const &)> FillExt
std::function< void(T const &)> Spectator
IndexExtractor getIndex
extractor function for the index for parallel branches
BranchDef(key_type key, std::string _branchName, Args &&... args)
size_t nofBranches
number of branches controlled by this definition for the same type
void init(Arg &&arg, Args &&... args)
recursively init member variables from parameter pack
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))