Potenzia il contenitore multiindice con indice basato su valori nidificati
-
06-07-2019 - |
Domanda
Se ho un oggetto come questo:
struct Bar {
std::string const& property();
};
Posso creare un contenitore multi-index per questo in questo modo:
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;
Ma se ho una lezione come questa:
struct Foo {
Bar const& bar();
};
Come posso costruire un indice su .bar (). property ()
per un contenitore di oggetti Foo
?
Normalmente anniderei le chiamate a boost :: bind
, ma non riesco a capire come farlo funzionare nel contesto di un contenitore multiindice.
Soluzione
Credo che sia necessario creare un oggetto predicato che accetta due istanze di Foo e il suo operatore () può chiamare Foo :: bar () su entrambe le istanze.
Qualcosa di simile
struct MyPredicate
{
bool operator() (const Foo& obj1, const Foo& obj2) const
{
// fill in here
}
};
e quindi utilizzare
...
boost::multi_index::ordered_unique<boost::multi_index::tag<tag_prop>,
boost::multi_index::identity<Foo>, MyPredicate>,
...
Altri suggerimenti
Invece di fornire un comparatore definito dall'utente, puoi scrivere un key extractor definito dall'utente:
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;
Vedi Funzioni avanzate degli estrattori di chiavi Boost.MultiIndex
Per quanto mi piace usare lambda per fare cose semplici, questo può degenerare rapidamente :)
Nel tuo caso, dato che è un po 'più complicato, farei affidamento su una funzione gratuita o un comparatore di predicati.
Il predicato ha il vantaggio di definire i tipi in modo più chiaro, quindi di solito è più facile introdurlo.
Inoltre, per motivi di leggibilità, di solito scrivo i miei indici, che danno:
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;
L'approccio predicato richiede più codice boilerplate, ma consente di separare bene la logica di confronto dalla definizione dell'indice, che è essa stessa separata dalla definizione del contenitore.
Una chiara separazione semplifica la visualizzazione della struttura a colpo d'occhio.