Boost::Graph:如何使用自定义顶点导入 graphviz class

Boost::Graph: how to import graphviz with custom Vertex class

我有这个 graphviz 输入:

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;
}

类型有:

struct Vertex
{
    glm::vec3 p = {};
    // ...
};

typedef
boost::adjacency_list<
    boost::setS,
    boost::vecS,
    boost::undirectedS,
    Vertex,
    Edge>
Graph;

如何使用 boost::read_graphviz 并将其设置为正确地将 graphviz 中的 p 属性 适配到结构 Vertexp 字段?我尝试将 boost::dynamic_propertiesdp.property("p", boost::get(&Vertex::p, g)); 一起使用,但它不起作用,因为类型不匹配(也许是因为是 write_graphviz)。

是的,你要求英雄气概。 dynamic_properties 工具实际上假设您将访问对象中的左值属性,而不是转换后的值。

我以前 运行 参与过这个:

解决它

该库是完全通用的。 属性 映射是高度通用的并且不假定左值,这可以从 concept hierarchy that distinguishes between ReadWritePropertyMap and LvaluePropertyMap.

中看出

因此您实际上可以“只”编写自己的 属性 地图适配器:

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);
    };
}

我将根据您预期的文本序列化格式快速填写 getput 的一些合理实施。具体的我就不解释了,大家可以按照自己认为合适的方式来写:

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 ;
}