From ca96116c4464b2a69a9412ea93a7d531586f1c42 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Tue, 14 Jul 2026 13:52:58 +0200 Subject: [PATCH 1/5] [tree] Honour ObjectAutoRegistrationEnabled() in TEventList. --- core/base/src/TROOT.cxx | 2 +- tree/tree/src/TEventList.cxx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 9ee7e332ca2b3..039a0c0bd6957 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -728,7 +728,7 @@ namespace Internal { /// | TEfficiency | Yes | No | /// | TProfile2D | Yes | TH1::AddDirectoryStatus() | /// | TEntryList | No, but planned for 6.42 | No | - /// | TEventList | No, but planned for 6.42 | No | + /// | TEventList | Yes | No | /// | TFunction | No, but work in progress | No | /// /// ## Setting defaults diff --git a/tree/tree/src/TEventList.cxx b/tree/tree/src/TEventList.cxx index 7bcfc52c7f5ca..1fcffdab9c712 100644 --- a/tree/tree/src/TEventList.cxx +++ b/tree/tree/src/TEventList.cxx @@ -53,6 +53,7 @@ the TEventList object created in the above commands: #include "TDirectory.h" #include "TCollection.h" #include "TMathBase.h" +#include "TROOT.h" #include @@ -64,9 +65,11 @@ the TEventList object created in the above commands: TEventList::TEventList(const char *name, const char *title, Int_t initsize, Int_t delta) : TNamed(name, title), fSize(std::max(100, initsize)), fDelta(std::max(100, delta)) { - fDirectory = gDirectory; - if (fDirectory) - fDirectory->Append(this); + if (ROOT::Experimental::ObjectAutoRegistrationEnabled()) { + fDirectory = gDirectory; + if (fDirectory) + fDirectory->Append(this); + } } //////////////////////////////////////////////////////////////////////////////// From e130261298a619676df8d6017386fbcd076d714a Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 22 Jul 2026 18:20:29 +0200 Subject: [PATCH 2/5] [tree] Honour ObjectAutoRegistrationEnabled() in TEntryList. TEntryList and its derived classes will from here on only register themselves to gDirectory when the thread-local auto-registration is enabled. --- core/base/src/TROOT.cxx | 2 +- tree/tree/src/TEntryList.cxx | 35 +++++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 039a0c0bd6957..4642517e7779e 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -727,7 +727,7 @@ namespace Internal { /// | RooPlot | Yes | RooPlot::addDirectoryStatus() | /// | TEfficiency | Yes | No | /// | TProfile2D | Yes | TH1::AddDirectoryStatus() | - /// | TEntryList | No, but planned for 6.42 | No | + /// | TEntryList (+ derived)| Yes | No | /// | TEventList | Yes | No | /// | TFunction | No, but work in progress | No | /// diff --git a/tree/tree/src/TEntryList.cxx b/tree/tree/src/TEntryList.cxx index 832c5645574d0..43169710e7f1a 100644 --- a/tree/tree/src/TEntryList.cxx +++ b/tree/tree/src/TEntryList.cxx @@ -160,7 +160,7 @@ End_Macro #include "TRegexp.h" #include "TSystem.h" #include "TObjString.h" - +#include "TROOT.h" //////////////////////////////////////////////////////////////////////////////// /// default c-tor @@ -172,10 +172,11 @@ TEntryList::TEntryList() = default; TEntryList::TEntryList(const char *name, const char *title) : TNamed(name, title) { - - fDirectory = gDirectory; - if (fDirectory) - fDirectory->Append(this); + if (ROOT::Experimental::ObjectAutoRegistrationEnabled()) { + fDirectory = gDirectory; + if (fDirectory) + fDirectory->Append(this); + } } //////////////////////////////////////////////////////////////////////////////// @@ -185,9 +186,11 @@ TEntryList::TEntryList(const char *name, const char *title, const TTree *tree) : { TEntryList::SetTree(tree); - fDirectory = gDirectory; - if (fDirectory) - fDirectory->Append(this); + if (ROOT::Experimental::ObjectAutoRegistrationEnabled()) { + fDirectory = gDirectory; + if (fDirectory) + fDirectory->Append(this); + } } //////////////////////////////////////////////////////////////////////////////// @@ -198,9 +201,11 @@ TEntryList::TEntryList(const char *name, const char *title, const char *treename { SetTree(treename, filename); - fDirectory = gDirectory; - if (fDirectory) - fDirectory->Append(this); + if (ROOT::Experimental::ObjectAutoRegistrationEnabled()) { + fDirectory = gDirectory; + if (fDirectory) + fDirectory->Append(this); + } } //////////////////////////////////////////////////////////////////////////////// @@ -210,9 +215,11 @@ TEntryList::TEntryList(const TTree *tree) { SetTree(tree); - fDirectory = gDirectory; - if (fDirectory) - fDirectory->Append(this); + if (ROOT::Experimental::ObjectAutoRegistrationEnabled()) { + fDirectory = gDirectory; + if (fDirectory) + fDirectory->Append(this); + } } //////////////////////////////////////////////////////////////////////////////// From e67da7b25fc1f842d6b26f80cc987e06dbc837a4 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 8 Jul 2026 18:01:02 +0200 Subject: [PATCH 3/5] [core] Explain how to select ROOT auto-registration defaults. Explain that defaults can be set using .rootrc or using environment variables, and how to do this from within the program. --- core/base/src/TROOT.cxx | 49 +++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 4642517e7779e..397d04d376295 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -295,24 +295,20 @@ namespace { return moduleHeaderInfoBuffer; } + /// State helper for object auto registration. enum class AutoReg : unsigned char { kNotInitialised = 0, kOn, kOff, }; - //////////////////////////////////////////////////////////////////////////////// - /// \brief Test if various objects (such as TH1-derived classes) should automatically register - /// themselves (ROOT 6 mode) or not (ROOT 7 mode). - /// A default can be set in a .rootrc using e.g. "Root.ObjectAutoRegistration: 1" or setting - /// the environment variable "ROOT_OBJECT_AUTO_REGISTRATION=0". - AutoReg &ObjectAutoRegistrationEnabledImpl() + /// Set up the default state for object auto registration by inspecting the environment. + /// This state is used to initialise the auto-registration state for each thread that starts. + AutoReg ObjectAutoRegistrationDefault() { static constexpr auto rcName = "Root.ObjectAutoRegistration"; // Update the docs if this is changed static constexpr auto envName = "ROOT_OBJECT_AUTO_REGISTRATION"; // Update the docs if this is changed - thread_local static AutoReg tlsState = AutoReg::kNotInitialised; - - static const AutoReg defaultState = []() { + static const AutoReg defaultFromEnvironment = []() { AutoReg autoReg = AutoReg::kOn; // ROOT 6 default std::stringstream infoMessage; @@ -356,10 +352,18 @@ namespace { return autoReg; }(); - if (tlsState == AutoReg::kNotInitialised) { - assert(defaultState != AutoReg::kNotInitialised); - tlsState = defaultState; - } + return defaultFromEnvironment; + } + + //////////////////////////////////////////////////////////////////////////////// + /// \brief Test if various objects (such as TH1-derived classes) should automatically register + /// themselves (ROOT 6 mode) or not (ROOT 7 mode). + /// A default can be set in a .rootrc using e.g. "Root.ObjectAutoRegistration: 1" or setting + /// the environment variable "ROOT_OBJECT_AUTO_REGISTRATION=0". + AutoReg &ObjectAutoRegistrationEnabledImpl() + { + thread_local static AutoReg tlsState = ObjectAutoRegistrationDefault(); + assert(tlsState != AutoReg::kNotInitialised); return tlsState; } @@ -733,10 +737,20 @@ namespace Internal { /// /// ## Setting defaults /// - /// A default can be set in a .rootrc using e.g. `Root.ObjectAutoRegistration: 1` or setting - /// the environment variable `ROOT_OBJECT_AUTO_REGISTRATION=0`. Note that this default affects - /// all the threads that get started. - /// When a default is set using one of these methods, ROOT will notify with an Info message. + /// A default can be set (in order of precedence): + /// 1. Setting the environment variable `ROOT_OBJECT_AUTO_REGISTRATION=[01]` + /// 2. Setting `Root.ObjectAutoRegistration: [01]` in a .rootrc file. + /// + /// To do this programmatically, one can use + /// \code{.cpp} + /// setenv("ROOT_OBJECT_AUTO_REGISTRATION", 1); + /// // or using ROOT's TEnv: + /// gEnv->SetValue("Root.ObjectAutoRegistration", 1); + /// \endcode + /// This has to be done *before* the first object with auto-registration is created. Once this is done, + /// every thread starts with the same default. A running thread's behaviour can only be changed using + /// Enable/DisableObjectAutoRegistration(). + /// When the default state is changed using the environment or .rootrc, ROOT issues a reminder. /// /// ## Difference to TH1::AddDirectoryStatus() /// @@ -765,6 +779,7 @@ namespace Internal { assert(state != AutoReg::kNotInitialised); return state == AutoReg::kOn; } + } // namespace Experimental } // end of ROOT namespace From be8959268f5755bbf37b997a3ab59828dc102bf8 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Thu, 23 Jul 2026 11:42:50 +0200 Subject: [PATCH 4/5] [tree][docs] Explain how histograms or event lists can be registered for TTree::Draw. --- tree/tree/src/TTree.cxx | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx index 8023b9b939bf3..2b67ed2f0d7c4 100644 --- a/tree/tree/src/TTree.cxx +++ b/tree/tree/src/TTree.cxx @@ -4172,9 +4172,9 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// // 100 bins in x-direction; lower limit on x-axis is 10; upper limit is 60 /// // 50 bins in y-direction; lower limit on y-axis is .1; upper limit is .5 /// ~~~ -/// By default, the specified histogram is reset. -/// To continue to append data to an existing histogram, use "+" in front -/// of the histogram name. +/// By default, if a histogram with the same name is already registered to the current +/// ROOT directory, the specified histogram is reset. To continue to append data to an +/// existing histogram, use "+" in front of the histogram name. /// /// A '+' in front of the histogram name is ignored, when the name is followed by /// binning information as described in the previous paragraph. @@ -4184,6 +4184,15 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// will not reset `hsqrt`, but will continue filling. This works for 1-D, 2-D /// and 3-D histograms. /// +/// Note that when the automatic registration of histograms is off (see \ref DisableObjectAutoRegistration() ), +/// external histogram are not visible to TTree::Draw unless they are registered to the current directory explicitly. +/// ~~~ {.cpp} +/// auto histo = new TH1D("histo", ...); +/// histo->SetDirectory(gDirectory); +/// tree.Draw("sqrt(x)>>histo","y>0") +/// ~~~ +/// When auto-registration is off, histograms created by TTree::Draw will still be registered to the current directory. +/// /// ### Accessing collection objects /// /// TTree::Draw default's handling of collections is to assume that any @@ -4434,6 +4443,14 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// will not reset yplus, but will enter the selected entries at the end /// of the existing list. /// +/// Note that when the automatic registration of event lists is off (see \ref DisableObjectAutoRegistration() ), +/// they are not visible to TTree::Draw unless they are registered to the current directory explicitly. +/// ~~~ {.cpp} +/// auto elist = new TEventList("elist", ...); +/// elist->SetDirectory(gDirectory); +/// tree.Draw(">>+elist","y>0") +/// ~~~ +/// /// ### Using a TEventList, TEntryList or TEntryListArray as Input /// /// Once a TEventList or a TEntryList object has been generated, it can be used as input From a733fcf5ef21bf4b5bc4b694036ed74acbc2ef01 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 29 Jul 2026 13:43:01 +0200 Subject: [PATCH 5/5] [NFC] Apply clang-format for TTree. Clang-format enforces lines breaks, even if the lines weren't touched in git-clang-format mode. --- tree/tree/src/TTree.cxx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx index 2b67ed2f0d7c4..7d1636aaf9052 100644 --- a/tree/tree/src/TTree.cxx +++ b/tree/tree/src/TTree.cxx @@ -3925,7 +3925,8 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// vs "e2" vs "e3" and "e4" mapped on the current color palette. /// (to create histograms in the 2, 3, and 4 dimensional case, /// see section "Saving the result of Draw to an histogram") -/// - "e1:e2:e3:e4:e5" with option "GL5D" produces a 5D plot using OpenGL. `gStyle->SetCanvasPreferGL(true)` is needed. +/// - "e1:e2:e3:e4:e5" with option "GL5D" produces a 5D plot using OpenGL. `gStyle->SetCanvasPreferGL(true)` is +/// needed. /// - Any number of variables no fewer than two can be used with the options "CANDLE" and "PARA" /// - An arbitrary number of variables can be used with the option "GOFF" /// @@ -3964,7 +3965,8 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// If the selection expression returns an array, it is iterated over in sync with the /// array returned by the varexp argument (as described below in "Drawing expressions using arrays and array /// elements"). For example, if, for a given event, varexp evaluates to -/// `{1., 2., 3.}` and selection evaluates to `{0, 1, 0}`, the resulting histogram is filled with the value 2. For example, for each event here we perform a simple object selection: +/// `{1., 2., 3.}` and selection evaluates to `{0, 1, 0}`, the resulting histogram is filled with the value 2. For +/// example, for each event here we perform a simple object selection: /// ~~~{.cpp} /// // Muon_pt is an array: fill a histogram with the array elements > 100 in each event /// tree->Draw('Muon_pt', 'Muon_pt > 100') @@ -4039,7 +4041,8 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// | `fMatrix[][2] - fResults[][]` | six | on both 1st and 2nd dimensions of fResults | /// | `fMatrix[][2] - fResults[3][]` | two | on 1st dim of fMatrix and 2nd of fResults (at the same time) | /// | `fMatrix[][] - fResults[][]` | six | on 1st dim then on 2nd dim | -/// | `fMatrix[][fResult[][]]` | 30 | on 1st dim of fMatrix then on both dimensions of fResults. The value if fResults[j][k] is used as the second index of fMatrix.| +/// | `fMatrix[][fResult[][]]` | 30 | on 1st dim of fMatrix then on both dimensions of fResults. The +/// value if fResults[j][k] is used as the second index of fMatrix.| /// /// /// In summary, TTree::Draw loops through all unspecified dimensions. To @@ -4519,12 +4522,14 @@ Long64_t TTree::Draw(const char* varexp, const TCut& selection, Option_t* option /// Once TTree::Draw has been called, it is possible to access useful /// information still stored in the TTree object via the following functions: /// -/// - GetSelectedRows() // return the number of values accepted by the selection expression. In case where no selection was specified, returns the number of values processed. +/// - GetSelectedRows() // return the number of values accepted by the selection expression. In case where no selection +/// was specified, returns the number of values processed. /// - GetV1() // returns a pointer to the double array of V1 /// - GetV2() // returns a pointer to the double array of V2 /// - GetV3() // returns a pointer to the double array of V3 /// - GetV4() // returns a pointer to the double array of V4 -/// - GetW() // returns a pointer to the double array of Weights where weight equal the result of the selection expression. +/// - GetW() // returns a pointer to the double array of Weights where weight equal the result of the +/// selection expression. /// /// where V1,V2,V3 correspond to the expressions in /// ~~~ {.cpp}