diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index 1239bd5cf6c69..5d92fd622b623 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -79,6 +79,7 @@ env: GLOBAL_OVERRIDES: 'asserts=ON LLVM_ENABLE_ASSERTIONS=ON' ROOT_TEST_S3_ACCESS_KEY: ${{ secrets.ROOT_TEST_S3_ACCESS_KEY }} ROOT_TEST_S3_SECRET_KEY: ${{ secrets.ROOT_TEST_S3_SECRET_KEY }} + ROOT_OBJECT_AUTO_REGISTRATION: 0 concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 9ee7e332ca2b3..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; } @@ -727,16 +731,26 @@ namespace Internal { /// | RooPlot | Yes | RooPlot::addDirectoryStatus() | /// | TEfficiency | Yes | No | /// | TProfile2D | Yes | TH1::AddDirectoryStatus() | - /// | TEntryList | No, but planned for 6.42 | No | - /// | TEventList | No, but planned for 6.42 | No | + /// | TEntryList (+ derived)| Yes | No | + /// | TEventList | Yes | No | /// | TFunction | No, but work in progress | No | /// /// ## 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 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); + } } //////////////////////////////////////////////////////////////////////////////// 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); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx index 8023b9b939bf3..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 @@ -4172,9 +4175,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 +4187,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 +4446,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 @@ -4502,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}