Catching forgetting to bind the variable to the TTree early on

Hi all,

Consider the following code:

// test.cpp
void test(){
    int var1 = 1;
    int var2 = 2;
    int var3 = 3;
    // there is a lot of variables to compute
    
    auto file = std::make_unique<TFile>("results.root", "RECREATE");
    auto tree = std::make_unique<TTree>("events", "events");
    
    tree->Branch("var1", &var1);
    tree->Branch("var2", &var2);
    
    tree->Fill();
    file->Write();
}

In this code I accidentally forgot to bind var3.

tree->Branch("var3", &var3);

I would like an advice on how can I catch such mistakes early on, ideally during the compilcation, rather than to discover I missed one after all of my X hour jobs are finished. Of course, I could add a dedicated testing, but I would like to achieve that with a minimum extra boilerplate code.

ChatGPT proposed me a nice alternative of writing the Event class:

struct Event {
    int var1;
    int var2;
    int var3;
};

void test(){

    auto event = Event(1, 2, 3);
    
    auto file = std::make_unique<TFile>("results.root", "RECREATE");
    auto tree = std::make_unique<TTree>("events", "events");
    
    tree->Branch("Event", &event);

    tree->Fill();
    file->Write();
}

In such a way, the branches are created automatically and there is no possibility to forget anything, which I like.

However, this makes reading files in python quite annoying.

import ROOT

f = ROOT.TFile.Open("results.root")
t = f.Get("events")

for event in t:
    # this I could do with the naked variables
    print(event.var1) 
    # this I have to do if there is an intermediate branch
    # and I get the "no dictionary for class Event" warning in addition
    print(event.GetLeaf("var1").GetValue())

My question is:
Is there a middle ground?

Ideally I would like to define the TTree structure with my custom class w/o this intermediate Branch class.
As the root develops faster than the tutorials, maybe I am missing something that already exists :slight_smile:

EDIT:

For my simple example I could also do something like this:

void test(){
    int var1 = 1;
    int var2 = 2;
    int var3 = 3;

    std::map<const char*, int*> vars = {
        {"var1", &var1},
        {"var2", &var2},
        {"var3", &var3}
    };

    
    auto file = std::make_unique<TFile>("results.root", "RECREATE");
    auto tree = std::make_unique<TTree>("events", "events");
    
    for (const auto& [name, ptr] : vars) {
        tree->Branch(name, ptr);
    }

    tree->Fill();
    file->Write();
}

However, in my application the variables that I write have different types, so the actual problem I would like to improve looks more like this:

void test(){
    bool is_good_event = true;
    int n_tracks = 5;
    int n_hits = 10;
    std::string filename = "original.data";
    float true_energy = 42.f;
    std::vector<float> track_momenta = {1.f, 2.f, 3.f, 4.f, 5.f};
    std::vector<float> hit_energies = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0};
    std::vector<bool> is_good_hit = {true, true, true, false, true, true, true, false, false, true};
    // and so on
    
    auto file = std::make_unique<TFile>("results.root", "RECREATE");
    auto tree = std::make_unique<TTree>("events", "events");
    
    tree->Branch("is_good_event", &is_good_event);
    tree->Branch("n_tracks", &n_tracks);
    tree->Branch("n_hits", &n_hits);
    tree->Branch("filename", &filename);
    tree->Branch("true_energy", &true_energy);
    tree->Branch("track_momenta", &track_momenta);
    tree->Branch("hit_energies", &hit_energies);
    tree->Branch("is_good_hit", &is_good_hit);
    
    tree->Fill();
    file->Write();
}

You could use RNtuple instead, there the variable is created at the same time that you define the branch. So you can’t miss anything there.

1 Like

Another option could be to have a tree->GetListOfBranches()or tree->GetListOfLeaves() at some point, compare the elements to the ones you expect and print out the missing ones. ROOT: TTree Class Reference

1 Like

Is there an article/docs where I can read about the current status of the RNtuples?
As far as I understand they are (or were?) experimental and early in the development.
I am wondering what features are already supported out of the box, what features are still in progress? Is it an easy fix to replace TTrees in the old code with the new RNtuples? Or are there caveats one needs to take into account? And so on..
Is there a public roadmap somewhere available one could take a look?

I guess you can take a look at: ROOT Users Workshop 2025 (17-21 November 2025): Timetable · Indico