views:

431

answers:

9

I need a method helping me, to reach variables named like "comboBox1", "comboBox2" etc each by each in a loop. I'd like to change code like:

//proceed comboBox1
//proceed comboBox2
//proceed comboBox3
//proceed comboBox4
//proceed comboBox5
//proceed comboBox6

Into:

for (int i = 1; i < numberOfBoxes; i++) {
    //proceed comboBox(i)
}

I tried to find somthing like 'eval', but google didn't give anything matching. I also tried preprocessing the name with operator ## but it seems there's no way to put current integer value to the macro.

+14  A: 

The simplest solution is to put them all in an array and iterator over that:

// I've made up a type, but you get the idea.
std::vector<ComboBox *> combos;
combos.insert(comboBox1);
combos.insert(comboBox2);
combos.insert(comboBox3);
combos.insert(comboBox4);
combos.insert(comboBox5);
combos.insert(comboBox6);

Now you can iterate over combos. The main problem is that c++ doesn't have reflection. So you can't generated a string at runtime and get the address of an object or function like you can in some other languages.

EDIT: I just saw that you are using Qt. In that case, you should use:

QList<T> qFindChildren ( const QObject * obj, const QString & name );

or

QList<T> qFindChildren ( const QObject * obj, const QRegExp & regExp);

This lets you get a list based on runtime generated names. For example:

QList<QComboBox *> combos = qFindChildren(ui, QRegExp("combo[0-9]+"));

then you can just iterate over that!

Evan Teran
You're right. I was only curious if there's an alternative to this solution.
If really must (and you have to want it badly), you can generate an object system that supports a limited degree of reflection. The ROOT framework does it. Not that I recommend it, or anything.
dmckee
Of course, in fact this is what the Qt MOC system does. This way it can connect signals/slots dynamically at runtime. Anything hand rolled will have some limitations though (for example templates would be **very, very** hard to handle correctly).
Evan Teran
Yours is probably the best solution.
Tim
Nice and easy. I didn't know about that. Thank you! :)
+3  A: 

There isn't any way in C++ to identify a variable by name at runtime. Instead of using a bunch of discrete variables with names like comboBox1, comboBox2, etc., why not make an array of ComboBoxes that you can iterate over? Then your loop would look like:

for (int i = 1; i < numberOfBoxes; i++) {
   doSomething(comboBoxes[i]);
}
MattK
I was looking for something for dynamic naming of variables but it looks there's no such solution in c++. Thank you for help.
+1  A: 

You will need to store your variables in an array.

E.g

mComboBoxes[MAX_COMBOBOXES];

// initialization

for (int i = 0; i < numberOfBoxes; i++) {
    mComboBoxes[i];
}

Note that arrays have zero-based indices.

Andrew Grant
+1  A: 

Use arrays. That's what they are there for. Or containers.

T combobox_array[ N ]; // N comboboxes
for (int i = 0; i < N; ++i)
     process(combobox_array[ i ]); // note array indices are 0 to N-1

Or, with STL

vector<T> ca(N);
for_each(ca.begin(), ca.end(), do_something);
dirkgently
+4  A: 

The only way I know how to do that is by creating them in the code/dynamically and in an array. (Not through the wizard) You are not alone in finding this shortcoming of the MFC (I presume) wizard thing.

As an alternative, if your resources are sequential in the resource file (again I am assuming MFC-like implementation) you can iterate through the resource IDs to get the resources. This assumes they have sequential resource IDs. I have used this method lately. It workd fine. Not sure if it is what you are looking for or will work with your GUI.

Tim
You're right. Right now i'm navigating throught my window grid layout and finding combo boxes by coordinates. I was only curious if there's something more "low-leveled" :)
In MFC (older Win32 GUI) you can create resources statically and there is a numeric ID associated with each one. That is the ID I am referring to. I am not sure if QT also uses numbers to identify them and if you can order them to suit your needs.
Tim
No, with my level of knowlegde about QT, there're no such things like IDs to objects in the window.
Evan Teran
+1  A: 

What is "proceed"?

You need another level of indirection. Perhaps store pointers to your combo boxes in a vector that you can iterate over later.

Brian Neal
this will work as well. And probably works better than the other solutions if he wanted to define his UI elements at design time rather than run time.
Tim
+1  A: 

Is there a reason you can't have an array/vector/list of comboBox's?

std::vector<ComboBox> comboBoxes;

or better yet maintain a list of pointers to comboBoxes?

std::vector<ComboBox*> comboBoxPtrVec;
ComboBox* comboBox1 = new ComboBox();
ComboBox* comboBox2 = new ComboBox();
comboBoxPtrVec.push_back( comboBox1 );
comboBoxPtrVec.push_back( comboBox2 );

for (insigned int i = 0; i < comboBoxPtrVec.size(); ++i)
{
   // process comboBox
   comboBoxPtrVec[i]->DoSomething();
}
Doug T.
+2  A: 

I used the following approach for function registration, but you can try applying in your case, hopefully it warrants this C style workaround that doesn't respect spaces:

#define GET_COMBO_BOX(x,y) x ## y

for (int i = 1; i < numberOfBoxes; i++) 
  GET_COMBO_BOX(comboBox1,i);

This obviously will NOT do what you want as i is determined at compile time and macro are expended at preprocessing, but it will give you an idea how to pre-generate function calls.

Here is more complete example of where macro concatenation is used:

#include<iostream>
#include<string>

using namespace std;


template< class E > struct EnumNames
{
    static const char* const* names;
    static int names_size;
};


#define REGISTER_ENUM( e ) \
    const char* const* EnumNames< e >::names = e ## _names; \
    int EnumNames< e >::names_size = sizeof( e ## _names ) / sizeof( const char* );

enum ElementType { NEURON, SYNAPSE };
const char* const ElementType_names[] = { "N", "S" };
REGISTER_ENUM( ElementType )

enum TokenMainType { EP, IP, NT, AI };
const char* const TokenMainType_names[] = { "EP", "IP", "NT", "AI" };
REGISTER_ENUM( TokenMainType )

template<class E>
ostream& operator <<(ostream& os, const E& e) 
{
    if (e > EnumNames< E >::names_size) 
        cout << "Error" << endl;
    os << EnumNames< E >::names[e];
    return os;

}

template<class E>
istream& operator >>(istream& is, E& e) {
    std::string tmp_e_string;
    is >> tmp_e_string;
    for (int i = 0; i < EnumNames< E >::names_size; ++i) {
        if (tmp_e_string == EnumNames< E >::names[i])
        {
            e = E(i);
            return is;
        }

    }
    cerr << "Fehler: tmp_nntype_string: " << tmp_e_string << endl;
    return is;

}


int main (int argc, char **argv)
{
    ElementType test1(NEURON);
    cout<<string(EnumNames<ElementType>::names[test1])<<endl;

}
+3  A: 

If you're into preprocessor hacks, see the Boost.Preprocessor library:

// Shamelessly copied from the Boost docs, and only slightly
// adapted. (and probably breaking it ;)
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>

#define DO_SOMETHING(z, n, text) BOOST_PP_CAT(text, n)->DoSomething();

BOOST_PP_REPEAT_FROM_TO(0, 3, DO_SOMETHING, comboBox)

would expand to something like:

comboBox0->DoSomething();
comboBox1->DoSomething();
comboBox2->DoSomething();
Éric Malenfant