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)

user1192525
  • 657
  • 4
  • 20
  • Summarizing, my problem here is that `std::for_each` is going to pass me a `std::pair` whereas `Filter::filter` expects a reference to `Data`. – user1192525 Feb 26 '12 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 ? – Arunmu Feb 26 '12 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. – user1192525 Feb 26 '12 at 13:40
  • Still not clear :(. How does your map look like map,?>. – Arunmu Feb 26 '12 at 13:48
  • The map looks like: `std::map` 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) (Filter&, std::pair&)`. Now `for_each`is passing a `std::pair` since I am iterating over a map rather than a vector. – user1192525 Feb 26 '12 at 14:09
  • http://stackoverflow.com/questions/2311752/boost-bind-to-access-stdmap-elements-in-stdfor-each . Some thing very close to what you want – Arunmu Feb 26 '12 at 14:14
  • Please make your title describe the question rather than just listing technologies. It's useless as it stands. – Lightness Races in Orbit Feb 26 '12 at 14:20
  • Hi ArunMu,thanks for the link, it solved my question. – user1192525 Feb 26 '12 at 14:30

2 Answers2

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))); 
}
Igor R.
  • 14,716
  • 2
  • 49
  • 83
  • Hello. Is it possible to get double d_; from &Samples::value_type::second ? – Max Oct 18 '18 at 13:19
  • I mean run not through the second but through the fields of the second – Max Oct 18 '18 at 13:23
  • 1
    @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.) – Igor R. Oct 18 '18 at 16:55
  • I made solution with lambda) I'll read about fusion and open new question. Thank you – Max Oct 18 '18 at 18:37
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.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 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) (Filter&, std::pair&)’` ... `error: creating array with negative size (‘-0x00000000000000001’)` – user1192525 Feb 26 '12 at 13:59
  • 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. – Dietmar Kühl Feb 26 '12 at 17:11