提升具有关联值的双图
boost bimap with associated value
有没有办法构建一个 boost::bimap(或多索引容器),它有两个键,加上它们都指向的一个值?另外,可以查询一个key得到另一个吗?
我可以构造一个 boost 多索引容器,它对某个元素有两个键,但无法弄清楚如何在给定另一个键的值的情况下获取一个键的值?
我正在尝试做类似的事情:
struct s {};
int main()
{
typedef std::map<int, std::shared_ptr<s>> left_map_type;
typedef std::map<std::string, std::shared_ptr<s>> right_map_type;
typedef boost::bimap<left_map_type, right_map_type> bi_map_type;
typedef bi_map_type::value_type value_type;
bi_map_type bim;
std::shared_ptr<s> sp = std::make_shared<s>();
std::shared_ptr<s> sp2 = std::make_shared<s>();
left_map_type lm { {1, sp}, {2, sp2} };
right_map_type rm { {"foo", sp}, {"bar", sp2} };
bim.insert(lm, rm);
/*
For instance, given the key "foo", search bim to obtain BOTH the shared pointer sp as well as the alternate key '1'.
*/
}
我会在像这样的记录上使用多索引容器:
struct Data { }; // your "s"
struct Record {
int id;
std::string name;
Data data; // std::shared_ptr<Data>?
};
现在您可以制作一个容器,通过 ID 和名称添加唯一索引:
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
bmi::ordered_unique< bmi::tag<struct by_id>, bmi::member<Record, int, &Record::id>>,
bmi::ordered_unique< bmi::tag<struct by_name>, bmi::member<Record, std::string, &Record::name>>
> >;
格式化更详细:
using Table = boost::multi_index_container<
Record,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_id>,
bmi::member<Record, int, &Record::id>>,
bmi::ordered_unique<
bmi::tag<struct by_name>,
bmi::member<Record, std::string, &Record::name>>>>;
See below for a less verbose way;
现在您可以创建 table 并使用任何索引访问它:
Table table;
auto& left = table.get<by_id>(); // or get<0>
auto& right = table.get<by_name>(); // or get<1>
无论您使用什么接口,任何更改都会反映在所有其他索引中,并且保证了唯一性约束。例如
table.emplace(1, "one", Data{"Sleepy", {1, 2, 3}});
table.emplace(2, "two", Data{"Grumpy", {2, 4, 6}});
table.emplace(3, "three", Data{"Sneezy", {3, 6, 9}});
只是打印它们(使用 libfmt 进行演示):
// Simple enumeration:
fmt::print("Just the table:\n - {}\n", fmt::join(table, "\n - "));
fmt::print("By id:\n - {}\n", fmt::join(left, "\n - "));
fmt::print("By name:\n - {}\n", fmt::join(right, "\n - "));
版画
Just the table:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
By id:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
By name:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
这说明默认索引是第一个声明的索引(这里的“左视图”,或者我更喜欢这样称呼它:by_id
)。
搜索就像您对标准容器的期望一样:
auto id2 = left.find(2);
auto nameTwo = right.find("two");
if (id2 != left.end())
fmt::print("id2: {}\n", *id2);
if (nameTwo != right.end())
fmt::print("nameTwo: {}\n", *nameTwo);
对于非唯一索引,equal_range
很有用。有 lower_bound
和 upper_bound
等等
现场演示
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>
#include <memory>
struct Data {
std::string extra;
std::vector<int> ints;
};
struct Record {
int id;
std::string name;
Data data; // std::shared_ptr<Data>?
};
namespace bmi = boost::multi_index;
#define Index(name) \
bmi::ordered_unique< \
bmi::tag<struct by_##name>, \
bmi::member<Record, decltype(Record::name), &Record::name>>
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
Index(id),
Index(name)
> >;
#include <fmt/ranges.h>
template <>
struct fmt::formatter<Data, char> : fmt::formatter<std::string, char> {
auto format(Data const& data, auto& ctx) {
return fmt::format_to(ctx.out(), "Data{{{}, {}}}", data.extra,
data.ints);
}
};
template <>
struct fmt::formatter<Record, char> : fmt::formatter<std::string, char> {
auto format(Record const& rec, auto& ctx) {
return fmt::format_to(ctx.out(), "Record{{{}, {}, {}}}", rec.id,
rec.name, rec.data);
}
};
int main()
{
Table table;
auto& left = table.get<by_id>(); // or get<0>
auto& right = table.get<by_name>(); // or get<1>
table.emplace(1, "one", Data{"Sleepy", {1, 2, 3}});
table.emplace(2, "two", Data{"Grumpy", {2, 4, 6}});
table.emplace(3, "three", Data{"Sneezy", {3, 6, 9}});
// Simple enumeration:
fmt::print("Just the table:\n - {}\n", fmt::join(table, "\n - "));
fmt::print("By id:\n - {}\n", fmt::join(left, "\n - "));
fmt::print("By name:\n - {}\n", fmt::join(right, "\n - "));
// find:
auto id2 = left.find(2);
auto nameTwo = right.find("two");
if (id2 != left.end())
fmt::print("id2: {}\n", *id2);
if (nameTwo != right.end())
fmt::print("nameTwo: {}\n", *nameTwo);
}
正在打印上图。
高级Topics/Usages
需要记住的几点:
- 如果您确实需要共享数据的所有权,当然可以使用
shared_ptr<Data>
- 您还可以在 references/pointers 上构建多索引容器,因此我建议您不要 shared_ptr 除非您知道它实际上是必需的
- 存在更灵活的密钥提取机制(例如,您可以在
Record::data::ints::length()
上有一个非唯一的有序索引)
- 你可以有组合键,它支持对有序索引的部分查询。这会启用“类似数据库”的查询,请参见例如Boost multi-index container vs a multi-level mapping container based on std::unordered_map (map of maps) or Using boost multi index like relational DB
- 与标准容器一样,关键元素是
const
。在多索引容器中,这意味着所有访问器 return const 引用。 modify
、replace
函数请参考文档。
- 有一个
project
函数可以在索引之间转换迭代器,如果您需要这个
奖励:更简洁?
或者,使用巧妙的宏来减少重复¹
#define Index(name) \
bmi::ordered_unique< \
bmi::tag<struct by_##name>, \
bmi::member<Record, decltype(Record::name), &Record::name>>
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
Index(id),
Index(name)
> >;
¹ 我暂时懒得制作该模板元函数而不是宏
有没有办法构建一个 boost::bimap(或多索引容器),它有两个键,加上它们都指向的一个值?另外,可以查询一个key得到另一个吗?
我可以构造一个 boost 多索引容器,它对某个元素有两个键,但无法弄清楚如何在给定另一个键的值的情况下获取一个键的值?
我正在尝试做类似的事情:
struct s {};
int main()
{
typedef std::map<int, std::shared_ptr<s>> left_map_type;
typedef std::map<std::string, std::shared_ptr<s>> right_map_type;
typedef boost::bimap<left_map_type, right_map_type> bi_map_type;
typedef bi_map_type::value_type value_type;
bi_map_type bim;
std::shared_ptr<s> sp = std::make_shared<s>();
std::shared_ptr<s> sp2 = std::make_shared<s>();
left_map_type lm { {1, sp}, {2, sp2} };
right_map_type rm { {"foo", sp}, {"bar", sp2} };
bim.insert(lm, rm);
/*
For instance, given the key "foo", search bim to obtain BOTH the shared pointer sp as well as the alternate key '1'.
*/
}
我会在像这样的记录上使用多索引容器:
struct Data { }; // your "s"
struct Record {
int id;
std::string name;
Data data; // std::shared_ptr<Data>?
};
现在您可以制作一个容器,通过 ID 和名称添加唯一索引:
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
bmi::ordered_unique< bmi::tag<struct by_id>, bmi::member<Record, int, &Record::id>>,
bmi::ordered_unique< bmi::tag<struct by_name>, bmi::member<Record, std::string, &Record::name>>
> >;
格式化更详细:
using Table = boost::multi_index_container<
Record,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_id>,
bmi::member<Record, int, &Record::id>>,
bmi::ordered_unique<
bmi::tag<struct by_name>,
bmi::member<Record, std::string, &Record::name>>>>;
See below for a less verbose way;
现在您可以创建 table 并使用任何索引访问它:
Table table;
auto& left = table.get<by_id>(); // or get<0>
auto& right = table.get<by_name>(); // or get<1>
无论您使用什么接口,任何更改都会反映在所有其他索引中,并且保证了唯一性约束。例如
table.emplace(1, "one", Data{"Sleepy", {1, 2, 3}});
table.emplace(2, "two", Data{"Grumpy", {2, 4, 6}});
table.emplace(3, "three", Data{"Sneezy", {3, 6, 9}});
只是打印它们(使用 libfmt 进行演示):
// Simple enumeration:
fmt::print("Just the table:\n - {}\n", fmt::join(table, "\n - "));
fmt::print("By id:\n - {}\n", fmt::join(left, "\n - "));
fmt::print("By name:\n - {}\n", fmt::join(right, "\n - "));
版画
Just the table:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
By id:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
By name:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
这说明默认索引是第一个声明的索引(这里的“左视图”,或者我更喜欢这样称呼它:by_id
)。
搜索就像您对标准容器的期望一样:
auto id2 = left.find(2);
auto nameTwo = right.find("two");
if (id2 != left.end())
fmt::print("id2: {}\n", *id2);
if (nameTwo != right.end())
fmt::print("nameTwo: {}\n", *nameTwo);
对于非唯一索引,equal_range
很有用。有 lower_bound
和 upper_bound
等等
现场演示
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>
#include <memory>
struct Data {
std::string extra;
std::vector<int> ints;
};
struct Record {
int id;
std::string name;
Data data; // std::shared_ptr<Data>?
};
namespace bmi = boost::multi_index;
#define Index(name) \
bmi::ordered_unique< \
bmi::tag<struct by_##name>, \
bmi::member<Record, decltype(Record::name), &Record::name>>
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
Index(id),
Index(name)
> >;
#include <fmt/ranges.h>
template <>
struct fmt::formatter<Data, char> : fmt::formatter<std::string, char> {
auto format(Data const& data, auto& ctx) {
return fmt::format_to(ctx.out(), "Data{{{}, {}}}", data.extra,
data.ints);
}
};
template <>
struct fmt::formatter<Record, char> : fmt::formatter<std::string, char> {
auto format(Record const& rec, auto& ctx) {
return fmt::format_to(ctx.out(), "Record{{{}, {}, {}}}", rec.id,
rec.name, rec.data);
}
};
int main()
{
Table table;
auto& left = table.get<by_id>(); // or get<0>
auto& right = table.get<by_name>(); // or get<1>
table.emplace(1, "one", Data{"Sleepy", {1, 2, 3}});
table.emplace(2, "two", Data{"Grumpy", {2, 4, 6}});
table.emplace(3, "three", Data{"Sneezy", {3, 6, 9}});
// Simple enumeration:
fmt::print("Just the table:\n - {}\n", fmt::join(table, "\n - "));
fmt::print("By id:\n - {}\n", fmt::join(left, "\n - "));
fmt::print("By name:\n - {}\n", fmt::join(right, "\n - "));
// find:
auto id2 = left.find(2);
auto nameTwo = right.find("two");
if (id2 != left.end())
fmt::print("id2: {}\n", *id2);
if (nameTwo != right.end())
fmt::print("nameTwo: {}\n", *nameTwo);
}
正在打印上图。
高级Topics/Usages
需要记住的几点:
- 如果您确实需要共享数据的所有权,当然可以使用
shared_ptr<Data>
- 您还可以在 references/pointers 上构建多索引容器,因此我建议您不要 shared_ptr 除非您知道它实际上是必需的
- 存在更灵活的密钥提取机制(例如,您可以在
Record::data::ints::length()
上有一个非唯一的有序索引) - 你可以有组合键,它支持对有序索引的部分查询。这会启用“类似数据库”的查询,请参见例如Boost multi-index container vs a multi-level mapping container based on std::unordered_map (map of maps) or Using boost multi index like relational DB
- 与标准容器一样,关键元素是
const
。在多索引容器中,这意味着所有访问器 return const 引用。modify
、replace
函数请参考文档。 - 有一个
project
函数可以在索引之间转换迭代器,如果您需要这个
奖励:更简洁?
或者,使用巧妙的宏来减少重复¹
#define Index(name) \
bmi::ordered_unique< \
bmi::tag<struct by_##name>, \
bmi::member<Record, decltype(Record::name), &Record::name>>
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
Index(id),
Index(name)
> >;
¹ 我暂时懒得制作该模板元函数而不是宏