25.7. G. ROOT C++ Cheat Sheet
Table of Contents
General Syntax Reminders
C++ used inside ROOT follows standard C++ syntax, with a few conveniences provided by the ROOT interpreter. This section lists the most common elements you will use in ROOT analysis code.
Basic Program and Macro Structure
A minimal standalone C++ program:
#include <iostream>
using namespace std;
int main() {
cout << "Hello ROOT C++" << endl;
return 0;
}
A minimal ROOT macro function (in myMacro.C):
void myMacro() {
cout << "Hello from ROOT macro" << endl;
}Calling the macro from the ROOT prompt:
root[] .x myMacro.C // interpreted
root[] .x myMacro.C(10, 2.5) // with arguments
root[] .L myMacro.C+ // compile with ACLiC
root[] myMacro(); // call compiled function
Use // for single line comments and / ... / for block comments.
Includes and Namespaces
Standard library includes:
#include <iostream>
#include <cmath>
#include <vector>
#include <string>
#include <algorithm>ROOT includes (when compiling with a compiler):
#include "TFile.h"
#include "TH1F.h"
#include "TTree.h"
#include "TCanvas.h"
#include "TRandom3.h"
#include "TGraph.h"
At the ROOT prompt, most ROOT headers are already available. For standard C++ you still need includes when you compile with +.
Using namespaces:
using namespace std;
In ROOT, std:: prefix is often optional but you should use it in modern code:
std::cout << "Value = " << value << std::endl;Variables and Data Types
Fundamental Types
Common C++ types for ROOT work:
int nEvents = 1000;
long bigIndex = 1000000L;
float energy = 3.14f;
double mass = 0.13957;
bool passedCut = true;
char letter = 'A';String types:
std::string name = "pion";
// ROOT C-style string for some older interfaces
char cname[16] = "histName";Pointer syntax (very common with ROOT objects):
TH1F *h1 = new TH1F("h1", "Title", 100, 0, 10);
TFile *f = TFile::Open("file.root", "READ");Type Conversions
Explicit casts:
double x = 3.7;
int i = (int)x; // truncates to 3
int j = int(x); // sameAutomatic promotion in expressions:
int n = 5;
double y = 2.0;
double z = n * y; // n is promoted to doubleOperators
Arithmetic Operators
a + b; // addition
a - b; // subtraction
a * b; // multiplication
a / b; // division
a % b; // remainder (integers)Increment and decrement:
i++; // post-increment
++i; // pre-increment
i--; // post-decrement
--i; // pre-decrementCompound assignment:
x += 5;
y -= 2;
z *= 3;
w /= 4;Comparison and Logical Operators
Comparison:
a == b; // equal
a != b; // not equal
a < b; // less than
a <= b; // less or equal
a > b; // greater than
a >= b; // greater or equalLogical:
a && b; // logical AND
a || b; // logical OR
!a; // logical NOTCombined logical expressions, very common in cuts:
if (pt > 0.5 && fabs(eta) < 2.5 && charge != 0) { ... }Control Flow
if, else if, else
if (x > 0) {
cout << "Positive" << endl;
} else if (x < 0) {
cout << "Negative" << endl;
} else {
cout << "Zero" << endl;
}Pay attention to braces when you have more than one statement.
for Loops
Standard index loop:
for (int i = 0; i < 10; ++i) {
cout << "i = " << i << endl;
}
Loop over a std::vector by index:
std::vector<double> v = {1.0, 2.0, 3.0};
for (size_t i = 0; i < v.size(); ++i) {
cout << v[i] << endl;
}Range based loop (C++11 and later, works in modern ROOT):
for (auto value : v) {
cout << value << endl;
}while Loops
int i = 0;
while (i < 10) {
cout << "i = " << i << endl;
++i;
}switch
Useful for simple integer or enum cases:
int code = 2;
switch (code) {
case 1:
cout << "One";
break;
case 2:
cout << "Two";
break;
default:
cout << "Other";
break;
}Functions
Defining and Calling Functions
General pattern:
return_type functionName(arg_type1 arg1, arg_type2 arg2) {
// body
return value; // if not void
}Example:
double kineticEnergy(double mass, double momentum) {
return momentum * momentum / (2.0 * mass);
}Calling:
double ke = kineticEnergy(0.5, 1.2);Functions with no return value:
void printEvent(int i) {
cout << "Event " << i << endl;
}Function prototypes are needed before use in compiled code:
double myFunc(double x); // declaration
double myFunc(double x) { // definition
return x * x;
}ROOT interpreter is often more forgiving, but for compiled macros you must respect correct declaration order or use header files.
Passing by Value and by Reference
By value:
void incrementByValue(int x) {
x += 1; // original variable unchanged
}By reference:
void incrementByRef(int &x) {
x += 1; // original variable is changed
}Using references is common for output parameters:
void computeMeanRMS(const TH1 *h, double &mean, double &rms) {
mean = h->GetMean();
rms = h->GetRMS();
}Arrays and std::vector
C Arrays
Declaration:
int a[5]; // uninitialized
double b[3] = {1,2,3}; // initializedIndex from 0:
a[0] = 10;
cout << b[2] << endl;
Arrays are often used with TGraph:
const int n = 3;
double x[n] = {1.0, 2.0, 3.0};
double y[n] = {2.0, 4.0, 6.0};
TGraph *gr = new TGraph(n, x, y);std::vector
Declaration and initialization:
std::vector<double> v; // empty
v.push_back(1.0);
v.push_back(2.0);
std::vector<int> ids = {11, 13, 211};Access:
cout << v[0] << endl;
cout << v.at(1) << endl; // bounds-checked
size_t n = v.size();Iteration:
for (size_t i = 0; i < v.size(); ++i) {
cout << v[i] << endl;
}
for (auto value : v) {
cout << value << endl;
}
In TTrees, std::vector<T> is commonly used as a branch type for variable length data.
ROOT Specific C++ Idioms
Creating and Using ROOT Objects
Use new when you want objects to live on the heap:
TH1F *h = new TH1F("h", "My hist", 100, 0, 10);
h->Fill(1.0);
h->Draw();You can also create objects on the stack in macros:
void myHist() {
TH1F h("h", "My hist", 100, 0, 10);
h.Fill(2.0);
h.Draw();
}Pay attention to ROOT ownership rules when working with files, canvases, and histograms attached to directories.
Accessing Members and Methods
Use . for objects, -> for pointers:
TH1F h("h", "title", 100, 0, 1);
TH1F *hp = &h;
h.Fill(0.5); // object
hp->Fill(0.5); // pointerInput and Output
C++ stream output:
cout << "Mean = " << h.GetMean() << endl;
ROOT specific Print:
h.Print();
Formatted output via Form:
cout << Form("Entries = %d, Mean = %.3f", int(h.GetEntries()), h.GetMean()) << endl;Pointers, References, and auto
Pointers
Declaration and usage:
TFile *f = TFile::Open("data.root", "READ");
if (!f || f->IsZombie()) {
cout << "Could not open file" << endl;
return;
}Dereferencing:
TH1F *h = (TH1F*)f->Get("h1");
if (h) {
cout << "Entries: " << h->GetEntries() << endl;
}References
Binding a reference:
TH1F &href = *h; // href is another name for *h
href.Fill(3.14);References cannot be null. They are useful for function parameters.
auto Type Deduction
Handy with long ROOT type names:
auto c = new TCanvas("c","c",800,600);
auto h1 = new TH1F("h1","h1",100,0,1);
auto tree = (TTree*)f->Get("events");
auto lets the compiler deduce the type from the initializer. Use it especially for iterator types.
Common ROOT Loop Patterns
Loop over Histogram Bins
int nbins = h->GetNbinsX();
for (int i = 1; i <= nbins; ++i) {
double x = h->GetBinCenter(i);
double cont = h->GetBinContent(i);
double err = h->GetBinError(i);
}
Note that ROOT histogram bins start at 1 and go to nbins. Bin 0 is underflow and bin nbins+1 is overflow.
Loop over TTree Entries
TTree *t = (TTree*)f->Get("tree");
double energy;
t->SetBranchAddress("energy", &energy);
Long64_t nentries = t->GetEntries();
for (Long64_t i = 0; i < nentries; ++i) {
t->GetEntry(i);
if (energy > 10.0) {
h->Fill(energy);
}
}Memory and new/delete Basics
ROOT often manages memory for you, but in modern C++ you still need to understand dynamic allocation.
TH1F *h = new TH1F("h","h",100,0,1);
// use h
delete h; // free memory when you created it with new and ROOT does not own it
If an object is written to a TFile or created on a TCanvas, ROOT may take ownership. In many analyses you rely on ROOT cleanup at end of session, but for long running programs you should manage lifetimes carefully.
Always match new with delete for objects you own. Do not delete objects that ROOT owns, such as histograms retrieved from a TFile directory, unless you explicitly detached or cloned them. Incorrect deletes can lead to crashes.
Preprocessor and Macros
Preprocessor Directives
Conditional compilation:
#ifdef DEBUG
cout << "Debug info" << endl;
#endifInclude guards for headers:
#ifndef MYHEADER_H
#define MYHEADER_H
// declarations
#endifSimple macro definitions:
#define N_BINS 100
#define PI 3.141592653589793
Use constants or constexpr in modern C++ instead of preprocessor where possible:
constexpr int N_BINS = 100;Common Math Functions
Include <cmath>:
#include <cmath>
double y = std::sqrt(x);
double z = std::sin(phi);
double a = std::exp(-x);
double b = std::log(x); // natural log
double c = std::log10(x); // base 10 log
double d = std::fabs(x); // absolute value
With ROOT you also have TMath:
#include "TMath.h"
double y = TMath::Sqrt(x);
double z = TMath::Sin(phi);String Handling
std::string operations:
std::string name = "run";
name += "_42";
cout << name << endl;
if (name == "run_42") { ... }
const char *cname = name.c_str(); // for ROOT APIs that need const char*
Formatting with Form or std::to_string:
int run = 42;
std::string hname = "h_run_" + std::to_string(run);
TH1F *h = new TH1F(hname.c_str(), "title", 100, 0, 1);
TH1F *h2 = new TH1F(Form("h_run_%d", run), "title", 100, 0, 1);Error Handling Patterns
Simple checks:
TFile *f = TFile::Open("input.root");
if (!f || f->IsZombie()) {
cerr << "Error opening file" << endl;
return;
}
TH1F *h = (TH1F*)f->Get("hData");
if (!h) {
cerr << "Histogram hData not found" << endl;
return;
}Guard against division by zero:
double denom = h->Integral();
if (denom > 0) {
h->Scale(1.0 / denom);
} else {
cerr << "Cannot normalize, integral is zero" << endl;
}
Always check pointers returned by ROOT factory methods (like Get, Open, GetHistogram) before dereferencing them. Attempting to use a null pointer is a common source of segmentation faults.
Quick Reference Tables
Common fundamental types:
| Purpose | Type | Typical usage example |
|---|---|---|
| Integer counts | int | number of events, bins |
| Large integer index | long | entry index, sizes |
| Single precision | float | detector coordinates, quick vars |
| Double precision | double | energies, masses, physics calc |
| True or false | bool | cuts, flags |
| Character | char | single letter code |
| Text string | std::string | filenames, labels |
Common loop patterns:
| Task | Pattern example |
|---|---|
| Simple counter | for (int i = 0; i < N; ++i) { ... } |
| Over vector elements | for (auto v : vec) { ... } |
| TTree entries | for (Long64_t i = 0; i < n; ++i) { t->GetEntry(i); } |
| Histogram bins | for (int i = 1; i <= nb; ++i) { ... } |
These patterns and snippets cover most C++ constructs that you will routinely use with ROOT.
Views: 10
KAHIBARO