【问题标题】:Boost::Graph: how to import graphviz with custom Vertex classBoost::Graph:如何使用自定义 Vertex 类导入 graphviz
【发布时间】:2021-10-04 05:43:11
【问题描述】:

我有这个 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(&amp;Vertex::p, g)); 一起使用,但它不起作用,因为类型不匹配(可能是因为write_graphviz 而不是)。

【问题讨论】:

    标签: c++ boost graphviz glm-math boost-graph


    【解决方案1】:

    是的,你要求的是英雄主义。 dynamic_properties 工具确实有点假设您将访问对象中的左值属性,而不是转换后的值。

    我以前也遇到过:

    解决它

    这个库是完全通用的。属性映射是高度通用的,不假定左值,这可以从区分ReadWritePropertyMapLvaluePropertyMapconcept 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);
        };
    }
    

    我将根据您预期的文本序列化格式快速填写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/xMKhz9G8ehttps://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 ;
    }
    

    【讨论】:

    • 成功了,谢谢!!我试图实现一个适配器,但我做不到......有时,如何连接 boost 非常复杂,我发现文档并没有很好地解释它。我需要更好地了解boost::get(&amp;Vertex::p, g) 的作用以及更多...再次感谢! ;)
    猜你喜欢
    • 2011-08-12
    • 1970-01-01
    • 2013-01-15
    • 2022-08-23
    • 2011-12-24
    • 1970-01-01
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多