
John Torjo wrote:
Hi all,
I think Peter Dimov has already thought about this ;) Read the current mem_fn docs carefully and you'll find a data-case also. I agree it's very useful functionality. It was added fairly "silently" though (mid 2002 IIRC) which might have made people miss it(?). // Fredrik Blomqvist

Fredrik wrote:
Indeed! Quite cool! What I miss is the fact that I want mem_fn to treat the object non-const, and have one more function (lets call it const_mem_fn) which would treat the object as non-const. At this time, mem_fn treats the object as const: // mem_fn.hpp, line 256 typedef R const & result_type; typedef T const * argument_type; If we were to have what I said above, we could simply modify any sequence inplace. Combining it with bind, and the possibilities are limit-less. Imagine this: struct test { int i; std::string s; }; std::vector<test> tests; // ... fill void set(int & i, int val) { i = val }; int val = 10; // for each obj from vector, set obj.i = val; std::for_each( tests.begin(), tests.end(), bind(&decrement, bind(mem_fn(&test::i),_1), val); Changes to mem_fn are fairly easy. I could do them, if Peter is ok with it. (basically, I still think it would be simpler to have one more mem_data class to clearly diffenenciate from mem_fn, which always suggests a member function) Best, John

John Torjo wrote:
Out of curiosity, do you really need this functionality? The current implementation fails with the above example because of its "portable" nature (i.e. support for nonconforming compilers), but quality implementations of the TR1 proposal (Hi Howard!) are actually supposed to handle this code both as written (typos aside), and with the shorthand bind(&test::i, _1) applied. (The & before 'decrement' is also redundant.) You can persuade the current implementation to allow you to modify a member with bind<int&>(mem_fn(&test::i), _1).

Peter Dimov wrote:
Yup, that's how I came up with it ;) The truth is that not very often, but yes, I do need it at times.
The current implementation fails with the above example because of its "portable" nature (i.e. support for nonconforming compilers), but quality
That's why I proposed a totally different type (mem_data/const_mem_data).
implementations of the TR1 proposal (Hi Howard!) are actually supposed to handle this code both as written (typos aside), and with the shorthand
sorry 'bout the typos ;) #include <string> #include <vector> #include <algorithm> #include <boost/bind.hpp> using namespace boost; struct test { int i; std::string s; }; std::vector<test> tests; void set(int & i, int val) { i = val; } int main(int argc, char* argv[]) { std::for_each( tests.begin(), tests.end(), bind(&set, bind(mem_fn(&test::i),_1), 10) ); return 0; } on vc7.1: d:\boost\boost_latest\boost\boost\bind.hpp(273) : error C2664: 'void (int &,int)' : cannot convert parameter 1 from 'const int' to 'int &' Conversion loses qualifiers d:\boost\boost_latest\boost\boost\bind\bind_template.hpp(33) : see reference to function template instantiation 'R boost::_bi::list2<A1,A2>::operator ()<boost::_bi::bind_t<R,F,L>::result_type,void(__cdecl *)(int &,int),boost::_bi::list1<test &>>(boost::_bi::type<T>,F & ,A &)' being compiled with [ R=boost::_bi::bind_t<void,void (__cdecl *)(int &,int),boost::_bi::list2<boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1,boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2>>::result_type, A1=boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1, A2=boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2, F=void (__cdecl *)(int &,int), L=boost::_bi::list2<boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1,boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2>, T=boost::_bi::bind_t<void,void (__cdecl *)(int &,int),boost::_bi::list2<boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1,boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2>>::result_type, A=boost::_bi::list1<std::allocator<test>::value_type &> ] c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\algorithm(21) : see reference to function template instantiation 'boost::_bi::bind_t<R,F,L>::result_type boost::_bi::bind_t<R,F,L>::operator ()<std::allocator<_Ty>::value_type>(A1 &)' being compiled with [ R=void, F=void (__cdecl *)(int &,int), L=boost::_bi::list2<boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1,boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2>, _Ty=test, A1=std::allocator<test>::value_type ] d:\john\buff\testbind\testbind\testbind.cpp(20) : see reference to function template instantiation '_Fn1 std::for_each<std::vector<_Ty>::iterator,boost::_bi::bind_t<R,F,L>>(_InIt,_InIt,_Fn1)' being compiled with [ _Fn1=boost::_bi::bind_t<void,void (__cdecl *)(int &,int),boost::_bi::list2<boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1,boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2>>, _Ty=test, R=void, F=void (__cdecl *)(int &,int), L=boost::_bi::list2<boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B1,boost::_bi::list_av_2<boost::_bi::bind_t<boost::_bi::unspecified,boost::_mfi::dm<int,test>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>>,int>::B2>, _InIt=std::vector<test>::iterator ] I'm not sure how this could be handled correctly (in any implementation).
Good point! Up until now never thought of using bind like this ;) // compiles ok std::for_each( tests.begin(), tests.end(), bind(&set, bind<int&>(mem_fn(&test::i),_1), 10) ); Best, John

