views:

2771

answers:

7

I've found myself writing

for(int i=0;i<myvec.size();i++)
   myvec[i]->DoWhatever(param);

a lot, and I'd like to compress this into a foreach statement, but I'm not sure how to get param in there without going super-verbose. I've also got things like

for(int i=0;i<myvec.size();i++)
   if(myvec[i]->IsOK())
      myvec[i]->DoWhatever(param);

and I'd like to rewrite that guy too. Any thoughts?

Oh, also, for various reasons, I don't want to use boost.

+3  A: 

Not exactly an answer using std::foreach but maybe you could use iterators directly:

for(std::vector<...>::iterator it=myvec.begin();it!=myvec.end();it++)
   if(it->IsOK())
      it->DoWhatever(param);
rstevens
+8  A: 
#include <vector>
#include <algorithm>
#include <functional>

class X
{
    public:
        void doWhat(int x) {}
        bool IsOK() const {return true;}
};
class CallWhatIfOk
{
    public:
        CallWhatIfOk(int p): param(p) {}

        void operator()(X& x) const
        {   if (x.IsOK())    {x.doWhat(param);}}
    private:
        int param;
};

int main()
{
    std::vector<X>      myVec;

    std::for_each(  myVec.begin(),
                    myVec.end(),
                    std::bind2nd(std::mem_fun_ref(&X::doWhat),4)
                 );


    std::for_each(  myVec.begin(),
                    myVec.end(),
                    CallWhatIfOk(4)
                 );
}
Martin York
Thanks! Any ideas on the second, though?
Jesse Beder
+7  A: 

Oh, also, for various reasons, I don't want to use boost.

Valid decision, but most likely the wrong one. Consider Boost as an extension to the STL. C++ is a library-driven language. If you don't take this into account, your code will be qualitatively inferior.

While std::for_each can be used here, the absence of lambda expressions in C++ until C++0x makes this tedious. I advocate using Boost.ForEach! It makes this much easier:

foreach (yourtype x, yourvec)
    if (x.IsOK())
        x.Whatever();
Konrad Rudolph
Alas, it's not my decision whether to use boost.
Jesse Beder
Might want to say that you *can't* use boost then. Saying you don't *want to* is just asking for an argument... ;)
jalf
+3  A: 

My preferred solution is usually to write a functor to do what I need:

struct doWhatever {
  doWhatever(const Param& p) p(p) {}
  void operator(MyVec v&, Param p) {
    v.DoWhatever(param);
  }

private:
  Param p;
};

And then the loop:

std::for_each(myvec.begin(), myvec.end(), doWhatever(param));

Depending on how many variations of this you have, this might be a bit too verbose. There are plenty of options for doing it inline though. boost::lambda would let you construct the function you need at the call-site. boost::bind (or the standard library bind functions) would let you bind the parameter param to the function so you don't need to supply it as an argument every time.

boost::lambda is probably the most concise and flexible approach. I usually use the plain functor approach because the syntax is easier to remember. ;)

jalf
This is what I had (and was hoping to avoid) :)
Jesse Beder
hm, without boost (ouch), I don't think you can do it much shorter. In some of the cases, the std::bind_* stuff might help you, but there aren't any miraculous silver bullets. (At least until c++0x with the addition of lambda expressions)
jalf
+3  A: 

well when we have compilers that support C++0x lambda expresions, this becomes straightforward and minimally invasive:

std::for_each(myvec.begin(),myvec.end(),[&](X& item){
     item->DoWhatever(param);
});

and the second example may look like this:

std::for_each(myvec.begin(),myvec.end(),[&](X& item){   
   if(item->IsOK())      
      myvec[i]->DoWhatever(param);
});
Rick
That is so pity, it does not work on g++ :(. I hope in some point I will be able to use those constructions...
Arman
I hope so too :)
Rick
+1  A: 
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/bind.hpp>


struct A
{
  bool IsOK () { return true; }
  void DoWhatever (int param) {}
};

struct B
{
  bool IsOk (A * a) { return true; }
  void DoWhatever (A * a, int param) {}
};

typedef std::vector<A *> Myvec;

void main()
{
  Myvec myvec;
  int param = 1;
  B b;

  // first challenge using boost::bind (fnct in the same class)
  std::for_each (myvec.begin(), myvec.end(),
    boost::bind (&A::DoWhatever, _1, param));

  // first challenge using boost::bind (fnct in an external class)
  std::for_each (myvec.begin(), myvec.end(),
    boost::bind (&B::DoWhatever, &b, _1, param));

  // second challange using boost::lambda (fnct in the same class)
  std::for_each (myvec.begin(), myvec.end(),
    boost::lambda::if_then(
      boost::lambda::bind (&A::IsOK, boost::lambda::_1), 
      boost::lambda::bind (&A::DoWhatever, boost::lambda::_1, param)
    )
  );

  // second challange using boost::lambda (fnct in an external class)
  std::for_each (myvec.begin(), myvec.end(),
    boost::lambda::if_then(
      boost::lambda::bind (&B::IsOK, &b, boost::lambda::_1), 
      boost::lambda::bind (&B::DoWhatever, &b, boost::lambda::_1, param)
    )
  );

}

You can simplify it by using namespaces...

psaghelyi
A: 

If you are using GCC you can define something like:

foreach(element, array) for(typeof((array).Start()) element = (array).Start(), _end##element = (array).End(); element != _end##element; ++element)

and use it after like this:

foreach(element, array)

{

element->DoSomething();//or (*element)->DoSomething() if type is already a pointer

}

I use this on a custom array but it works fine with std::vector too.

Felics