Yeah, you're asking for heroics. The dynamic_properties
facility really sort-of assumes you will be accessing lvalue properties in objects, not transformed values.
I've run into this before:
Solving It
The library is thoroughly generic. Property Maps are highly generic and do NOT assume lvalue-ness, which can be seen from the concept hierarchy that distinguishes between ReadWritePropertyMap
and LvaluePropertyMap
.
So you can actually "just" write your own property map adaptor:
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);
};
}
I'll quickly fill in some sensible implementation for get
and put
based on your expected text serialized format. I won't explain the details, and you can write it in any way you deem appropriate:
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});
}
Full Demo
Now you can use the adapted property map:
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);
And if we roundtrip the graph:
{
std::ifstream ifs("input.txt", std::ios::binary);
boost::read_graphviz(ifs, g, dp);
}
boost::write_graphviz_dp(std::cout, g, dp);
We can see that input is preserved. Sadly no online compilers can support both Boost and GLM at the time of writing, so you'll have to run the example yourself (see https://godbolt.org/z/xMKhz9G8e, https://wandbox.org/permlink/RkulvhWxcRnl1RbC etc).
Full listing for that purpose:
#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);
}
Prints
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 ;
}