♻️
Boost Multi-index Containers をいい感じにする
Boost Multi-index Containers Library を使うと複数のキーを持つコンテナが作れる
例えば以下の様なメンバを持ったクラスがあるとする
struct Item {
int item_id;
int item_category_id;
std::string name;
std::string description;
// etc...
};
すると multi_index_container
を使って以下の様に書ける
// タグの定義
struct tag_item_id { };
struct tag_item_category_id { };
struct tag_item_name { };
// コンテナ型の定義
using item_container_t = boost::multi_index_container<
std::shared_ptr<Item>, // 格納する値の型
boost::multi_index::indexed_by<
boost::multi_index::hashed_unique< // 1つ目のキーは item_id
boost::multi_index::tag<tag_item_id>,
boost::multi_index::member<Item, int, &Item::item_id>
>,
boost::multi_index::hashed_non_unique< // 2つ目のキーは item_category_id
boost::multi_index::tag<tag_item_category_id>,
boost::multi_index::member<Item, int, &Item::item_category_id>
>,
boost::multi_index::hashed_non_unique< // 3つ目のキーは name
boost::multi_index::tag<tag_item_name>,
boost::multi_index::member<Item, std::string, &Item::name>
>
>
>;
// コンテナ
item_container_t items;
// 色々入れる
// items.insert(std::make_shared<Item>(...));
// ...
// item_id が 100 のやつを取得
auto it_id_100 = items.get<tag_item_id>().find(100);
// item_category_id が 10 のやつを取得
auto it_category_10 = items.get<tag_item_category_id>().equal_range(10);
// name が "hoge" のやつを取得
auto it_hoge = items.get<tag_item_name>().find("hoge");
これでいいのだがコンテナ型の定義が長すぎて非常にめんどくさい
特にメンバ変数の型とかいちいち書くのだるいしメンバポインタ渡してるんだからそれから取得してほしい
が、下記ドキュメントではそれはできないからあきらめろと書いてある :anger:
It might seem that the first and second parameters are superfluous, since the type of the base object and of the associated data field are already implicit in the pointer to member argument: unfortunately, it is not possible to extract this information with current C++ mechanisms, which makes the syntax of member a little too verbose.
しかし、これは嘘である
今のC++にできないことなどないはずである
なので頑張っていい感じにするヘルパーを作ってみた
これを導入すると以下の様に簡単に書ける
// タグの定義
struct tag_item_name { };
// コンテナ型の定義
using item_container_t = boost::multi_index_container<
std::shared_ptr<Item>, // 格納する値の型
boost::multi_index::indexed_by<
IDX(&Item::item_id)::hashed_unique<>, // 1つ目のキーは item_id
IDX(&Item::item_category_id)::hashed_non_unique<>, // 2つ目のキーは item_category_id
IDX(&Item::name, tag_item_name)::hashed_non_unique<> // 3つ目のキーは name, tag型も指定できる
>
>;
// コンテナ
item_container_t items;
// 色々入れる
// items.insert(std::make_shared<Item>(...));
// ...
// item_id が 100 のやつを取得
auto it_id_100 = items.get<IDX(&Item::item_id)>().find(100);
// item_category_id が 10 のやつを取得
auto it_category_10 = items.get<IDX(&Item::item_category_id)>().equal_range(10);
// name が "hoge" のやつを取得
auto it_hoge = items.get<IDX(&Item::name)>().find("hoge");
auto it_hoge = items.get<tag_item_name>().find("hoge"); // タグ型でもいける
※ IDX
マクロで ##__VA_ARGS__
を使ってるので gcc じゃないとだめ
ちなみに C++17 だと IDX
マクロは不要になる
参考文献
Discussion