问题
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.
回答1:
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!
回答2:
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.
回答3:
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]);
}
回答4:
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();
回答5:
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;
}
回答6:
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.
回答7:
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);
回答8:
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.
回答9:
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();
}
来源:https://stackoverflow.com/questions/566603/how-to-reach-iteratively-few-variables-which-names-differ-only-by-number-in-c