EmDroid
EmDroid

Reputation: 6046

Boost multiindex with vector of keys attribute

I have a class which looks like this (simplified):

class DataObject {
public:
    int getId() const;
    const std::vector<int>& getAccounts() const;
};

What I need to have an associative array (hashmap) of the objects indexed by both id and all the accounts it applies to. I.e. I need to lookup all the DataObject instances which apply to a particular account (whereas a single DataObject can apply to multiple accounts).

What I was thinking first was to use the boost::multi_index_contatiner to index by both. However, I don't know how to create the index of all accounts for the object, and I'm not sure if that is even possible.

What I started with was this:

struct tags
{
    struct id;
    struct account;
};

typedef boost::multi_index_container<
    const DataObject*,
    boost::multi_index::indexed_by<
        // index by the rule id (replacement, removal)
        boost::multi_index::hashed_unique<
            boost::multi_index::tag<tags::id>,
            boost::multi_index::const_mem_fun<DataObject, int, &DataObject::getId>
        >,
        // index by the account
        boost::multi_index::hashed_non_unique<
            boost::multi_index::tag<tags::account>,
            ???
        >
    >
> DataMap_t;

And I don't know what I should put instead of the ??? to extract the single particular accounts. To recap, I do not want to index by the vector of accounts (which I'd know how to do), I need to index by each account in the vector.

So far I actually created a separate boost::unordered_map<int, const DataObject*> index for the IDs and another separate boost::unordered_multimap<int, const DataObject*> index for the account and there I iterate through all the accounts and store a value for each (and the same for the removal). But I'm wondering if there is a simpler way of doing that, preferably with the boost::multi_index_container.

Upvotes: 3

Views: 783

Answers (1)

sehe
sehe

Reputation: 393944

What you need is a relation table.

You could probably use Boost Bimap as well (seems easier to me):

The following code creates an empty bimap container:

typedef bimap<X,Y> bm_type; 
bm_type bm; 

Given this code, the following is the complete description of the resulting bimap.

  • bm.left is signature-compatible with std::map
  • bm.right is signature-compatible with std::map
  • bm is signature-compatible with std::set< relation >

Using Multi-Index I'd suggest storing the relations outside the objects:

struct Data { int id; };
struct Account { int id; };

struct Relation { int data_id, account_id; };

using Table = boost::multi_index_container<Relation, 
    bmi::indexed_by<
        bmi::ordered_unique<
            bmi::tag<struct by_data>,
            bmi::member<Relation, int, &Relation::data_id>
        >
        bmi::ordered_non_unique<
            bmi::tag<struct by_account>,
            bmi::member<Relation, int, &Relation::account_id>
        >
    >
>;

If you are careful about object lifetimes and reference stability, you could go fo a little more caller convenience:

struct Data { int id; };
struct Account { int id; };

struct Relation { 
    std::reference_wrapper<Data const> data;
    std::reference_wrapper<Account const> account;

    int data_id() const    { return data.get().id; }
    int account_id() const { return account.get().id; }
};

using Table = boost::multi_index_container<Relation, 
    bmi::indexed_by<
        bmi::ordered_unique<
            bmi::tag<struct by_data>,
            bmi::const_mem_fun<Relation, int, &Relation::data_id>
        >
        bmi::ordered_non_unique<
            bmi::tag<struct by_account>,
            bmi::const_mem_fun<Relation, int, &Relation::account_id>
        >
    >
>;

Upvotes: 4

Related Questions