1

I need to access several objects of a class called TH2D, which is defined by the program root (https://root.cern.ch/drupal/), with a for loop to use two of its methods.

The name of the objects are in the form run_0, run_1, run_2, etc..

defined like this:

TH2F* run_0 = new TH2F( "runID = 0", "projection during the first run",
            COLUMN, -COLUMN_BOUND, COLUMN_BOUND,
            RAW, -RAW_BOUND, RAW_BOUND );

inside the for loop for the first object I simply do:

int bin = run_0->FindBin(j, k);
double Nphotons = run_0->GetBinContent(bin);

What do I need to do to have something like this inside a for loop in c++ automatically changing run_0 to run_1 to run_2, etc...?

1st iteration -

int bin = run_0->FindBin(j, k);
double Nphotons = run_0->GetBinContent(bin);

2nd iteration -

int bin = run_1->FindBin(j, k);
double Nphotons = run_1->GetBinContent(bin);

SOLVED WITH:

TH2F* run[8];
run[0] = new TH2F( "runID = 0", "projection during the first run",
            COLUMN, -COLUMN_BOUND, COLUMN_BOUND,
            RAW, -RAW_BOUND, RAW_BOUND );

....

for( Int_t id = 0; id != 8; ++id)
{
int bin = run[id]->FindBin(j, k);
double Nphotons = run[id]->GetBinContent(bin);
}
  • 2
    Put pointers to them into an array. Iterate the array. Assume run_0, run_1, run_2 etc are either already in a collection or are defined somewhere. – Robinson Apr 22 '15 at 11:43
  • Do you create these objects in your own code? Then you will want to put them (or pointers to them) directly in a container instead of naming individual objects. If, otoh, the objects are in somebody elses library, you can still store pointers to them in a container if you must traverse them more than once. Iterating/looping over containers is trivial. – Peter - Reinstate Monica Apr 22 '15 at 12:19
  • I have created the objects in my code with: TH2F* run_0 = new TH2F( "runID = 0, "projection during the first run", COLUMN, -COLUMN_BOUND, COLUMN_BOUND, RAW, -RAW_BOUND, RAW_BOUND ); for example – Andre Monteiro Apr 22 '15 at 12:21
  • Do you have a good reason for newing these objects? – juanchopanza Apr 22 '15 at 12:32
  • the objects are basically an histogram of an image projection and I make new TH2F before, because afterwards I will need to fill it in with the information that I will extract from a file. – Andre Monteiro Apr 22 '15 at 13:00
  • It doesn't matter what the objects are. Do you need to dynamically allocate them with `new`? – juanchopanza Apr 22 '15 at 21:13

2 Answers2

0

Disclaimer: For simplicity, I will assume tokens in the form run_n are functions, rather than objects. This answer should apply to objects, too.

The only way to place a number like this in an identifier is by macro expansion:

#define run(n) run_##n()
run(0); // Good. Will expand to run_0();

Unfortunately, that just doesn't work with iteration:

#include <iostream>

void run_0(){std::cout << 0;}
void run_1(){std::cout << 1;}

int main() {
  for (int i=0;i<2;++i) {
    run(i); // Bad. Will expand to run_i() and probably cause a compiler error.
  }
}

And then you have the problem that macros don't iterate or have recursion. If you write this:

#define run(start,end) run(start,end-1) run_##end();

It will not expand into an infinite recursion, as someone might have guessed. Instead:

run(0,1); // Will expand to...
run(0, 1-1) run_1(); // And stop expanding here.

Since no function run() that takes two arguments exists (Hope it doesn't), it will cause a compiler error.

Your only bet, then, is having a list to all possible functions, and then iterate that:

void (*run[10])() = {run_0, run_1, run_2, run_3, run_4, run_5, run_6, run_7, run_8, run_9};

int main() {
  for (int i=0;i<10;++i) {
    run[i](); // Now it works!
  }
}

The answer to your question, then, is: No, it's not possible. There are not-so-optimal workarounds, of course, but - in essence - it is not possible to iterate over a group of identifiers by name.

EDIT: Since you are defining the objects, and can afford to change how to define them, this is quite simple:

P.S.: Since you are "newing" the objects, I'm going to use new. Preferably, you should implement the Rule of Three and allocate them statically, or use a smart pointer instead.

TH2F* run[10]; // Up to 10 objects. Probably better to use an std::vector instead.
run[0] = new TH2F( "runID = 0", "projection during the first run", COLUMN, -COLUMN_BOUND, COLUMN_BOUND, RAW, -RAW_BOUND, RAW_BOUND );
run[1] = new TH2F( "runID = 1", "projection during the second run", COLUMN, -COLUMN_BOUND, COLUMN_BOUND, RAW, -RAW_BOUND, RAW_BOUND );

Then you can iterate this array and do what you need.

Community
  • 1
  • 1
Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
  • So what you are saying is that I cannot do what I want automatically? P.S. I have edited my question, so I think you can have a better understanding of what I want to do :) – Andre Monteiro Apr 22 '15 at 13:58
  • Yes, that't it. It's not possible, unfortunately. For any solution, you will have to list all objects somewhere. Unless, of course **you** are defining the objects in the form `run_n` and are able to change this to the more iteration friendly `run[n]` (i.e.: An array of runs, instead of several separate ones) – Not a real meerkat Apr 22 '15 at 14:04
  • Ok I think I got it. I am defining each object like this: `TH2F* run_0 = new TH2F( "runID = 0", "projection during the first run", COLUMN, -COLUMN_BOUND, COLUMN_BOUND, RAW, -RAW_BOUND, RAW_BOUND ); TH2F* run_1 = new TH2F( "runID = 1", "projection during the second run", COLUMN, -COLUMN_BOUND, COLUMN_BOUND, RAW, -RAW_BOUND, RAW_BOUND );` – Andre Monteiro Apr 22 '15 at 14:11
  • how can put that in an array? – Andre Monteiro Apr 22 '15 at 14:12
  • Thank you Cassio! I am feeling like a real noob :) With your sugestion I was able not only to get what I needed , but to reduce my code as well :) – Andre Monteiro Apr 22 '15 at 14:46
