views:

441

answers:

4

Hi! I was wandering how it's possible to reverese strings that are contained in a vector using a single for_each command just in one "simple" line.

Yea, I know it is easy with a custom functor, but I can't accept, that it can't be done using bind (at least I couldn't do it).

#include <vector>
#include <string>
#include <algorithm>

std::vector<std::string> v; 
v.push_back("abc");
v.push_back("12345");

std::for_each(v.begin(), v.end(), /*call std::reverse for each element*/);

Edit: Thanks a lot for those funtastic solutions. However, the solution for me was not to use the tr1::bind that comes with the Visual Studio 2008 feature pack/SP1. I don't know why it does not work like expected but that's the way it is (even MS admits that it's buggy). Maybe some hotfixes will help.

With boost::bind everything works like desired and is so easy (but sometimes relly messy:)). I really should have tried boost::bind in the first place...

+5  A: 

You would have to roll your own reverse object:

struct Reverser
{
    void operator()(std::string& value) const
    {
        std::reverse(value.begin(),value.end());
    }
};

Now you can do it one line:

std::for_each(v.begin(), v.end(), Reverser());
Martin York
Like I said, it's not a problem using a function object...
fmuecke
+12  A: 

std::for_each expects a unary function (or at least something with the typedefs of a unary function).

std::reverse<> is a binary function. It takes two iterators. It would be possible to bind it all together using boost::bind, but it would be a pretty horrible mess. Something like:

boost::bind(
    &std::reverse<std::string::iterator>,
        boost::bind(&std::string::begin, _1), 
        boost::bind(&std::string::end, _1))

Better, I think, would be to write a reusable function called reverse_range like so:

template <class Range>
void reverse_range(Range& range)
{
    std::reverse(range.begin(), range.end());
}

(probably with some metaprogramming to ensure that Range& isn't a double-reference)

And then use that in your for_each (after adapting it to be a unary function, of course).

std::for_each(v.begin(), v.end(),
    std::ptr_fun(&reverse_range<std::string>));

EDIT:

Because string::begin and string::end have both const and non-const variants, it is necessary to cast them (as litb discovered while I was off writing them to test my answer ... +1!). This makes it very verbose. Typedefs can make it a bit more sanitary, but to stick with the one-liner theme:

boost::bind(
    &std::reverse<std::string::iterator>,
    boost::bind(
        (std::string::iterator (std::string::*)())&std::string::begin, _1),
    boost::bind(
        (std::string::iterator (std::string::*)())&std::string::end, _1)
    )
);

Which just screams for refactoring.

Finally, because I'm bored, bonus points for C++0x:

std::for_each(v.begin(), v.end() [](std::string& s){ std::reverse(s); });

EDIT: boost::bind works just fine, no need for boost::lambda.

Kaz Dragon
Nice! That's exactly what I wanted to know. Thank you!
fmuecke
Johannes Schaub - litb
Surely, i would prefer a manual loop :)
Johannes Schaub - litb
For these cases, i have `get_nonconst` / `get_const` functions in my toolbox: http://codepad.org/WI3w5xNh
Johannes Schaub - litb
+5  A: 

With Boost.Phoenix2:

std::for_each(v.begin(), v.end(), boost::phoenix::reverse(arg1));

To paraphrase mr-edd: measurably awesomer :)

Full example:

#include <boost/spirit/home/phoenix.hpp>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>

int main(void)
{

    using namespace boost::phoenix::arg_names; // for "arg1"

    std::vector<std::string> v;
    v.push_back("hello");
    v.push_back("world");
    std::for_each(v.begin(), v.end(), boost::phoenix::reverse(arg1));

    std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}

prints:

olleh
dlrow
Éric Malenfant
+1 It also reminds me of my own personal wrapper for all STL algorithms, so they are callable on full containers: range::for_each(v, range::reverse<std::string>); - Simple :)
UncleBens
+1 Nice. Although I adore Boost.Spirit (just the sheer cleverness of the entire thing gives me the shivers, never mind that it compiles -- eventually -- down to something so tiny and fast), I haven't given Phoenix much time. Perhaps I should go sharpen that tool.
Kaz Dragon
Ahh, phoenix! I love it.
fmuecke
+3  A: 

Hi, it could be also done with the BOOST_FOREACH macro:

BOOST_FOREACH( std::string& s, v )
    std::reverse( s.begin(), s.end() );
Martin
+1 That's also very nice and compact!
fmuecke