For cycle with strings

Hello again,
I am trying to make a for loop that would help me shorten my code. I have several histograms that have almost same name, but differ in suffix. I would like the loop to take the histogram, set all the things like color, scaling, filling etc. So far I have managed to make a piece of code that might do it (hopefully… I am new to programming and struggle a lot), but I get error: no member named ‘SetFillStyle’ in ‘std::__cxx11::basic_string’ name.SetFillStyle(1001);.

 std::vector<string> names {"ttbar1_pt", "ttbar1_eta", "ttbar1_phi"};
  for (const auto& name: names){
   name->SetFillStyle(1001);
//...color, scaling...
}

I could make that pt, eta, phi a number (ttbar1_1, ttbar1_2,…) and know how to do it, but I prefer having letters there, because they make clear what does the variable stay for.
Any ideas how to do that so that it works?
Thank you!

Hi Marta,
“where” are the histograms? Are they stored in a ROOT file or are they objects that you have created earlier in your script?

The error you get is because name, within the for loop, is a std::string, so you are trying to call SetFillStyle on a std::string and that obviously doesn’t work. You need something like

for (const auto &name: names) {
   TH1* histo = nullptr; 
   gDirectory->GetObject(name.c_str(), histo);
   histo->SetFillStyle(1001);
   // ...color, scaling...
}

i.e. you need to retrieve the histogram object from ROOT using the name before you can do anything with it.

Cheers,
Enrico

Thank you for your response! I get what is wrong there now. Unfortunatelly, your idea would not work well for me, because each of the histograms is getting something else from the file, like this:

TFile *ttbar = TFile::Open("ttbar_out.root");
     TH1D *ttbar1_pt = (TH1D*)ttbar->Get("h_muon1_pt");
     TH1D *ttbar1_eta = (TH1D*)ttbar->Get("h_muon1_eta");
     TH1D *ttbar1_phi = (TH1D*)ttbar->Get("h_muon1_phi");

But do you think I could get something like std::vector<TH1D>? I mean, to put there histograms instead of strings.

If your histograms are all of the same type (TH1D) and they are all in the same file, I don’t see why my code above shouldn’t work:

std::vector<string> names({"ttbar1_pt", "ttbar1_eta", "ttbar1_phi"});
TFile ttbar("ttbar_out.root");
for (const auto &name: names) {
   TH1D* histo = nullptr; 
   ttbar.GetObject(name.c_str(), histo);
   histo->SetFillStyle(1001);
   // ...color, scaling...
}

You can indeed put them in a std::vector<TH1D*> (note the *, it’s a vector of pointers to histograms, not a vector of histograms):

std::vector<string> names({"ttbar1_pt", "ttbar1_eta", "ttbar1_phi"});
TFile ttbar("ttbar_out.root");
std::vector<TH1D*> histos;
for (const auto &name: names)
   histos.push_back(static_cast<TH1D*>(ttbar.Get(name.c_str())));

for (auto histo: histos) {
   histo->SetFillStyle(1001);
   // ...color, scaling...
}

I haven’t tested the snippets, but they should give you an idea.
Cheers,
Enrico

When I try to compile this, I get error: no matching constructor for initialization of 'std::vector<TH1D *>' std::vector<TH1D*> histos;. So I try to include TH1::TH1D, but that gives me error, too, saying no member named ‘TH1D’ in 'TH1. (Sorry if it is stupid question, I am a beginner learning as I go)

You might need to #include <TH1D.h>

If that doesn’t work, please post the minimal, complete program that reproduces the error together with the full error message.

I tried it, doesn’t help.
So, here is the code:
mwe.C (932 Bytes)

And here is the error message:
error.txt (2.7 KB)
(I put it in a separate text file, because it’s longer then the code…)

Hi Marta,
thank you for the minimal reproducer, very helpful!

Here’s the main issue (the code also contains typos, e.g. histo.push_back instead of histos.push_back, which the compiler will point out):

for (const auto& name: names){
    histo.push_back(static_cast<TH1D*>(name))

names is a vector<string>, so in this loop name is a string.
You can’t convert the string "ttbar1_pt" to the variable ttbar1_pt!

Here’s one way to do what you want. Make sure you understand what’s happening, C++ can be tricky – if not, just ask:

void mwe(){
  TFile *ttbar = TFile::Open("ttbar_out.root");
  std::vector<std::string> names{"h_muon1_pt", "h_muon1_eta", "h_muon1_phi"};
  std::vector<TH1D*> histos; // vector of pointers to histograms
 
  // fill the vector of TH1D pointers and do things that you want to do for each histogram
  for (const auto& name: names) {
    TH1D* h_ptr = static_cast<TH1D*>(ttbar->Get(name.c_str()));
    histos.push_back(h_ptr);
    h_ptr->SetFillStyle(1001);
    // ...
  }

  TCanvas* Canvas1_pt = new TCanvas("Canvas1_pt", "muon1 pt", 1000, 800);
  histos[0]->Draw("same hist"); // I chose one of them
}

Hope this helps!
Enrico

It works! Hooray!
Thank you very much!

1 Like