views:

352

answers:

3

If I have an object like this:

struct Bar {
    std::string const& property();
};

I can create a multi-index container for it like this:

struct tag_prop {};
typedef boost::multi_index_container<
 Bar,
 boost::multi_index::indexed_by<
  boost::multi_index::ordered_non_unique<
   boost::multi_index::tag<tag_prop>,
   boost::multi_index::const_mem_fun<
    Bar, const std::string&, &Bar::property
   >
  >
 >
 , ... other indexes
> BarContainer;

But if I have a class like this:

struct Foo {
   Bar const& bar();
};

How can I construct an index on .bar().property() for a container of Foo objects?

Normally I would nest calls to boost::bind, but I can't figure out how to make it work in the context of a multi-index container.

+4  A: 

I believe you need to create a predicate object that takes two instances of Foo and its operator() can call Foo::bar() on both instances.

Something like

struct MyPredicate
{

 bool operator() (const Foo& obj1, const Foo& obj2) const
 {
  // fill in here
 }
};

and then use

...
boost::multi_index::ordered_unique<boost::multi_index::tag<tag_prop>, 
 boost::multi_index::identity<Foo>, MyPredicate>,
...

Check out MultiIndex Ordered indices reference

Stephen Nutt
Perfect, thank you.
Tim Sylvester
+1  A: 

As much as I like using lambdas to do simple things, this can quickly degenerate :)

In your case, since it's a bit more complicated, I would rely either on a free function or a predicate comparator.

The predicate as the advantage of defining types more clearly so it's usually easier to actually bring it in.

Also, for readability's sake, I usually typedef my indexes, which gives:

namespace mi = boost::multi_index;

struct FooComparator
{
  bool operator()(Foo const& lhs, Foo const& rhs) const
  {
    return lhs.bar().property() < rhs.bar().property();
  }
};

typedef mi::ordered_unique <
          mi::tag<tag_prop>,
          mi::identity<Foo>,
          FooComparator
        > foo_bar_index_t;

typedef boost::multi_index_container <
          Foo,
          mi::indexed_by < 
            foo_bar_index_t,
            // ... other indexes
          >
        > foo_container_t;

The predicate approach requires more boilerplate code, but it allows to nicely separate the comparison logic from the index definition, which is itself separated from the container definition.

Clear separation makes it easier to view the structure at a glance.

Matthieu M.
Agreed. I typically use typedefs and namespace aliases as well, but it seemed over-complicated for this trivial example, so I collapsed them to a single declaration.
Tim Sylvester
+3  A: 
Joaquín M López Muñoz