-2

In essence, put them in an array:

Thingy * thing1 = new Thingy();
Thingy * thing2 = new Thingy();
std::vector<Thingy *> things;
things.push_back(thing1);
things.push_back(thing2);
for(auto thing : things)
{
    thing->doStuff();
}

In reality consider using smart pointers instead so you don't forget to delete them afterwards.

Edit because people don't like it leaking:

std::shared_ptr<Thingy> thing1(new Thingy);
std::shared_ptr<Thingy> thing2(new Thingy);
std::vector<std::shared_ptr<Thingy> > things;
things.push_back(thing1);
things.push_back(thing2);
for(auto thing : things)
{
    thing->doStuff();
}

and if you really want:

std::vector<std::shared_ptr<Thingy> > things;
for (int i = 0; i < count; ++i)
{
    things->push_back(std::make_shared<Thingy>());
}
for(auto & thing : things)
{
    thing->doStuff();
}
Andy Newman
  • 1,178
  • 2
  • 8
  • 23
  • I think the question was can you do this without having to manually enumerate the objects. Maybe with the ## operator in a macro or some other way. – Benjy Kessler Apr 22 '15 at 11:47
  • memory leak because I was trying to provide the information the OP needed in the simplest form possible, rather than a complete program – Andy Newman Apr 22 '15 at 12:17
  • Why new the objects in the first place? Where does it say OP needs dynamic allocation? – juanchopanza Apr 22 '15 at 12:31
  • OP says "TH2F* run_0 = new TH2F" as the way he creates objects, and he isn't really asking for a new way to create them - he is asking how to iterate over them. I'm trying to answer the question that was asked. – Andy Newman Apr 22 '15 at 14:06