是的,你要求的是英雄主义。 dynamic_properties 工具确实有点假设您将访问对象中的左值属性,而不是转换后的值。
我以前也遇到过:
解决它
这个库是完全通用的。属性映射是高度通用的,不假定左值,这可以从区分ReadWritePropertyMap 和LvaluePropertyMap 的concept hierarchy 看出。
所以你实际上可以“只是”编写自己的属性映射适配器:
namespace Adapt {
template <typename Prop> struct Vec3 {
Prop inner;
Vec3(Prop map) : inner(map) { }
// traits
using value_type = std::string;
using reference = std::string;
using key_type = typename boost::property_traits<Prop>::key_type;
using category = boost::read_write_property_map_tag;
friend std::string get(Vec3 adapt, key_type const& key);
friend void put(Vec3 adapt, key_type const& key, value_type const& value);
};
}
我将根据您预期的文本序列化格式快速填写get 和put 的一些合理实现。细节我就不解释了,你可以用你认为合适的任何方式来写:
friend std::string get(Vec3 adapt, key_type const& key) {
auto const& v = get(adapt.inner, key);
std::ostringstream oss;
oss << "(" << v.x << "," << v.y << "," << v.z << ")";
return oss.str();
}
friend void put(Vec3 adapt, key_type const& key, std::string const& value) {
using namespace boost::spirit::x3;
float x,y,z;
auto attr = std::tie(x,y,z);
phrase_parse( //
begin(value), end(value),
'(' > double_ > ',' > double_ > ',' > double_ > ')' > eoi,
space, attr);
put(adapt.inner, key, glm::vec3{x,y,z});
}
完整演示
现在您可以使用调整后的属性映射:
Graph g;
auto id = boost::get(&Vertex::id, g);
auto p = Adapt::Vec3{boost::get(&Vertex::p, g)};
boost::dynamic_properties dp;
dp.property("node_id", id);
dp.property("p", p);
如果我们往返图表:
{
std::ifstream ifs("input.txt", std::ios::binary);
boost::read_graphviz(ifs, g, dp);
}
boost::write_graphviz_dp(std::cout, g, dp);
我们可以看到输入被保留。遗憾的是,在撰写本文时,没有任何在线编译器可以同时支持 Boost 和 GLM,因此您必须自己运行示例(请参阅 https://godbolt.org/z/xMKhz9G8e、https://wandbox.org/permlink/RkulvhWxcRnl1RbC 等)。
为此目的的完整列表:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <iomanip>
struct Vertex {
glm::vec3 p = {};
int id;
};
struct Edge { };
using Graph = boost::adjacency_list<boost::setS, boost::vecS,
boost::undirectedS, Vertex, Edge>;
using VD = Graph::vertex_descriptor;
using ED = Graph::edge_descriptor;
namespace Adapt {
template <typename Prop> struct Vec3 {
Prop inner;
Vec3(Prop map) : inner(map) { }
// traits
using value_type = std::string;
using reference = std::string;
using key_type = typename boost::property_traits<Prop>::key_type;
using category = boost::read_write_property_map_tag;
friend std::string get(Vec3 adapt, key_type const& key) {
auto const& v = get(adapt.inner, key);
std::ostringstream oss;
oss << "(" << v.x << "," << v.y << "," << v.z << ")";
return oss.str();
}
friend void put(Vec3 adapt, key_type const& key, std::string const& value) {
using namespace boost::spirit::x3;
float x,y,z;
auto attr = std::tie(x,y,z);
phrase_parse( //
begin(value), end(value),
'(' > double_ > ',' > double_ > ',' > double_ > ')' > eoi,
space, attr);
put(adapt.inner, key, glm::vec3{x,y,z});
}
};
}
int main()
{
Graph g;
auto id = boost::get(&Vertex::id, g);
auto p = Adapt::Vec3{boost::get(&Vertex::p, g)};
boost::dynamic_properties dp;
dp.property("node_id", id);
dp.property("p", p);
{
std::istringstream iss(R"~(
graph G {
0[p="(30, 3, 2)"]; 1[p="(29, 3, 2)"]; 2[p="(30, 2, 2)"]; 3[p="(30, 3, 3)"];
4[p="(30, 2, 3)"]; 5[p="(29, 3, 3)"]; 6[p="(29, 2, 3)"];
0--1; 2--0; 3--4; 5--3; 6--5; 5--1; 3--0; 4--6; 2--4; })~");
boost::read_graphviz(iss, g, dp);
}
boost::write_graphviz_dp(std::cout, g, dp);
}
打印
graph G {
0 [p="(30,3,2)"];
1 [p="(29,3,2)"];
2 [p="(30,2,2)"];
3 [p="(30,3,3)"];
4 [p="(30,2,3)"];
5 [p="(29,3,3)"];
6 [p="(29,2,3)"];
0--1 ;
2--0 ;
3--4 ;
5--3 ;
6--5 ;
5--1 ;
3--0 ;
4--6 ;
2--4 ;
}