Question

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.

Was it helpful?

Solution

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

OTHER TIPS

Rather than providing a user-defined comparator, you can write a user-defined key extractor:

struct FooBarPropertyExtractor
{
  typedef std::string result_type;
  const result_type& oeprator()(const Foo& f)
  {
    return f.bar().property();
  }
};

...

typedef boost::multi_index_container<
        Bar,
        boost::multi_index::indexed_by<
                boost::multi_index::ordered_non_unique<
                        boost::multi_index::tag<tag_prop>,
                        FooBarPropertyExtractor
                >
        >
        , ... other indexes
> FooContainer;

See Advanced features of Boost.MultiIndex key extractors

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 has 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top