On Feb 26, 2004, at 2:34 PM, Peter Dimov wrote:
Hi! :-) On Mar 1, 2004, at 1:00 AM, John Torjo wrote:
Sure, it can be done. On a Metrowerks version which has not been announced (much less released): #include <string> #include <vector> #include <algorithm> #include <bind> using namespace std::tr1; using namespace std::tr1::placeholders; struct test { int i; std::string s; }; std::vector<test> tests; void set(int & i, int val) { i = val; } int main(int argc, char* argv[]) { std::for_each( tests.begin(), tests.end(), bind(&set, bind(mem_fn(&test::i),_1), 10) ); return 0; } // compiles and runs fine -Howard

Sorry, I mis-explained. For boost::bind, the above works like a bliss. It will have problems working with other code, that uses result_type. For example, boost::transform_iterator: #include <boost/bind.hpp> #include <boost/iterator/transform_iterator.hpp> using namespace boost; #include <algorithm> #include <vector> #include <string> struct test { std::string s; }; void clear_s( std::string & s) { s.erase(); } int main(int argc, char* argv[]) { std::vector<test> v; std::for_each( // of course, it would be simpler to use Range Template Library :D make_transform_iterator(v.begin(),mem_fn(&test::s)), make_transform_iterator(v.end(),mem_fn(&test::s)), clear_s); return 0; } c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\algorithm(21) : error C2664: 'void (std::string &)' : cannot convert parameter 1 from 'const std::string' to 'std::string &' Conversion loses qualifiers d:\john\buff\testbind2\testbind2\testbind2.cpp(23) : see reference to function template instantiation '_Fn1 std::for_each<boost::transform_iterator<UnaryFunction,Iterator,Reference,Value>,void(__cdecl *)(std::string &)>(_InIt,_InIt,_Fn1)' being compiled with [ _Fn1=void (__cdecl *)(std::string &), UnaryFunction=boost::_mfi::dm<std::string,test>, Iterator=std::vector<test>::iterator, Reference=boost::use_default, Value=boost::use_default, _InIt=boost::transform_iterator<boost::_mfi::dm<std::string,test>,std::vector<test>::iterator,boost::use_default,boost::use_default> ] That's why I said we should have two classes - one that defines: typedef R const & result_type; and one that defines: typedef R & result_type; So you can clearly choose if you treat your sequence as const or not. Best, John

John Torjo wrote:
Your examples are getting more and more contrived. ;-) To override the result_type, you'll need once again to revert to bind<std::string&>(mem_fn(&test::s), _1) instead of mem_fn(&test::s). If you do a "cvs update" you'll also be able to shorten it to bind<std::string&>(&test::s, _1)

Peter Dimov wrote:
sorry 'bout that. I've written the above in a hurry :( What I want to say is that sometimes mem_fn might be aggregated in another object (other than bind), that might use the 'result_type' typedef internally. Such an example is transform_iterator. I'm sure you're familiar with it. Based on result_type, it computes the iterator's value_type. Regarding the example above, I would like to be able to have a const sequence and also a non-const one. Which now should be realizable like this: // begin of sequence, const boost::make_transform_iterator(tests.begin(), mem_fn(&test::s) ); // begin of sequence, non-const boost::make_transform_iterator(tests.begin(), bind<std::string&>(&test::s,_1) ); This is acceptable. I just wish (when needing non-const sequence) I didn't have to write bind<std::string&>, since this requires me to know test::s's type. What if it changes in the future, etc.? I know that a solution that would not require me to write bind<std::string&> would be rather easy to implement and think it would be very useful. What do you think? Now that I think of it, I would even like something like: bind<non_const>(&test::s,_1); Best, John
participants (4)
-
Fredrik
-
Howard Hinnant
-
John Torjo
-
Peter Dimov