435,395 Members | 2,524 Online + Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,395 IT Pros & Developers. It's quick & easy.

How to pass the field name of a struct as a parameter?

 P: n/a There are some sturct arrays (or struct vectors), such as: struct S1_t { double keyField ; ..// other fields } S1_t s1; std::vector vecS1; I need to find the index of the item in array "s1" or vector "vecS1" which has the minimal value in field "keyField". Some codes could be: //---------------------------------------------- double minvalue = s1; for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++) .... { if (s1[i].keyField < minvalue ) { minvalue =s1[i].keyField; index = i; } } //---------------------------------------------- However, since there are many such arrays(vectors) with different struct type (so, with different "field" name) How can I define a function (or MACRO?) to get the index of the minumal item (by comparing the the value of a key field)? such as MIN(struct_array_variable, field_name) e.g. index = MIN(s1, keyField); to get the index of array "s1" with a minimal value of "keyField". And , index = MIN(s2,f2) get that of "s2" (defined as follows:) struct s2_t { int f2; // other fields } s2_t s2 A similar function is FIND(struct_array, field, value) to find the index from array "struct_array", where struct_array[index].field == value; I think that the sticking point is that how to pass the field name as a parameter? isn't it? Could anyone help me? Thank you very much! Jun 28 '06 #1
17 Replies

 P: n/a Jellicle wrote: There are some sturct arrays (or struct vectors), such as: struct S1_t { double keyField ; ..// other fields } S1_t s1; std::vector vecS1; I need to find the index of the item in array "s1" or vector "vecS1" which has the minimal value in field "keyField". Some codes could be: //---------------------------------------------- double minvalue = s1; You mean double minvalue = s1.keyField; ?? for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++) You mean for (int i=1; i< ... ?? ... { if (s1[i].keyField < minvalue ) { minvalue =s1[i].keyField; index = i; } } //---------------------------------------------- However, since there are many such arrays(vectors) with different struct type (so, with different "field" name) How can I define a function (or MACRO?) to get the index of the minumal item (by comparing the the value of a key field)? such as MIN(struct_array_variable, field_name) e.g. index = MIN(s1, keyField); to get the index of array "s1" with a minimal value of "keyField". And , index = MIN(s2,f2) get that of "s2" (defined as follows:) struct s2_t { int f2; // other fields } s2_t s2 A similar function is FIND(struct_array, field, value) to find the index from array "struct_array", where struct_array[index].field == value; I think that the sticking point is that how to pass the field name as a parameter? isn't it? No, it isn't. The sticking point is to learn about pointers to members and how they are used. V -- Please remove capital 'A's when replying by e-mail I do not respond to top-posted replies, please don't ask Jun 28 '06 #2

 P: n/a you can get the offset of a field f in a struct s using the following well-known trick: #define OFFSET(s,f) &((struct s *) 0)->f Once you have the offset, it becomes straightforward to solve your problem. -Shimin Jellicle wrote: There are some sturct arrays (or struct vectors), such as: struct S1_t { double keyField ; ..// other fields } S1_t s1; std::vector vecS1; I need to find the index of the item in array "s1" or vector "vecS1" which has the minimal value in field "keyField". Some codes could be: //---------------------------------------------- double minvalue = s1; for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++) ... { if (s1[i].keyField < minvalue ) { minvalue =s1[i].keyField; index = i; } } //---------------------------------------------- However, since there are many such arrays(vectors) with different struct type (so, with different "field" name) How can I define a function (or MACRO?) to get the index of the minumal item (by comparing the the value of a key field)? such as MIN(struct_array_variable, field_name) e.g. index = MIN(s1, keyField); to get the index of array "s1" with a minimal value of "keyField". And , index = MIN(s2,f2) get that of "s2" (defined as follows:) struct s2_t { int f2; // other fields } s2_t s2 A similar function is FIND(struct_array, field, value) to find the index from array "struct_array", where struct_array[index].field == value; I think that the sticking point is that how to pass the field name as a parameter? isn't it? Could anyone help me? Thank you very much! Jun 28 '06 #3

 P: n/a Victor Bazarov wrote: Jellicle wrote: Some codes could be: //---------------------------------------------- double minvalue = s1; You mean double minvalue = s1.keyField; ?? for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++) You mean for (int i=1; i< ... ?? You are right exactly :-). (and I should check if vecS1 exists in a vector :-) ) But what I puzzled is that how to handle the various types of structs and fields in different arrays or vectors. Jun 28 '06 #4

 P: n/a Shimin wrote: you can get the offset of a field f in a struct s using the following well-known trick: Please don't top-post. See item four in the FAQ below: Brian Jun 28 '06 #5

 P: n/a Shimin wrote: you can get the offset of a field f in a struct s using the following well-known trick: #define OFFSET(s,f) &((struct s *) 0)->f Once you have the offset, it becomes straightforward to solve your problem. -Shimin First, don't top-post. It is considered rude. Second, don't use this trick unless you must. (Generally, if you must, it means your design is flawed.) A better solution would use a function to extract the proper key. Something like: struct S1 { int key1; /*...*/ }; struct S2 { int key2; /*...*/ }; int GetKey( const S1& s ) { return s.key1; } int GetKey( const S2& s ) { return s.key2; } template int MinKey( const vector& v ) { if( v.size() == 0 ) return -1; int min = GetKey( v ); for( vector::const_iterator i = v.begin()+1; i != v.end(); ++i ) { if( GetKey( *i ) < min ) min = GetKey( *i ); } return min; } If the keys are also different types, you could still do it, but it would have to be a little fancier. Cheers! --M Jun 28 '06 #6

 P: n/a Shimin åéï¼ you can get the offset of a field f in a struct s using the following well-known trick: #define OFFSET(s,f) &((struct s *) 0)->f Once you have the offset, it becomes straightforward to solve your problem. -Shimin Thank you! Would you explain it in detail? 1)how to implement something like : if (s1[i].keyField < minvalue ) 2) Is it possible to return a value, so I can write a compact and beautiful statement in my codes: index = MIN(s1, field); But I am afrad that a macro can not return a value. Jun 28 '06 #7

 P: n/a Jellicle wrote: There are some sturct arrays (or struct vectors), such as: struct S1_t { double keyField ; ..// other fields } S1_t s1; std::vector vecS1; I need to find the index of the item in array "s1" or vector "vecS1" which has the minimal value in field "keyField". Some codes could be: //---------------------------------------------- double minvalue = s1; for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++) ... { if (s1[i].keyField < minvalue ) { minvalue =s1[i].keyField; index = i; } } //---------------------------------------------- However, since there are many such arrays(vectors) with different struct type (so, with different "field" name) How can I define a function (or MACRO?) to get the index of the minumal item (by comparing the the value of a key field)? such as MIN(struct_array_variable, field_name) e.g. index = MIN(s1, keyField); to get the index of array "s1" with a minimal value of "keyField". A STL style solution using iterators would be: #include template Iterator minfield(Iterator start, const Iterator &end, T std::iterator_traits::value_type::*p) { T acc = (*start).*p; Iterator min_i(start); ++start; while(start != end) { const T &r = (*start).*p; if(r < acc) { acc = r; min_i = start; } ++start; } return min_i; } Usage would be: index = minfield(s1, s1 + 100, &S1_t::keyfield) - s1; or index = minfield(vecS1.begin(), vecS1.end(), &S1_t::keyfield) - vecS1.begin(); Note that the function assumes that there is at least one value in the range [start, end). Jun 29 '06 #8

 P: n/a Jellicle wrote: Shimin åéï¼ you can get the offset of a field f in a struct s using the following well-known trick: #define OFFSET(s,f) &((struct s *) 0)->f Once you have the offset, it becomes straightforward to solve your problem. -Shimin Thank you! Would you explain it in detail? To repeat from my post above, this trick should be avoided. See my post and Markus Schoder's for two better options. 1)how to implement something like : if (s1[i].keyField < minvalue ) 2) Is it possible to return a value, so I can write a compact and beautiful statement in my codes: index = MIN(s1, field); But I am afrad that a macro can not return a value. Technically, a macro doesn't return anything. Macros are not functions (despite the similar looking syntax); they are text substitutions. If the text you substitute evaluates to a value, then it can be assigned to some variable like you want to here. If it does not, then it cannot be. For instance: #define A 42 #define B { cout << "Hello"; } int main() { int val1 = A; // Ok: becomes "int val1 = 42;" int val2 = B; // Error: becomes "int val2 = { cout << "Hello"; };" } However, macros should be avoided when possible. See this FAQ and its references for reasons why: http://www.parashift.com/c++-faq-lit...s.html#faq-9.5 Cheers! --M Jun 29 '06 #9

 P: n/a Markus Schoder wrote: A STL style solution using iterators would be: #include template Iterator minfield(Iterator start, const Iterator &end, T std::iterator_traits::value_type::*p) { T acc = (*start).*p; Iterator min_i(start); ++start; while(start != end) { const T &r = (*start).*p; if(r < acc) { acc = r; min_i = start; } ++start; } return min_i; } Usage would be: index = minfield(s1, s1 + 100, &S1_t::keyfield) - s1; or index = minfield(vecS1.begin(), vecS1.end(), &S1_t::keyfield) - vecS1.begin(); Note that the function assumes that there is at least one value in the range [start, end). Thank you and everyone! But I cannot compile the codes successfully in Visual C++ 6.0. It said that "error C2653: 'value_type' : is not a class or namespace name" on this line: const Iterator &end, T std::iterator_traits::value_type::*p) if I remove "::" between "value_type" and "*p", it says: "error C2143: syntax error : missing ',' before '*' " Since I don't understand this code line exactly, I don't know how to correct it. Would you help me? Thank you! Jun 29 '06 #10

 P: n/a Jellicle wrote: But I cannot compile the codes successfully in Visual C++ 6.0. You probably need a newer compiler. Jun 29 '06 #11

 P: n/a Markus Schoder wrote: Jellicle wrote: But I cannot compile the codes successfully in Visual C++ 6.0. You probably need a newer compiler. Try this (I didn't test it, but it compiles :-) ). I think it should work with VC++ 6, and it works with keys of different types and different names: #include #include using namespace std; template Iterator minfield( Iterator start, const Iterator &end, key_type T::*p) { key_type acc = (*start).*p; Iterator min_i(start); ++start; while(start != end) { const key_type &r = (*start).*p; if(r < acc) { acc = r; min_i = start; } ++start; } return min_i; } struct S1 { typedef int key_type; key_type key1; S1( key_type key ) : key1(key) {} }; struct S2 { typedef double key_type; key_type key2; S2( key_type key ) : key2(key) {} }; int main() { std::vector v1; v1.push_back( S1(1) ); v1.push_back( S1(5) ); v1.push_back( S1(-10) ); v1.push_back( S1(3) ); vector::const_iterator m1 = minfield( v1.begin(), v1.end(), &S1::key1 ); assert( m1->key1 == -10 ); std::vector v2; v2.push_back( S2(1.1) ); v2.push_back( S2(5.1) ); v2.push_back( S2(-1.1) ); v2.push_back( S2(3.1) ); vector::const_iterator m2 = minfield( v2.begin(), v2.end(), &S2::key2 ); assert( m2->key2 == -1.1 ); return 0; } Cheers! --M Jun 29 '06 #12

 P: n/a mlimber åéï¼ Jellicle wrote: Shimin åéï¼ you can get the offset of a field f in a struct s using the following well-known trick: #define OFFSET(s,f) &((struct s *) 0)->f Once you have the offset, it becomes straightforward to solve your problem. -Shimin Thank you! Would you explain it in detail? To repeat from my post above, this trick should be avoided. See my post and Markus Schoder's for two better options. 1)how to implement something like : if (s1[i].keyField < minvalue ) 2) Is it possible to return a value, so I can write a compact and beautiful statement in my codes: index = MIN(s1, field); But I am afrad that a macro can not return a value. Technically, a macro doesn't return anything. Macros are not functions (despite the similar looking syntax); they are text substitutions. If the text you substitute evaluates to a value, then it can be assigned to some variable like you want to here. If it does not, then it cannot be. For instance: #define A 42 #define B { cout << "Hello"; } int main() { int val1 = A; // Ok: becomes "int val1 = 42;" int val2 = B; // Error: becomes "int val2 = { cout << "Hello"; };" } However, macros should be avoided when possible. See this FAQ and its references for reasons why: http://www.parashift.com/c++-faq-lit...s.html#faq-9.5 Thank you! And I found the url link you gave was quite useful for me:) I think that the (only?) good feature of a macro is that it works on "compiling time". so I can replace the codes ( not the data). It maybe save some similar codes. for example, #define FIELDNAME( field) field mystruct.FIELDNAME(myfield) ==> mystruct.myfield Cheers! --M Jun 29 '06 #13

 P: n/a mlimber åéï¼ Markus Schoder wrote: Jellicle wrote: But I cannot compile the codes successfully in Visual C++ 6.0. You probably need a newer compiler. Try this (I didn't test it, but it compiles :-) ). I think it should work with VC++ 6, and it works with keys of different types and different names: #include #include using namespace std; template Iterator minfield( Iterator start, const Iterator &end, key_type T::*p) { key_type acc = (*start).*p; Iterator min_i(start); ++start; while(start != end) { const key_type &r = (*start).*p; if(r < acc) { acc = r; min_i = start; } ++start; } return min_i; } struct S1 { typedef int key_type; key_type key1; S1( key_type key ) : key1(key) {} }; struct S2 { typedef double key_type; key_type key2; S2( key_type key ) : key2(key) {} }; int main() { std::vector v1; v1.push_back( S1(1) ); v1.push_back( S1(5) ); v1.push_back( S1(-10) ); v1.push_back( S1(3) ); vector::const_iterator m1 = minfield( v1.begin(), v1.end(), &S1::key1 ); assert( m1->key1 == -10 ); std::vector v2; v2.push_back( S2(1.1) ); v2.push_back( S2(5.1) ); v2.push_back( S2(-1.1) ); v2.push_back( S2(3.1) ); vector::const_iterator m2 = minfield( v2.begin(), v2.end(), &S2::key2 ); assert( m2->key2 == -1.1 ); return 0; } Cheers! --M Thank you, mlimber. I am really grateful for you kind help! Yes, it works well in VC6 :) However, my question is that the structs were defined by other people, and were used somewhere else. So I cannot redefine them. Jun 29 '06 #14

 P: n/a Jellicle wrote: mlimber åéï¼ Markus Schoder wrote: Jellicle wrote: > But I cannot compile the codes successfully in Visual C++ 6.0. You probably need a newer compiler. Try this (I didn't test it, but it compiles :-) ). I think it should work with VC++ 6, and it works with keys of different types and different names: #include #include using namespace std; template Iterator minfield( Iterator start, const Iterator &end, key_type T::*p) { key_type acc = (*start).*p; Iterator min_i(start); ++start; while(start != end) { const key_type &r = (*start).*p; if(r < acc) { acc = r; min_i = start; } ++start; } return min_i; } struct S1 { typedef int key_type; key_type key1; S1( key_type key ) : key1(key) {} }; struct S2 { typedef double key_type; key_type key2; S2( key_type key ) : key2(key) {} }; int main() { std::vector v1; v1.push_back( S1(1) ); v1.push_back( S1(5) ); v1.push_back( S1(-10) ); v1.push_back( S1(3) ); vector::const_iterator m1 = minfield( v1.begin(), v1.end(), &S1::key1 ); assert( m1->key1 == -10 ); std::vector v2; v2.push_back( S2(1.1) ); v2.push_back( S2(5.1) ); v2.push_back( S2(-1.1) ); v2.push_back( S2(3.1) ); vector::const_iterator m2 = minfield( v2.begin(), v2.end(), &S2::key2 ); assert( m2->key2 == -1.1 ); return 0; } Cheers! --M Thank you, mlimber. I am really grateful for you kind help! Yes, it works well in VC6 :) However, my question is that the structs were defined by other people, and were used somewhere else. So I cannot redefine them. There should be no need to alter the structs (I had initially added the key_type to them but realized I didn't need to). If the structs looked like this, it would work the same: struct S1 { int key1; // ... whatever else }; struct S2 { double key2; // ... whatever else }; Cheers! --M Jun 29 '06 #15

 P: n/a Jellicle wrote: I think that the (only?) good feature of a macro is that it works on "compiling time". Macros are occasionally necessary. See this FAQ: http://www.parashift.com/c++-faq-lit....html#faq-6.16 so I can replace the codes ( not the data). It maybe save some similar codes. for example, #define FIELDNAME( field) field mystruct.FIELDNAME(myfield) ==> mystruct.myfield I don't see how this example buys you anything, and thus I don't think it is a good use of macros. Cheers! --M Jun 29 '06 #16

 P: n/a mlimber åéï¼ Jellicle wrote: mlimber åéï¼ Markus Schoder wrote: > Jellicle wrote: > > But I cannot compile the codes successfully in Visual C++ 6.0. > > You probably need a newer compiler. Try this (I didn't test it, but it compiles :-) ). I think it should work with VC++ 6, and it works with keys of different types and different names: #include #include using namespace std; template Iterator minfield( Iterator start, const Iterator &end, key_type T::*p) { key_type acc = (*start).*p; Iterator min_i(start); ++start; while(start != end) { const key_type &r = (*start).*p; if(r < acc) { acc = r; min_i = start; } ++start; } return min_i; } struct S1 { typedef int key_type; key_type key1; S1( key_type key ) : key1(key) {} }; struct S2 { typedef double key_type; key_type key2; S2( key_type key ) : key2(key) {} }; int main() { std::vector v1; v1.push_back( S1(1) ); v1.push_back( S1(5) ); v1.push_back( S1(-10) ); v1.push_back( S1(3) ); vector::const_iterator m1 = minfield( v1.begin(), v1.end(), &S1::key1 ); assert( m1->key1 == -10 ); std::vector v2; v2.push_back( S2(1.1) ); v2.push_back( S2(5.1) ); v2.push_back( S2(-1.1) ); v2.push_back( S2(3.1) ); vector::const_iterator m2 = minfield( v2.begin(), v2.end(), &S2::key2 ); assert( m2->key2 == -1.1 ); return 0; } Cheers! --M Thank you, mlimber. I am really grateful for you kind help! Yes, it works well in VC6 :) However, my question is that the structs were defined by other people, and were used somewhere else. So I cannot redefine them. There should be no need to alter the structs (I had initially added the key_type to them but realized I didn't need to). If the structs looked like this, it would work the same: struct S1 { int key1; // ... whatever else }; struct S2 { double key2; // ... whatever else }; Yes! It works! Thank you:) And do you think that at least I can save a little time/space if I use a macro as this :-) // for vector of struct #define MINFIELD(vs,f) (minfield(vs.begin(), vs.end(), &f) - vs.begin()) // for array of struct #define MINFIELD2(as,size,f) (minfield(as, as+size, &f) - as) I have test these as follows #define MINFIELD(vs,f) (minfield(vs.begin(), vs.end(), &f) - vs.begin()) #define MINFIELD2(as,size,f) (minfield(as, as+size, &f) - as) main() { score_t scores={100,93, 71, 70,59,83, 77,99,88}; int d2 = MINFIELD2(scores, 3, score_t::mathematics); int d3 = MINFIELD2(scores, 3, score_t::mathematics) * 6; int d4 = 8 * MINFIELD2(scores, 3, score_t::mathematics); std::vector gg; goods_t g; g.price = 122.1; g.amount=100; gg.push_back(g); g.price = 322.2; g.amount=120; gg.push_back(g); g.price = 1012.4; g.amount=12; gg.push_back(g); g.price = 102.3; g.amount=110; gg.push_back(g); int i5 = MINFIELD(gg, goods_t::price); int i6= 5*MINFIELD(gg, goods_t::price); int i7 = MINFIELD(gg, goods_t::price) * 6; return 0; } Cheers! --M Jun 30 '06 #17

 P: n/a Jellicle åéï¼ mlimber åéï¼ Jellicle wrote: mlimber åéï¼ > Markus Schoder wrote: > > Jellicle wrote: > > > But I cannot compile the codes successfully in Visual C++ 6.0. > > > > You probably need a newer compiler. > > Try this (I didn't test it, but it compiles :-) ). I think it should > work with VC++ 6, and it works with keys of different types and > different names: > > #include > #include > > using namespace std; > > template > Iterator minfield( > Iterator start, > const Iterator &end, > key_type T::*p) > { > key_type acc = (*start).*p; > Iterator min_i(start); > ++start; > while(start != end) > { > const key_type &r = (*start).*p; > if(r < acc) > { > acc = r; > min_i = start; > } > ++start; > } > return min_i; > } > > struct S1 > { > typedef int key_type; > key_type key1; > S1( key_type key ) : key1(key) {} > }; > > struct S2 > { > typedef double key_type; > key_type key2; > S2( key_type key ) : key2(key) {} > }; > > int main() > { > std::vector v1; > v1.push_back( S1(1) ); > v1.push_back( S1(5) ); > v1.push_back( S1(-10) ); > v1.push_back( S1(3) ); > > vector::const_iterator m1 = minfield( > v1.begin(), v1.end(), &S1::key1 ); > assert( m1->key1 == -10 ); > > std::vector v2; > v2.push_back( S2(1.1) ); > v2.push_back( S2(5.1) ); > v2.push_back( S2(-1.1) ); > v2.push_back( S2(3.1) ); > > vector::const_iterator m2 = minfield( > v2.begin(), v2.end(), &S2::key2 ); > assert( m2->key2 == -1.1 ); > > return 0; > } > > Cheers! --M Thank you, mlimber. I am really grateful for you kind help! Yes, it works well in VC6 :) However, my question is that the structs were defined by other people, and were used somewhere else. So I cannot redefine them. There should be no need to alter the structs (I had initially added the key_type to them but realized I didn't need to). If the structs looked like this, it would work the same: struct S1 { int key1; // ... whatever else }; struct S2 { double key2; // ... whatever else }; Yes! It works! Thank you:) And do you think that at least I can save a little time/space if I use a macro as this :-) // for vector of struct #define MINFIELD(vs,f) (minfield(vs.begin(), vs.end(), &f) - vs.begin()) // for array of struct #define MINFIELD2(as,size,f) (minfield(as, as+size, &f) - as) I have test these as follows #define MINFIELD(vs,f) (minfield(vs.begin(), vs.end(), &f) - vs.begin()) #define MINFIELD2(as,size,f) (minfield(as, as+size, &f) - as) main() { score_t scores={100,93, 71, 70,59,83, 77,99,88}; int d2 = MINFIELD2(scores, 3, score_t::mathematics); int d3 = MINFIELD2(scores, 3, score_t::mathematics) * 6; int d4 = 8 * MINFIELD2(scores, 3, score_t::mathematics); std::vector gg; goods_t g; g.price = 122.1; g.amount=100; gg.push_back(g); g.price = 322.2; g.amount=120; gg.push_back(g); g.price = 1012.4; g.amount=12; gg.push_back(g); g.price = 102.3; g.amount=110; gg.push_back(g); int i5 = MINFIELD(gg, goods_t::price); int i6= 5*MINFIELD(gg, goods_t::price); int i7 = MINFIELD(gg, goods_t::price) * 6; return 0; } struct score_t { int mathematics; int physics; int chemistry; }; struct goods_t { double price; int amount; }; Cheers! --M Jun 30 '06 #18

This discussion thread is closed

Replies have been disabled for this discussion. 