|
Boost : |
From: David Gruener (David.Gruener_at_[hidden])
Date: 2005-02-16 14:55:53
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk