8

I've got a map that stores a simple struct with a key. The struct has two member functions, one is const the other not. I've managed calling the const function using std::for_each without any problems, but I've got some problems calling the non-const function.

struct MyStruct {
  void someConstFunction() const;
  void someFunction();
};

typedef std::map<int, MyStruct> MyMap;
MyMap theMap;

//call the const member function
std::for_each(theMap.begin(), theMap.end(),
   boost::bind(&MyStruct::someConstFunction, boost::bind(&MyMap::value_type::second, _1)));

//call the non-const member function
std::for_each(theMap.begin(), theMap.end(),
   boost::bind(&MyStruct::someFunction, boost::bind(&MyMap::value_type::second, _1)));

The call to the const member function works fine, but it seems boost internally expects a const MyStruct somewhere, and thus fails with the following compilation error in MSVC7.1.

boost\bind\mem_fn_template.hpp(151): error C2440: 'argument' : cannot convert from 'const MyStruct *__w64 ' to 'MyStruct *const '

I'd appreciate any help on how to set the template parameters correctly, so bind does recognize the parameters correctly and let me call the non const function.

thanks, Carl

Carl
  • 83
  • 1
  • 3
  • How about if you back up and tell us what you're really trying to accomplish here? Using for_each with a map with boost::bind *might* be reasonable, but chances are pretty good that a different general approach will work better (many times this kind of question arises, it's because `std::for_each` is a poor choice for the situation, and something like `std::copy` or std::accumulate` would do the job much more simply). – Jerry Coffin Feb 22 '10 at 15:24
  • The MyStruct is used in a sort of particle system, where MyStruct is the particle. The const function is a draw() function, the non-const function computes the new position. The key in the map is the creation date. Anyway, at the point I posted the question it was more about how to make that work than if this was a good design in the beginning. – Carl Feb 22 '10 at 15:36

4 Answers4

8

IIRC, Boost.Bind uses boost::mem_fn for its binding to members capability. Now, if you look at mem_fun (scroll down to the // data member support part), you'll see that it typedefs its result_type as a const&, while is still has overloads of the function call operator supporting the extraction of a non-const member from a non-const argument.

It thus seems that the problem is that this confuses Boost.Bind's return type deduction mechanism. A solution would thus to explicitly tell Bind that the result is not const:

//call the non-const member function
std::for_each(theMap.begin(), theMap.end(),
   boost::bind(&MyStruct::someFunction, 
       boost::bind<MyStruct&>(&MyMap::value_type::second, _1)
   )
);
Éric Malenfant
  • 13,938
  • 1
  • 40
  • 42
  • +1 Nice detective work. :-) Strangely, using `boost::lamba::bind` will compile without explicitly specifying the return type. Perhaps `boost::lamda::bind` is smarter than `boost::bind` in deducing return types? – Emile Cormier Feb 22 '10 at 16:45
  • Wow, thank you very much. That compiles just fine. Though I love to use Boost it's still quite hard for me to read most of their code, so I failed. Thanks for your help. – Carl Feb 22 '10 at 16:56
  • I think you mean `boost::mem_fn` – Manuel Feb 23 '10 at 09:26
7

If you find yourself having to do this a lot I recommend you use the Boost.RangeEx library:

#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/mem_fn.hpp>
#include <map>

struct MyStruct {
  void someConstFunction() const;
  void someFunction();
};

typedef std::map<int, MyStruct> MyMap;
MyMap theMap;

int main()
{
    //call the const member function
    boost::for_each(theMap | boost::adaptors::map_values,
                    boost::mem_fn(&MyStruct::someConstFunction));

    //call the non-const member function
    boost::for_each(theMap | boost::adaptors::map_values,
                    boost::mem_fn(&MyStruct::someFunction));
}

It's been accepted into Boost but it doesn't come with the official distribution yet. Until it does you can download it from the Boost Vault (download link to zip file).

Manuel
  • 12,749
  • 1
  • 27
  • 35
  • That looks a lot more readable and understandable than the bind solution. It's definitely worth a closer look. Thanks for the tip. – Carl Feb 22 '10 at 15:50
  • @Carl - also notice that boost::mem_fn is easier to use than boost::bind in this case – Manuel Feb 22 '10 at 15:52
  • This answer is orthogonal to the question, it replaces the `std::for_each` loop by `boost::for_each`, but does not tell how to use `boost::bind` as argument to either. Even so, it does provide a workaround. – David Rodríguez - dribeas Feb 23 '10 at 08:49
  • @David - I think the workaround is the best solution here. People trying to do overly complex things with `bind` remind me of that saying about regular expressions: "some people, when confronted with a problem..." – Manuel Feb 23 '10 at 09:28
  • Yes, but the workaround is using `mem_fn`, not the `for_each` change. That is, using `boost::mem_fn` instead of `boost::bind` provides a workaround, but that is completely orthogonal to the change from `std::for_each` to `boost::for_each`. – David Rodríguez - dribeas Feb 23 '10 at 09:48
  • @David - did you miss the `boost::adaptors::map_values` part? I think `boost::mem_fn` alone does not help in the case of nested binds – Manuel Feb 23 '10 at 09:54
4

If you are already depend on Boost, you may be willing to check Boost Foreach

BOOST_FOREACH(MyMap::value_type const& val, MyMap)
{
  val.second.someConstFunction();
}

Much much readable, though I don't know about performance issues.

Also note that you can't use templated typed within the macro without "escaping" the , character:

  • either by a typedef before
  • or by using a second pair of parenthesis around the type
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • I know Boost Foreach and that works of course. But I'm just curious to find the correct syntax to the above solution, since the code above works fine for the const function and fails for the non-const one. – Carl Feb 22 '10 at 15:41
  • 4
    I believe the correct code would be (remove the const if you want to change the values): BOOST_FOREACH(MyMap::value_type const& val, theMap) { ... } – Bklyn Feb 22 '10 at 17:37
  • and you don't have to link to boost when using Boost.Bind or Boost.Foreach – Mathias Soeken Feb 23 '10 at 00:28
  • @Rupert: corrected the "linked" by "depend" since it's effectively more clear that you merely have a dependence since those are header only libraries. – Matthieu M. Feb 23 '10 at 16:02
0

One problem I spotted: the second bind is called for a non function member. second is a data member, not a method of std::pair

Cătălin Pitiș
  • 14,123
  • 2
  • 39
  • 62
  • 3
    I found this technique in this article: http://www.informit.com/articles/article.aspx?p=412354&seqNum=4 It states "You can bind to a member variable just as you can with a member function, or a free function.". Since the for_each code is essentially the same for both member functions and the problem is only encountered in the call to the non-const member function, I guess the article's correct. – Carl Feb 22 '10 at 15:29