
Hello, i just started using the boost::multi_index_container and ran into some trouble. I'm using boost version 1.32. Well, i tried a slightly modified version of Example 6 ("complex searches and foreign keys"). This is the code: -------------------------------------------------------------------------- #define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING #define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE #include <boost/multi_index_container.hpp> #include <boost/multi_index/member.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/mem_fun.hpp> #include <string> using namespace std; using namespace boost; using namespace boost::multi_index; template<class KeyExtractor1,class KeyExtractor2> struct key_from_key { public: typedef typename KeyExtractor1::result_type result_type; key_from_key( const KeyExtractor1& key1_=KeyExtractor1(), const KeyExtractor2& key2_=KeyExtractor2()): key1(key1_),key2(key2_) {} template<typename Arg> result_type operator()(Arg& arg)const { return key1(key2(arg)); } private: KeyExtractor1 key1; KeyExtractor2 key2; }; struct person { string get_name() const { return "GM"; } // XXX }; struct car_manufacturer : public person // XXX { }; struct car_model { string model; car_manufacturer* manufacturer; int price; }; multi_index_container< car_model, indexed_by< ordered_unique< key_from_key< const_mem_fun<person,std::string,&person::get_name>, member<car_model,car_manufacturer*,&car_model::manufacturer> > >
mic;
-------------------------------------------------------------------------- As one might see, the change i made is that the key get_name() of the car_manufacturer now comes from a base class called person. However, this code wont compile because the compiler argues that there is no '*' operator for car_manufactuerer. I found out that the problem is the handling with the so called chained pointers in the mem_fun structs. template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const // [1] { return operator()(*x); } Type operator()(const Class& x)const // [2] { return (x.*PtrToMemberFunction)(); } In the example the compiler calls [1] for car_manufacturer*, which seems right. After that, it can chose (among other methods) between [1] and [2] (Class = person) with argument type car_manufacturer and choses the better matching [1] with the followed error. However, maybe things could be done better here. The mem_fun structs could be smart enogh to deal with classes derived from template argument Class. That means [2] should be called in the case of operator() with a "Class"-derived argument. I dont know much about metaprogramming, but the following diff for const_mem_fun (as an example) of file mem_fun.hpp seems to work with a compiler supporting full template specialisation at class scope. Unfortunately gcc up to 3.4 doesn't seem to support this, while icc 8.0 does. I'm pretty sure there is a better sollution than mine, assumed that a "problem" even exists, which is my question to you. :] ------------------------------------------------------------------------------- *************** *** 12,17 **** --- 12,18 ---- #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */ #include <boost/mpl/if.hpp> #include <boost/type_traits/remove_reference.hpp> + #include <boost/type_traits/is_base_and_derived.hpp> namespace boost{ *************** *** 45,54 **** { typedef typename remove_reference<Type>::type result_type; ! template<typename ChainedPtr> ! Type operator()(const ChainedPtr& x)const { ! return operator()(*x); } Type operator()(const Class& x)const --- 46,58 ---- { typedef typename remove_reference<Type>::type result_type; ! template<typename MaybeChainedPtr> ! Type operator()(const MaybeChainedPtr& x)const { ! return dispatch_cptr< typename mpl::if_< ! is_base_and_derived<Class, MaybeChainedPtr>, ! Class, ! MaybeChainedPtr>::type > (x); } Type operator()(const Class& x)const *************** *** 65,70 **** --- 69,87 ---- { return operator()(x.get()); } + + private: + template<typename MaybeChainedPtr> + Type dispatch_cptr(const MaybeChainedPtr& x)const + { + return operator()(*x); + } + + template<> + Type dispatch_cptr<Class>(const Class& x)const + { + return operator()(x); + } }; ------------------------------------------------------------------------------ Would some type handling like this make sense? If not, whats the best way to use dervied member function of a (pointer)member of the container type as key? If yes, are there similar issues in other extractors like member extractor? --David

Hi David, thanks for trying Boost.MultiIndex! David Gruener ha escrito:
Hello,
i just started using the boost::multi_index_container and ran into some trouble. I'm using boost version 1.32. Well, i tried a slightly modified version of Example 6 ("complex searches and foreign keys"). This is the code:
[...]
As one might see, the change i made is that the key get_name() of the car_manufacturer now comes from a base class called person. However, this code wont compile because the compiler argues that there is no '*' operator for car_manufactuerer. I found out that the problem is the handling with the so called chained pointers in the mem_fun structs.
template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const // [1] { return operator()(*x); }
Type operator()(const Class& x)const // [2] { return (x.*PtrToMemberFunction)(); }
In the example the compiler calls [1] for car_manufacturer*, which seems right. After that, it can chose (among other methods) between [1] and [2] (Class = person) with argument type car_manufacturer and choses the better matching [1] with the followed error.
Yes, your analysis of the problem is correct.
However, maybe things could be done better here. The mem_fun structs could be smart enogh to deal with classes derived from template argument Class. That means [2] should be called in the case of operator() with a "Class"-derived argument. I dont know much about metaprogramming, but the following diff for const_mem_fun (as an example) of file mem_fun.hpp seems to work with a compiler supporting full template specialisation at class scope. Unfortunately gcc up to 3.4 doesn't seem to support this, while icc 8.0 does. I'm pretty sure there is a better sollution than mine, assumed that a "problem" even exists, which is my question to you. :]
[...]
------------------------------------------------------------------------------
Would some type handling like this make sense?
Yep, it would make sense. I guess it can be probably simplified, so as to not rely in full template specialisation at class scope. Anyway, please keep reading.
If not, whats the best way to use dervied member function of a (pointer)member of the container type as key?
This is an infortunate problem with pointers to members as template arguments. The expression const_mem_fun<Derived,int,&Base::get> is not valid even though &Base::get is really an int (Derived::*)const(). However, there's a way out using the alternative const_mem_fun_explicit. See the attached example. Funny thing is that const_mem_fun_explicit was never meant to solve this kind of problems :)
If yes, are there similar issues in other extractors like member extractor?
member<> has the very same problem, and in this case there's no alternative member_explicit<>. You can workaround this with a little more typing than desireable. See the attached code for an example. I guess I'll have to consider how to best approach the problem. I'm reluctant to change the extractors as they are relatively fragile for buggy compilers (MSVC++ 6.0) but will think it over. Hope the attached workarounds serve your needs in the meantime. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
participants (2)
-
David Gruener
-
Joaquín Mª López Muñoz