Jump to content


(C++) Nested for_each?


10 replies to this topic

#1 eddie

    Senior Member

  • Members
  • PipPipPipPip
  • 751 posts

Posted 14 October 2006 - 03:22 AM

Hey. I'm trying to better acclimate myself to the std:: library, and I'm wondering if it's possible to 'nest' for_each calls.

i.e. I've got this:


typedef std::vector<Sector> Sectors;

typedef std::vector<Sectors> SectorsArray;


And I'd like to be able to do something like this:


for_each( m_SectorsArray.begin()

        , m_SectorsArray.end()

        , for_each( /* ? */

                  , /* ? */

                  , std::mem_fun_ref( &IRenderable::Render ) 

                  )

        );


In order:

1.- What's the proper way of doing this using for_each? Is it even possible?
2.- Am I crazy for wanting to do this? Is there a better way of doing it?

I loathe to use iterators, as theres's a *lot* of code that goes into setting up the for loop over the for_each, but I want to try them both and figure it out either way.

Any help would be appreciated. ;)

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4780 posts
  • LocationBellevue, WA

Posted 14 October 2006 - 03:50 AM

The trouble with for_each is you have to use a functor object. In particular, the object passed to for_each must be a UnaryFunction, which means it must have an operator() member that takes an argument whose type is equal to the type of the elements stored in the vector (technically, constructible from that type). So, you could do this by nesting the inner for_each within the functor:
class InnerFunctor {
public:
    void operator () (int value) {
        // do whatever
    }
};

class OuterFunctor {
public:
    void operator () (const std::vector<int> &value) {
        InnerFunctor i;
        for_each (value.begin(), value.end(), i);
    }
};

// Then use it like this
OuterFunctor o;
std::vector<std::vector<int> > myVec;
for_each(myVec.begin(), myVec.end(), o);
Or, you can use mem_fun_ref for the inner functor as you did in your code sample. But I don't think there's a way to do what you describe without writing an explicit outer functor, since C++ doesn't support defining local functions. On the other hand, you might be able to write some template functor to act as an adaptor for this situation, letting you use it for any kind of std::vector<std::vector<T> >.
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 14 October 2006 - 04:05 AM

Or just forget it and use a normal for-loop :)

#4 snk_kid

    New Member

  • Members
  • PipPip
  • 16 posts

Posted 14 October 2006 - 09:47 AM

Reedbeta said:

The trouble with for_each is you have to use a functor object. In particular, the object passed to for_each must be a UnaryFunction, which means it must have an operator() member ...

No it doesn't, it can be any C++ callable entity which includes functions.

@Eddie if you really want to do this then you can use the boost lambda library, look specificially here another alternative is boost phoenix V2 this isn't an offical boost library yet but this will highly likely be the replacement for (and merger of) boost.bind & boost.lambda in the future.

#5 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 14 October 2006 - 11:27 AM

I would use a regular for loop as well, boost lambda is an option but terribly inconvenient to set up in this way and very hard to read as well.

The problem is that for_each is a template function, and you need boost::bind to bind 3 parameters to the inner for_each function. bind() needs to deduce the signature of the function in the first parameter, but it can't deduce them based solely on the name 'for_each', so you'd have to cast that function to a functionpointer (void (*)(Sectors::iterator, Sectors::iterator, a_type_im_not_even_willing_to_type_out)). It's hard to figure out (maybe even impossible in a platform independent way), it's unreadable and it's unmaintainable. Definitely a no go. Stick to a basic for loop.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#6 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 14 October 2006 - 11:46 AM

With a different design of for_each, it would be very easy btw
template<class T> foreach_target_t
{
private:
    T m_target;

public:
    foreach_target_t(T target) : m_target(target) { }

    template<class CONT> void operator() (CONT & cont)
    {
        std::for_each(cont.begin(), cont.end(), m_target);
    }

    template<class CONT> void operator() (const CONT & cont)
    {
        std::for_each(cont.begin(), cont.end(), m_target);
    }

    template<class FwdIt> void operator() (FwdIt begin, FwdIt end)
    {
        std::for_each(begin, end, m_target);
    }
};

template<class T> foreach_target_t<T> foreach_target(T t)
{
    return foreach_target_t<T>(t);
}


void foo(SectorsArray & array)
{
    foreach_target(foreach_target(std::mem_fun_ref( &IRenderable::Render ))) (array);
}
The idea is that you create a functor object that takes a container or iterator pair as parameters, and then does a for_each on the container/iterators using the target function stored in the functor.

Not tested btw ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#7 snk_kid

    New Member

  • Members
  • PipPip
  • 16 posts

Posted 14 October 2006 - 02:18 PM

.oisyn said:

With a different design of for_each, it would be very easy btw
.....
The idea is that you create a functor object that takes a container or iterator pair as parameters, and then does a for_each on the container/iterators using the target function stored in the functor.

Not tested btw ;)

This is essentially how boost lambda & phoenix V2 works with the exception that BLL's for_each only take iterators (as far as I'm aware) and phoenix V2 has the added advantage that the framework is based on Currying, all of phoenix V2's 'functions' are curryable and when working with the framework of all your 'functions' can be curryable too.

#8 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 14 October 2006 - 02:41 PM

If you want to have a C#-like for_each in C++, check the work of Eric Niebler http://www.nwcpp.org...gs/2004/01.html .

#9 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 14 October 2006 - 07:29 PM

snk_kid said:

This is essentially how boost lambda & phoenix V2 works
No, with boost::lambda you can create anonymous functors using parameter placeholders (I don't know phoenix V2 btw). You can't do it easily with boost::lambda - it's very hard to create a unary function that takes a container as argument and calls std::for_each(cont.begin(), cont.end(), std::mem_fun_ref( &IRenderable::Render ))

You somehow need to do std::for_each(_1.begin(), _1.end(), ...), but that obviously isn't possible, so you'll need bind the three parameters. And that's where the trouble comes: boost::bind(std::for_each, (&_1) ->* (&Sectors::begin), (&_1) ->* (&Sectors::end), ...)

You'll get three ambiguity errors here - from std::for_each, Sectors::begin and Sectors::end. Because the compiler doesn't know which of the overloads to take to deduce the template paremeters on, you'd have to cast them explicitely. For begin and and perhaps doable, for_each however is another story.

As I said, not readable and not maintainable. Currying won't help you here either.

But if you think it's easy, I challange you to write a compilable version for the topicstarter using lambda (or phoenix V2) ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#10 snk_kid

    New Member

  • Members
  • PipPip
  • 16 posts

Posted 15 October 2006 - 01:03 PM

.oisyn said:

No, with boost::lambda you can create anonymous functors using parameter placeholders (I don't know phoenix V2 btw).......

*Sigh* Quite clearly you've totally misunderstood and underestimating me, now i suggest you follow my link i gave earlier before continuing further, I'll give it again here and I'll say it again I'm talking about BLL's for_each not std::for_each.

As you've said you have no idea about phoenix V2 (you can have alook here if you want), my point was that it takes the idea of generic functions as user-defined types with functional call overloads much, much further than you could ever imagine.

#11 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 15 October 2006 - 01:28 PM

Wow, I totally missed that last paragraph in the lamda docs *ashamed*. I stand corrected, my appoligies.

Btw, I just misunderstood you, I never underestimated you.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users