Boost logo

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