views:

802

answers:

2

I'm having difficulty in using std::for_each and other algorithms with a multimap, and want to know if someone could help me developing a functor that could pass the appropriate parameter to the "generic" functions.

My specific problem with map/multimap is that their iterators evaluate to a std::pair instead of the contained value (I mean, the mapped_type) which I need to work with. So, my question is,is there a way to pass the proper value to a function designed to work with one of the contained types of map/multimap?

Here is my sample code:

// the key type for the multimap
typedef enum { ... } Module;

// my class as the mapped type for the multimap
struct Patch
{
    void Apply(bool enable);
}

// I have some functors designed to work directly with Patch pointers
// because in other places I use set<Patch*> or other containers
struct ApplyOnCondtion: public unary_function<Patch*, void>
{
    void operator() (Patch* patch)
    {
     if(some_condition) patch->Apply(enable_or_not);
    }
}

// somewhere I have a collection of patches attributed to some module
multimap<Module, Patch*> patches;

// the problem is that using for_each with a map or a multimap results in
// a `pair<Module,Patch*>` as argument to the functor, not the Patch* as desired.
for_each(patches.begin(), patches.end(), ApplyOnCondition(...));

I think that maybe bind1st or bind2nd with conjunction with mem_fun could solve this problem, or the other way that I can think of is to create a new functor that stores the original functor and passes the correct member of the pair, but I'm not managing to get any good result. Can anyone with experience in STL give some advices?

EDIT 1

Ok, the best I can get, without using boost or an additional temporary container is the following:

#include <functional>
#include <utility>
using namespace std;


//////////////////////////////////////////////////////////////////////////
// any functor to be called must be derived from unary_function or
// have defined result_type and argument_type.
// template 'First' should be set to pair::first_type
template<typename First, typename Func>
class passSecond_t: public unary_function<
         pair<First,typename Func::argument_type>,
         typename Func::result_type>
{
    Func* func;

public:
    passSecond_t(Func &functor): func(&functor) {}

    result_type operator()(argument_type value)
    {
     return (*func)(value.second);
    }
};

// construction helper, unfortunately 'First' must be explicitly specified
template <typename First, typename Func>
passSecond_t<First, Func> passSecond(Func& functor)
{
    return passSecond_t<First, Func> (functor);
}


// the following is a sample
#include <map>
#include <algorithm>
#include <iostream>

struct SampleClass 
{
    void execute(char* text)
    {
     cout << "this: " << this << ", text: " << text << endl;
    }
};

struct SampleFunctor: public unary_function<SampleClass*,void>
{
    char* text;
    SampleFunctor(char* text_): text(text_) {}

    result_type operator() (argument_type argu)
    {
     argu->execute(text);
    }
};

void main()
{
    map<int,SampleClass*> mymap;
    SampleClass s1, s2;
    mymap[0] = &s1;
    mymap[1] = &s2;

    SampleFunctor myfunctor("my text");

    for_each(mymap.begin(), mymap.end(), passSecond<int>(myfunctor));
}
+1  A: 

I have a boost solution in the starts:

for_each(patches.begin(), patches.end(), 
         boost::bind(ApplyOnCondition(...),
                     boost::bind(&map_type::value_type::second, _1)));

This takes the ::second member of the pair, and pushes it forward to ApplyOnCondition's operator(). map_type is the type of the map (multimap<Module, Patch*> of course).

Johannes Schaub - litb
A: 

You can grab all the values using std::transform:

std::multimap<Module, Patch *> patches;

std::vector<Patch *> values(patches.size());
std::transform(patches.begin(), patches.end(), values.begin(),
    std::select2nd<std::multimap<Module, Patch *>::value_type>());

Then std::for_each on that vector:

std::for_each(values.begin(), values.end(), ApplyOnCondition(...));
strager