hlepme
hlepme

Reputation: 11

How to retrieve element from boost::multi_index_container

I'm trying to retrieve a value from a boost::multi_index_container, using a unique numerical id index. I've never used boost::multi_index_container before so I have some trouble understanding how they work. They seem to work a bit like a database, all I want to do is to retrieve an item by specifying the id. Any help would be greatly appreciated.

This is the datatype:

typedef boost::multi_index_container<
    // Multi index container holds pointers to the subnets.
    Subnet6Ptr,
    // The following holds all indexes.
    boost::multi_index::indexed_by<
        // First is the random access index allowing for accessing
        // objects just like we'd do with a vector.
        boost::multi_index::random_access<
            boost::multi_index::tag<SubnetRandomAccessIndexTag>
        >,
        // Second index allows for searching using subnet identifier.
        boost::multi_index::ordered_unique<
            boost::multi_index::tag<SubnetSubnetIdIndexTag>,
            boost::multi_index::const_mem_fun<Subnet, SubnetID, &Subnet::getID>
        >,
        // Third index allows for searching using an output from toText function.
        boost::multi_index::ordered_unique<
            boost::multi_index::tag<SubnetPrefixIndexTag>,
            boost::multi_index::const_mem_fun<Subnet, std::string, &Subnet::toText>
        >
    >
> Subnet6Collection;

The Subnet6Collection object is created when a dhcpv6-server (KEA) loads its config file. This file contains an optional numerical id value for each subnet, SubnetID in the datatype.

I would like to retrieve a Subnet6Ptr by specifying SubnetID.

Upvotes: 1

Views: 535

Answers (2)

hlepme
hlepme

Reputation: 11

Thanks for answering.

I tried the following (SubnetID is just uint32_t so I used 10 for test):

SubnetID id = 10;
Subnet6Collection coll;

auto& indexById = coll.index<SubnetSubnetIdIndexTag>();
auto it = index.find(id);
if (it != index.end()) {
  Subnet6Ptr p = *it;
} else {
  // No such ID found
}

but it does not compile:

Opt18_lease_select.cc:38:24: error: invalid use of ‘struct boost::multi_index::multi_index_container, boost::multi_index::indexed_by >, boost::multi_index::ordered_unique, boost::multi_index::const_mem_fun >, boost::multi_index::ordered_unique, boost::multi_index::const_mem_fun, &isc::dhcp::Subnet::toText> > > >::index’

auto& indexById = coll.index<SubnetSubnetIdIndexTag>();
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Opt18_lease_select.cc:39:17: error: overloaded function with no contextual type information

 auto it = index.find(id);
                 ^~~~

Opt18_lease_select.cc:40:17: error: overloaded function with no contextual type information

 if (it != index.end()) {
                 ^~~

Seems like index() find(), end() can't be used this way, or maybe I'm just missing some header file?

Upvotes: 0

Yes, Mutli-index can be a difficult beast to work with. As I wrote in a different answer, "Boost.Multi-index offers an extremely customisable interface, at the cost of offering an extremely complex interface."

Basically, when you want to access the contents of the container, you do it through one of its indices. You therefore start by obtaining a reference to the index which you want to use (the on tagged SubnetSubnetIdIndexTag in your case), and then treat that index pretty much like a container. Which container that is depends on the index's type. For an oredered unique index (as in your case), that would be somewhat like std::map (but with iterators pointing to values only), or like std::set with a transparent comparator which only compares IDs.

Here's what it looks like in code:

Subnet6Collection coll = something();
SubnetID idToLookFor = something2();

auto& indexById = coll.index<SubnetSubnetIdIndexTag>();
auto it = index.find(idToLookFor);
if (it != index.end()) {
  Subnet6Ptr p = *it;
} else {
  // No such ID found
}

Upvotes: 1

Related Questions