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,11 @@ 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+ continue ; // Completely ignore the empty trees.
68556859 Long64_t nentries = tree->GetEntries ();
6856- if (nentries == 0 ) continue ;
6860+ if (newtree && nentries == 0 )
6861+ continue ; // If we already have the structure and we have no entry, save time and skip
68576862 if (!newtree) {
68586863 newtree = (TTree*)tree->CloneTree (-1 , options);
68596864 if (!newtree) continue ;
@@ -6867,7 +6872,8 @@ TTree* TTree::MergeTrees(TList* li, Option_t* options)
68676872 newtree->ResetBranchAddresses ();
68686873 continue ;
68696874 }
6870-
6875+ if (nentries == 0 )
6876+ continue ;
68716877 newtree->CopyEntries (tree, -1 , options, true );
68726878 }
68736879 if (newtree && newtree->GetTreeIndex ()) {
@@ -6880,9 +6886,39 @@ TTree* TTree::MergeTrees(TList* li, Option_t* options)
68806886// / Merge the trees in the TList into this tree.
68816887// /
68826888// / Returns the total number of entries in the merged tree.
6889+ // / Trees with no branches will be skipped, the branch structure
6890+ // / will be taken from the first non-zero-branch Tree of {this+li}
68836891
68846892Long64_t TTree::Merge (TCollection* li, Option_t *options)
68856893{
6894+ if (fBranches .IsEmpty ()) {
6895+ if (!li || li->IsEmpty ())
6896+ return 0 ; // Nothing to do ....
6897+ // Let's find the first non-empty
6898+ TIter next (li);
6899+ TTree *tree;
6900+ while ((tree = (TTree *)next ())) {
6901+ if (tree == this || tree->GetListOfBranches ()->IsEmpty ())
6902+ continue ;
6903+ // We could come from a list made up of different names, the first one still wins
6904+ tree->SetName (this ->GetName ());
6905+ auto prevEntries = tree->GetEntries ();
6906+ auto result = tree->Merge (li, options);
6907+ if (result != prevEntries) {
6908+ // If there is no additional entries, the first write was enough.
6909+ tree->Write ();
6910+ }
6911+ // Make sure things are really written out to disk before attempting any reading.
6912+ if (tree->GetCurrentFile ()) {
6913+ tree->GetCurrentFile ()->Flush ();
6914+ // Read back the complete info in this TTree, so that caller does not
6915+ // inadvertently write the empty tree.
6916+ tree->GetDirectory ()->ReadTObject (this , this ->GetName ());
6917+ }
6918+ return result;
6919+ }
6920+ return 0 ; // All trees have empty branches
6921+ }
68866922 if (!li) return 0 ;
68876923 Long64_t storeAutoSave = fAutoSave ;
68886924 // Disable the autosave as the TFileMerge keeps a list of key and deleting the underlying
@@ -6915,11 +6951,39 @@ Long64_t TTree::Merge(TCollection* li, Option_t *options)
69156951// / info->fOutputDirectory and then overlay the new TTree information onto
69166952// / this TTree object (so that this TTree object is now the appropriate to
69176953// / use for further merging).
6954+ // / Trees with no branches will be skipped, the branch structure
6955+ // / will be taken from the first non-zero-branch Tree of {this+li}
69186956// /
69196957// / Returns the total number of entries in the merged tree.
69206958
69216959Long64_t TTree::Merge (TCollection* li, TFileMergeInfo *info)
69226960{
6961+ if (fBranches .IsEmpty ()) {
6962+ if (!li || li->IsEmpty ())
6963+ return 0 ; // Nothing to do ....
6964+ // Let's find the first non-empty
6965+ TIter next (li);
6966+ TTree *tree;
6967+ while ((tree = (TTree *)next ())) {
6968+ if (tree == this || tree->GetListOfBranches ()->IsEmpty ())
6969+ continue ;
6970+ // We could come from a list made up of different names, the first one still wins
6971+ tree->SetName (this ->GetName ());
6972+ auto prevEntries = tree->GetEntries ();
6973+ auto result = tree->Merge (li, info);
6974+ if (result != prevEntries) {
6975+ // If there is no additional entries, the first write was enough.
6976+ tree->Write ();
6977+ }
6978+ // Make sure things are really written out to disk before attempting any reading.
6979+ info->fOutputDirectory ->GetFile ()->Flush ();
6980+ // Read back the complete info in this TTree, so that TFileMerge does not
6981+ // inadvertently write the empty tree.
6982+ info->fOutputDirectory ->ReadTObject (this , this ->GetName ());
6983+ return result;
6984+ }
6985+ return 0 ; // All trees have empty branches
6986+ }
69236987 const char *options = info ? info->fOptions .Data () : " " ;
69246988 if (info && info->fIsFirst && info->fOutputDirectory && info->fOutputDirectory ->GetFile () != GetCurrentFile ()) {
69256989 if (GetCurrentFile () == nullptr ) {
0 commit comments