3030/* * \class TTree
3131\ingroup tree
3232
33- A TTree represents a columnar dataset. Any C++ type can be stored in its columns. The modern
33+ A TTree represents a columnar dataset. Any C++ type can be stored in its columns. The modern
3434version of TTree is RNTuple: please consider using it before opting for TTree.
3535
3636A TTree, often called in jargon *tree*, consists of a list of independent columns or *branches*,
@@ -141,19 +141,19 @@ It is strongly recommended to persistify those as objects rather than lists of l
141141~~~ {.cpp}
142142 auto branch = tree.Branch( branchname, STLcollection, buffsize, splitlevel);
143143~~~
144- `STLcollection` is the address of a pointer to a container of the standard
145- library such as `std::vector`, `std::list`, containing pointers, fundamental types
144+ `STLcollection` is the address of a pointer to a container of the standard
145+ library such as `std::vector`, `std::list`, containing pointers, fundamental types
146146or objects.
147147If the splitlevel is a value bigger than 100 (`TTree::kSplitCollectionOfPointers`)
148- then the collection will be written in split mode, i.e. transparently storing
148+ then the collection will be written in split mode, i.e. transparently storing
149149individual data members as arrays, therewith potentially increasing compression ratio.
150150
151- ### Note
152- In case of dynamic structures changing with each entry, see e.g.
151+ ### Note
152+ In case of dynamic structures changing with each entry, see e.g.
153153~~~ {.cpp}
154154 branch->SetAddress(void *address)
155155~~~
156- one must redefine the branch address before filling the branch
156+ one must redefine the branch address before filling the branch
157157again. This is done via the `TBranch::SetAddress` member function.
158158
159159\anchor addingacolumnofobjs
@@ -186,8 +186,8 @@ Another available syntax is the following:
186186- `p_object` is a pointer to an object.
187187- If `className` is not specified, the `Branch` method uses the type of `p_object`
188188 to determine the type of the object.
189- - If `className` is used to specify explicitly the object type, the `className`
190- must be of a type related to the one pointed to by the pointer. It should be
189+ - If `className` is used to specify explicitly the object type, the `className`
190+ must be of a type related to the one pointed to by the pointer. It should be
191191 either a parent or derived class.
192192
193193Note: The pointer whose address is passed to `TTree::Branch` must not
@@ -205,13 +205,13 @@ or not
205205 tree.Branch(branchname, &p_object);
206206~~~
207207In either case, the ownership of the object is not taken over by the `TTree`.
208- Even though in the first case an object is be allocated by `TTree::Branch`,
208+ Even though in the first case an object is be allocated by `TTree::Branch`,
209209the object will <b>not</b> be deleted when the `TTree` is deleted.
210210
211211\anchor addingacolumnoftclonesarray
212212## Add a column holding TClonesArray instances
213213
214- *The usage of `TClonesArray` should be abandoned in favour of `std::vector`,
214+ *The usage of `TClonesArray` should be abandoned in favour of `std::vector`,
215215for which `TTree` has been heavily optimised, as well as `RNTuple`.*
216216
217217~~~ {.cpp}
@@ -347,7 +347,7 @@ int main()
347347~~~
348348## TTree Diagram
349349
350- The following diagram shows the organisation of the federation of classes related to TTree.
350+ The following diagram shows the organisation of the federation of classes related to TTree.
351351
352352Begin_Macro
353353../../../tutorials/legacy/tree/tree.C
@@ -6841,6 +6841,8 @@ bool TTree::MemoryFull(Int_t nbytes)
68416841// /
68426842// / Trees in the list can be memory or disk-resident trees.
68436843// / The new tree is created in the current directory (memory if gROOT).
6844+ // / Trees with no branches will be skipped, the branch structure
6845+ // / will be taken from the first non-zero-branch Tree of {li}
68446846
68456847TTree* TTree::MergeTrees (TList* li, Option_t* options)
68466848{
@@ -6852,8 +6854,15 @@ TTree* TTree::MergeTrees(TList* li, Option_t* options)
68526854 while ((obj=next ())) {
68536855 if (!obj->InheritsFrom (TTree::Class ())) continue ;
68546856 TTree *tree = (TTree*)obj;
6857+ if (tree->GetListOfBranches ()->IsEmpty ()) {
6858+ if (gDebug > 2 ) {
6859+ tree->Warning (" MergeTrees" ," TTree %s has no branches, skipping." , tree->GetName ());
6860+ }
6861+ continue ; // Completely ignore the empty trees.
6862+ }
68556863 Long64_t nentries = tree->GetEntries ();
6856- if (nentries == 0 ) continue ;
6864+ if (newtree && nentries == 0 )
6865+ continue ; // If we already have the structure and we have no entry, save time and skip
68576866 if (!newtree) {
68586867 newtree = (TTree*)tree->CloneTree (-1 , options);
68596868 if (!newtree) continue ;
@@ -6867,7 +6876,8 @@ TTree* TTree::MergeTrees(TList* li, Option_t* options)
68676876 newtree->ResetBranchAddresses ();
68686877 continue ;
68696878 }
6870-
6879+ if (nentries == 0 )
6880+ continue ;
68716881 newtree->CopyEntries (tree, -1 , options, true );
68726882 }
68736883 if (newtree && newtree->GetTreeIndex ()) {
@@ -6880,9 +6890,43 @@ TTree* TTree::MergeTrees(TList* li, Option_t* options)
68806890// / Merge the trees in the TList into this tree.
68816891// /
68826892// / Returns the total number of entries in the merged tree.
6893+ // / Trees with no branches will be skipped, the branch structure
6894+ // / will be taken from the first non-zero-branch Tree of {this+li}
68836895
68846896Long64_t TTree::Merge (TCollection* li, Option_t *options)
68856897{
6898+ if (fBranches .IsEmpty ()) {
6899+ if (!li || li->IsEmpty ())
6900+ return 0 ; // Nothing to do ....
6901+ // Let's find the first non-empty
6902+ TIter next (li);
6903+ TTree *tree;
6904+ while ((tree = (TTree *)next ())) {
6905+ if (tree == this || tree->GetListOfBranches ()->IsEmpty ()) {
6906+ if (gDebug > 2 ) {
6907+ Warning (" Merge" ," TTree %s has no branches, skipping." , tree->GetName ());
6908+ }
6909+ continue ;
6910+ }
6911+ // We could come from a list made up of different names, the first one still wins
6912+ tree->SetName (this ->GetName ());
6913+ auto prevEntries = tree->GetEntries ();
6914+ auto result = tree->Merge (li, options);
6915+ if (result != prevEntries) {
6916+ // If there is no additional entries, the first write was enough.
6917+ tree->Write ();
6918+ }
6919+ // Make sure things are really written out to disk before attempting any reading.
6920+ if (tree->GetCurrentFile ()) {
6921+ tree->GetCurrentFile ()->Flush ();
6922+ // Read back the complete info in this TTree, so that caller does not
6923+ // inadvertently write the empty tree.
6924+ tree->GetDirectory ()->ReadTObject (this , this ->GetName ());
6925+ }
6926+ return result;
6927+ }
6928+ return 0 ; // All trees have empty branches
6929+ }
68866930 if (!li) return 0 ;
68876931 Long64_t storeAutoSave = fAutoSave ;
68886932 // Disable the autosave as the TFileMerge keeps a list of key and deleting the underlying
@@ -6915,11 +6959,43 @@ Long64_t TTree::Merge(TCollection* li, Option_t *options)
69156959// / info->fOutputDirectory and then overlay the new TTree information onto
69166960// / this TTree object (so that this TTree object is now the appropriate to
69176961// / use for further merging).
6962+ // / Trees with no branches will be skipped, the branch structure
6963+ // / will be taken from the first non-zero-branch Tree of {this+li}
69186964// /
69196965// / Returns the total number of entries in the merged tree.
69206966
69216967Long64_t TTree::Merge (TCollection* li, TFileMergeInfo *info)
69226968{
6969+ if (fBranches .IsEmpty ()) {
6970+ if (!li || li->IsEmpty ())
6971+ return 0 ; // Nothing to do ....
6972+ // Let's find the first non-empty
6973+ TIter next (li);
6974+ TTree *tree;
6975+ while ((tree = (TTree *)next ())) {
6976+ if (tree == this || tree->GetListOfBranches ()->IsEmpty ()) {
6977+ if (gDebug > 2 ) {
6978+ Warning (" Merge" ," TTree %s has no branches, skipping." , tree->GetName ());
6979+ }
6980+ continue ;
6981+ }
6982+ // We could come from a list made up of different names, the first one still wins
6983+ tree->SetName (this ->GetName ());
6984+ auto prevEntries = tree->GetEntries ();
6985+ auto result = tree->Merge (li, info);
6986+ if (result != prevEntries) {
6987+ // If there is no additional entries, the first write was enough.
6988+ tree->Write ();
6989+ }
6990+ // Make sure things are really written out to disk before attempting any reading.
6991+ info->fOutputDirectory ->GetFile ()->Flush ();
6992+ // Read back the complete info in this TTree, so that TFileMerge does not
6993+ // inadvertently write the empty tree.
6994+ info->fOutputDirectory ->ReadTObject (this , this ->GetName ());
6995+ return result;
6996+ }
6997+ return 0 ; // All trees have empty branches
6998+ }
69236999 const char *options = info ? info->fOptions .Data () : " " ;
69247000 if (info && info->fIsFirst && info->fOutputDirectory && info->fOutputDirectory ->GetFile () != GetCurrentFile ()) {
69257001 if (GetCurrentFile () == nullptr ) {
0 commit comments