2

I have made a small example in order to understand how boost::bind () works with collections. I have a class called Data:

class Data
{
public:
    void print ();
    const std::string& get () const;
    std::string& get () ;
};

I have created a std::vector of Data objects called samples and I am able to use bind in the same way as std::mem_fun_ref works.

std::for_each (samples.begin (),samples.end (),std::mem_fun_ref (&Data::print));
std::for_each (samples.begin (),samples.end (),boost::bind (&Data::print,_1));

The basic idea is that the bind returns a function object of type bind_t<RetType=void, ObjType=Data, ArgType=void>. The member function as the first parameter allows the compiler to deduce RetType, ObjType and ArgType. The placeholder _1 corresponds to the data object which must be provided by the algorithm.

Then std::for_each calls the function object "for each" element in the following way:

for ( ; first!=last; ++first ) f(*first);

bind_t::operator(ObjType& obj) is invoked and its definition should be something like this:

return (obj.*_method ());

I have crated a class called Filter that performs some processing over a data element.

class Filter
{
    void filter (Data& data);
    ...
};

If I want to apply the filter over the data elements in the vector I call bind in the following way:

std::for_each (samples.begin (),samples.end (),boost::bind (&Filter::filter,filter,_1));

for_each passes a Data object to bind_t::operator(). In this case the function object already has the object and just need the parameter so in this case placeholder _1 refers to the argument.

Here comes my question:

How can use bind if I have to iterate over a std::map rather than a vector?

(Sorry for all the explanation, I just want to make sure that I understand the way in which bind works)

8
  • Summarizing, my problem here is that std::for_each is going to pass me a std::pair<key,Data> whereas Filter::filter expects a reference to Data. Commented Feb 26, 2012 at 13:35
  • why didnt you come to the question directly ? :) And why do you think it will be any different than iterating over a vector ? Commented Feb 26, 2012 at 13:35
  • Hi ArunMu, sorry for the whole explanation. I just wanted to make sure that I understand how bind works (maybe I misunderstood something). Related to your second question, it will be different as now the function object receives a std::pair rather than a reference to the next vector element. Commented Feb 26, 2012 at 13:40
  • Still not clear :(. How does your map look like map<?,?>. Commented Feb 26, 2012 at 13:48
  • The map looks like: std::map<int,Data> and I want to apply the filter over all the data elements. That is, if I call: std::for_each (sampleMap.begin (),sampleMap.end (),boost::bind (&Filter::filter,filter,_1)) I get the following error: error: no match for call to (boost::_mfi::mf1<void, Filter, Data&>) (Filter&, std::pair<const int, Data>&). Now for_eachis passing a std::pair since I am iterating over a map rather than a vector. Commented Feb 26, 2012 at 14:09

2 Answers 2

2
#include <boost/bind.hpp>
#include <algorithm>
int main()
{
  struct Sample
  {
    int i_;
    double d_;
  };
  typedef std::map<int, Sample> Samples;
  struct Filter
  {
    void filter(const Sample &s)
    {
    }
  };
  Filter filter;
  Samples samples;
  std::for_each(samples.begin(), samples.end(), boost::bind(&Filter::filter, filter, boost::bind(&Samples::value_type::second, _1))); 
}
Sign up to request clarification or add additional context in comments.

4 Comments

Hello. Is it possible to get double d_; from &Samples::value_type::second ?
I mean run not through the second but through the fields of the second
@Max Do you want to iterate through the struct fields? Well, that's unrelated to the original question, but as long as we do not have reflection in c++, you need to use boost::fusion struct adaptor for that. Adapting a struct makes it an iterable fusion sequence. (You'd better open another question.)
I made solution with lambda) I'll read about fusion and open new question. Thank you
0

Of course, you can use bind() to iterate over a std::map<...>. However, note that the elements in the std::map<K, V> have the type std::pair<K const, V> i.e. the bound function needs to be cable of accessing objects of this type. That said, I'm not aware that bind() alone can be used to transform this argument into the argument you are actually interested (i.e. to a V). To do this, you probably need an auxiliary function which is called to do the transformation. If you bind() this function as well bind() can do a suitable composition, i.e. I think something like this should work:

template <typename Result>
struct project2nd {
    typedef Result const& result_type;
    template <typename T>
    result_type operator()(std::pair<T, Result> const& arg) const {
        return arg.second;
    }
};
...
... bind(&Filter::filter, filter, bind(project2nd<Data>(), _1)) ...

For bind() to work with a funciton object it seems it needs some information about related types: the result of the function call operator can't be deduced but is apparently needed internally. I assume that bind() is clever enough to pass references through. Otherwise the type needs to be changed to be Result. Also, I don't know if bind() also needs to know about the argumen type. If so, the project2nd class tmeplate would need to take both types as argument.

2 Comments

Hi Dietmar, first of all thanks for your response. I have tried your solution an I got some compilation errors: /home/anibal/local/include/boost/bind/bind.hpp:69:37: error: no type named ‘result_type’ in ‘struct project2nd’ ... error: no match for call to ‘(boost::_mfi::mf1<void, Filter, Data&>) (Filter&, std::pair<const int, Data>&)’ ... error: creating array with negative size (‘-0x00000000000000001’)
Er, yes... I didn't have the possibility to test the answer (I still don't have it, for that matter) so I forgot about some of the needs of using bind() with function objects. This particular one is a bit annoying as it requires that result_type is defined in the function object. This, in turn, means that it can't be deduced from the argument of the function call operator. I'll update the answer with a possible approach to do it. Off-hand, I don't know if there are additional requirements.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.