首页 文章

将boost图库与顶点的自定义类一起使用

提问于
浏览
3

我目前正在尝试在现有的c项目中使用boost图库 . 我想在boost图中存储自定义类的对象 . 下面是一个小示例,其中包含两个成员(字符串和int)的自定义类定义及其相应的getter方法 .

我有几个问题:

  • 如何在Graphviz输出中包含string和int值?我使用 boost::make_label_writer 发现这个answer类似的问题,但我不确定我的例子是否可以被采用(我使用的是自定义类和共享指针) .

  • 不应该在图中多次存储相同的对象(相同的字符串和int值) . 因此我在自定义类中重载了两个比较运算符 . 我还读到我必须将图形类型定义的第二个模板参数更改为 boost::setS ,但这会导致编译器发出非常长的错误消息...

  • 假设我创建了自定义类的新对象:如何检查它是否已存储在图形中?

#include <iostream>
#include <boost/graph/graphviz.hpp>

class my_custom_class {
    public:

    my_custom_class(const std::string &my_string,
                    int my_int) : my_string(my_string),
                                  my_int(my_int) {}

    virtual ~my_custom_class() {
    }

    std::string get_my_string() const {
        return my_string;
    }

    int get_int() const {
        return my_int;
    }

    bool operator==(const my_custom_class &rhs) const {
        return my_string == rhs.my_string &&
               my_int == rhs.my_int;
    }

    bool operator!=(const my_custom_class &rhs) const {
        return !(rhs == *this);
    }

    private:

    std::string my_string;

    int my_int;
};

namespace boost {
    enum vertex_my_custom_class_t {
        vertex_my_custom_class = 123
    };
    BOOST_INSTALL_PROPERTY(vertex,
                           my_custom_class);
}

int main() {
    typedef boost::adjacency_list<boost::vecS,
            boost::vecS,
            boost::directedS,
            boost::property<boost::vertex_my_custom_class_t,
                            std::shared_ptr<my_custom_class>>> graph_t;

    typedef boost::graph_traits<graph_t>::vertex_descriptor vertex_t;

    std::shared_ptr<my_custom_class> object_one = std::make_shared<my_custom_class>("Lorem", 123);
    std::shared_ptr<my_custom_class> object_two = std::make_shared<my_custom_class>("ipsum", 456);
    std::shared_ptr<my_custom_class> object_three = std::make_shared<my_custom_class>("Lorem", 123);

    std::cout << "object one: " << object_one->get_int() << "; " << object_one->get_my_string() << std::endl;
    std::cout << "object two: " << object_two->get_int() << "; " << object_two->get_my_string() << std::endl;
    std::cout << "object three: " << object_three->get_int() << "; " << object_three->get_my_string() << std::endl;

    std::cout << std::endl;

    std::cout << "object one == object two: " << (*object_one == *object_two) << std::endl;
    std::cout << "object one == object three: " << (*object_one == *object_three) << std::endl;

    std::cout << std::endl;

    graph_t graph;

    vertex_t vertex_one = boost::add_vertex(object_one, graph);
    vertex_t vertex_two = boost::add_vertex(object_two, graph);
    vertex_t vertex_three = boost::add_vertex(object_three, graph);

    boost::add_edge(vertex_one, vertex_two, graph);
    boost::add_edge(vertex_one, vertex_three, graph);

    boost::write_graphviz(std::cout, graph);

    return 0;
}

节目输出:

object one: 123; Lorem
object two: 456; ipsum
object three: 123; Lorem

object one == object two: 0
object one == object three: 1

digraph G {
0;
1;
2;
0->1 ;
0->2 ;
}

