tags:

views:

377

answers:

5

Consider the following:

typedef struct {
    int a;
    int b;
    int c;
    int d;
} ABCD;

typedef std::vector<ABCD> VecABCD;

Say I wanted to add up every 'a' member in a vector of type VecABCD. Easy! I just loop through the vector, and sum as I go.

int CalcSumOfA(const VecABCD &vec)
{
    int sumOfA = 0;
    VecABCD::const_iterator it;
    for(it=vec.begin();it!=vec.end();it++)
        sumOfA += it->a;
    return sumOfA;
}

Say I wanted to do the same thing with 'b'? Easy! I'd write....essentially the same function, but with only trivial changes. Same with 'c' and 'd'.

So, is there a more concise, less repetitive way of doing this? I'd like to do something like:

int sumOfA = SumOfMembers(myVec, a);

but I can't conceive of how I'd put such a function together. Ideally, it'd be a template, and I could use it with a vector of any kind of structure, not specifically bound to VecABCD. Anyone have any ideas?

+1  A: 

You could use for_each. Its an option.

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
typedef struct{
    int a;

}ABCD;

typedef vector<ABCD> vecABCD;

struct sum  : public unary_function<ABCD, void>
{
  sum(){count.a=count.b=count.c=count.d=0;}
  void operator() (ABCD x) {
       count.a+=x.a;
       count.b+=x.b;
       count.c+=x.c;
       count.d+=x.d;
   }
  ABCD count;
};

int main()
{

  ABCD s1={1,2,3,4};
  ABCD s2={5,6,7,8};

  vecABCD v;
  v.push_back(s1);
  v.push_back(s2);
  sum s = for_each(v.begin(), v.end(), sum());
  cout<<s.count.a<<endl;

}

output:

4
Tom
...how is for_each going to give you a count?
Peter
@Peter. Added an example. Analog to the one in the for_each link provided.
Tom
How would you this to calculate the sum of all b's??
Naveen
@Naveen: I see your point. Edited my answer to sum all a,b,c,d. Still, clearly for_each is not the best alternative.
Tom
+1  A: 

Use std::accumulate :)

Billy ONeal
+10  A: 

STL summations can be done with accumulate

#include <functional>

accumulate(v.begin(), v.end(), 0, bind(plus<int>(), _1, bind(&ABCD::a, _2)))

If you wanted this to be more generic, you could take a tr1::function to the member you want to bind:

int sum_over_vec(const vector<ABCD>& v, const tr1::function<int (const ABCD&)>& member)
{
  return accumulate(v.begin(), v.end(),
                    0,
                    bind(plus<int>(),
                         _1,
                         bind(member, _2)));
};

// ...

int sum_a = sum_over_vec(vec, bind(&ABCD::a, _1));


Another way to do it, rather than putting your logic in the functor, would be to put the logic in the iterator, using a boost::transform iterator:

tr1::function<int (const ABCD&)> member(bind(&ABCD::a, _1));
accumulate(make_transform_iterator(v.begin(), member),
           make_transform_iterator(v.end(),   member),
           0);
Todd Gardner
+1 for accumulate.What about int overflow? Some error check needed.
Kirill V. Lyadvinsky
Int overflow (or underflow) would depend on the use case for this; I would probably employ the ostrich algorithm until I had reason to suspect otherwise.
Todd Gardner
Is bind() in STL or only in Boost?
Ari
Bind is in boost, but it is also in TR1, which is a new set of standard libraries (but isn't the STL). Many popular compilers are already supporting it: http://msdn.microsoft.com/en-us/library/bb982702.aspx
Todd Gardner
A: 

Let's add one more option, unfortunately an ugly one. It's possible to take the relative address from start of the struct ABCD to it's member by offsetof -function. Deliver return value to function and it can do counting using relative location from the start of each struct. If your types may differ from int, you may want to deliver also the sizeof information.

+6  A: 

Another option would be to use pointer-to-members:

int CalcSumOf(const VecABCD & vec, int ABCD::*member)
{
    int sum = 0;
    for(VecABCD::const_iterator it = vec.begin(), end = vec.end(); it != end; ++it)
        sum += (*it).*member;
    return sum;
}
...
int sumA = CalcSumOf(myVec, &ABCD::a);  // find sum of .a members
int sumB = CalcSumOf(myVec, &ABCD::b);  // find sum of .b members
// etc.
Adam Rosenfield
+1. To me, this looks like a better solution.
Igor Krivokon
+1. I didn't know that!
Nick D