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

J

Jellicle

There are some sturct arrays (or struct vectors), such as:

struct S1_t
{
double keyField ;

..// other fields
}

S1_t s1[100];
std::vector<S1_t> 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[0];
for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++)
....
{
if (s1.keyField < minvalue )
{
minvalue =s1.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[100]




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!
 
S

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
 
V

Victor Bazarov

Jellicle said:
There are some sturct arrays (or struct vectors), such as:

struct S1_t
{
double keyField ;

..// other fields
}

S1_t s1[100];
std::vector<S1_t> 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[0];

You mean

double minvalue = s1[0].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.keyField < minvalue )
{
minvalue =s1.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[100]




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
 
J

Jellicle

Victor said:
Jellicle wrote:
Some codes could be:
//----------------------------------------------

double minvalue = s1[0];

You mean

double minvalue = s1[0].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[0] 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.
 
M

mlimber

Shimin said:
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<class T>
int MinKey( const vector<T>& v )
{
if( v.size() == 0 ) return -1;
int min = GetKey( v[0] );
for( vector<T>::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
 
J

Jellicle

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.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.
 
M

Markus Schoder

Jellicle said:
There are some sturct arrays (or struct vectors), such as:

struct S1_t
{
double keyField ;

..// other fields
}

S1_t s1[100];
std::vector<S1_t> 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[0];
for (int i=0; i< 100; i++ ) // for (int i=0; i< vecS1.size(); i++)
...
{
if (s1.keyField < minvalue )
{
minvalue =s1.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 <iterator>

template<class Iterator, typename T> Iterator minfield(Iterator start,
const Iterator &end, T std::iterator_traits<Iterator>::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).
 
M

mlimber

Jellicle said:
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.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-lite/inline-functions.html#faq-9.5

Cheers! --M
 
J

Jellicle

Markus Schoder wrote:

A STL style solution using iterators would be:

#include <iterator>

template<class Iterator, typename T> Iterator minfield(Iterator start,
const Iterator &end, T std::iterator_traits<Iterator>::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<Iterator>::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!
 
M

mlimber

Markus said:
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 <vector>
#include <cassert>

using namespace std;

template<class Iterator, typename key_type, typename T>
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<S1> v1;
v1.push_back( S1(1) );
v1.push_back( S1(5) );
v1.push_back( S1(-10) );
v1.push_back( S1(3) );

vector<S1>::const_iterator m1 = minfield(
v1.begin(), v1.end(), &S1::key1 );
assert( m1->key1 == -10 );

std::vector<S2> 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<S2>::const_iterator m2 = minfield(
v2.begin(), v2.end(), &S2::key2 );
assert( m2->key2 == -1.1 );

return 0;
}

Cheers! --M
 
J

Jellicle

mlimber 写é“:
Jellicle said:
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.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-lite/inline-functions.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
 
J

Jellicle

mlimber 写é“:
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 <vector>
#include <cassert>

using namespace std;

template<class Iterator, typename key_type, typename T>
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<S1> v1;
v1.push_back( S1(1) );
v1.push_back( S1(5) );
v1.push_back( S1(-10) );
v1.push_back( S1(3) );

vector<S1>::const_iterator m1 = minfield(
v1.begin(), v1.end(), &S1::key1 );
assert( m1->key1 == -10 );

std::vector<S2> 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<S2>::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.
 
M

mlimber

Jellicle said:
mlimber 写é“:



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
 
M

mlimber

Jellicle said:
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-lite/big-picture.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
 
J

Jellicle

mlimber 写é“:
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[3]={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<goods_t> 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;
}
 
J

Jellicle

Jellicle 写é“:
mlimber 写é“:
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[3]={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<goods_t> 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;

};
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
VetaMcRae
Top