1 回答

  • 4
    • 没有改变你的声明,这有点痛苦,但可能:
    {
        boost::dynamic_properties dp;
        boost::property_map<graph_t, boost::vertex_my_custom_class_t>::type custom = get(boost::vertex_my_custom_class, graph);
        dp.property("node_id", boost::make_transform_value_property_map(std::mem_fn(&my_custom_class::get_int), custom));
        dp.property("label", boost::make_transform_value_property_map(std::mem_fn(&my_custom_class::get_my_string), custom));
        boost::write_graphviz_dp(std::cout, graph, dp);
    }
    

    印刷: Live On Coliru

    digraph G {
    123 [label=Lorem];
    456 [label=ipsum];
    123 [label=Lorem];
    123->456 ;
    123->123 ;
    }
    
    • 你需要在外部照顾这个 . 为什么不拥有一组侵入式节点并以这种方式验证约束 . 正如你所说的那样改变顶点容器选择器没有任何效果(它最终会以升序存储顶点描述符,而它们保持前所未有的唯一性) .

    副作用从连续分配的顶点存储变为基于节点(pro:迭代器/参考稳定性,con:分配开销,减少的参考局部性,非隐式vertex_index) . 后者是罪魁祸首:BGL中的许多东西需要一个顶点索引,如果没有暗示(例如使用 vecS ),你必须传递一个 .

    有趣的是,由于我使用 write_graphviz_dp 具有特定的 node_id 属性,因此现在不需要隐式顶点索引,因此您可以将 vecS 更改为 setS 并观察行为: Live On Coliru

    • 我认为现在不是检查的合适时机 . 除了更新外部索引之外,没有比访问所有顶点更好的方法 .

    样式/简化

    因为 std::shared_ptr 暗示你有c 11,所以让我们使用它 .

    此外,整个舞蹈与自定义属性大多是一种更笨拙的做属性包:这些在语法上更容易和更好的支持 .

    看到不同:

    注意我过度热心一秒钟,使用shared-ptr你还需要transform-value-property-map:

    Live On Coliru

    #include <boost/graph/graphviz.hpp>
    #include <boost/property_map/transform_value_property_map.hpp>
    #include <iostream>
    
    struct MyVertex {
        MyVertex(std::string label, int id) : _label(std::move(label)), _id(id) {}
    
        std::string label() const { return _label; }
        int         id()    const { return _id;    }
    
        bool operator<(const MyVertex &rhs) const { return std::tie(_id, _label) < std::tie(rhs._id, rhs._label); }
      private:
        std::string _label;
        int _id;
    };
    
    using graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, std::shared_ptr<MyVertex>>;
    
    int main() {
        graph_t graph;
    
        auto v1 = add_vertex(std::make_shared<MyVertex>("Lorem", 123), graph);
        auto v2 = add_vertex(std::make_shared<MyVertex>("ipsum", 456), graph);
        auto v3 = add_vertex(std::make_shared<MyVertex>("Lorem", 123), graph);
    
        add_edge(v1, v2, graph);
        add_edge(v1, v3, graph);
    
        {
            boost::dynamic_properties dp;
            auto bundle = get(boost::vertex_bundle, graph);
            dp.property("node_id", make_transform_value_property_map(std::mem_fn(&MyVertex::id), bundle));
            dp.property("label", make_transform_value_property_map(std::mem_fn(&MyVertex::label), bundle));
    
            write_graphviz_dp(std::cout, graph, dp);
        }
    }
    

    比较:没有共享指针

    我不是说你不能使用它,但我也不相信你需要它 . 所以,让我们删除它,以便您看到差异:

    Live On Coliru

    #include <boost/graph/graphviz.hpp>
    #include <iostream>
    
    struct MyVertex {
        std::string label;
        int id;
        bool operator<(const MyVertex &rhs) const { return std::tie(id, label) < std::tie(rhs.id, rhs.label); }
    };
    
    using graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, MyVertex>;
    
    int main() {
        graph_t graph;
    
        auto v1 = add_vertex({"Lorem", 123}, graph);
        auto v2 = add_vertex({"ipsum", 456}, graph);
        auto v3 = add_vertex({"Lorem", 123}, graph);
    
        add_edge(v1, v2, graph);
        add_edge(v1, v3, graph);
    
        boost::dynamic_properties dp;
        dp.property("node_id", boost::get(&MyVertex::id, graph));
        dp.property("label", boost::get(&MyVertex::label, graph));
    
        write_graphviz_dp(std::cout, graph, dp);
    }
    

    这大约是代码的一半 . 从这里我们可以探索如何添加所需的功能

    唯一性:让我们试试我们的手

    最简单的方法是在添加新节点之前检查现有节点:

    Live On Coliru

    #include <boost/graph/graphviz.hpp>
    #include <boost/range/iterator_range.hpp>
    #include <iostream>
    
    struct MyVertex {
        std::string label;
        int id;
    
        auto key() const { return std::tie(id,label); }
        bool operator< (const MyVertex &rhs) const { return key() <  rhs.key(); }
        bool operator==(const MyVertex &rhs) const { return key() == rhs.key(); }
        bool operator!=(const MyVertex &rhs) const { return key() != rhs.key(); }
    };
    
    using graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, MyVertex>;
    
    int main() {
        graph_t graph;
    
        auto node = [&graph](std::string name, int id) {
            for (auto&& v : boost::make_iterator_range(vertices(graph)))
                if (graph[v] == MyVertex{name, id})
                    return v;
            return add_vertex({name, id}, graph);
        };
    
        auto v1 = node("Lorem", 123);
        auto v2 = node("ipsum", 456);
        auto v3 = node("Lorem", 123);
    
        assert(v3==v1);
    
        add_edge(v1, v2, graph);
        add_edge(v1, v3, graph);
    
        boost::dynamic_properties dp;
        dp.property("node_id", boost::get(&MyVertex::id, graph));
        dp.property("label", boost::get(&MyVertex::label, graph));
    
        write_graphviz_dp(std::cout, graph, dp);
    }
    

    注意 v3 现在等于 v1

    digraph G {
    123 [label=Lorem];
    456 [label=ipsum];
    123->456 ;
    123->123 ;
    }
    

相关问题