Boost:列出GraphML中指定的顶点和边属性

Boost: List the vertex and edge attributes specified in GraphML

我希望能够使用 Boost 库的 read_graphml 读取自定义 graphml 文件。然而,这要求我在读取文件时预先指定 properties/attribute 名称。

有没有办法代替列出文件中指定的属性或检查特定属性是否存在?我可能可以通过解析文件来做到这一点,但想知道是否可以通过 Boost 来完成。

我有以下入门概念:

Live On Coliru

#include <boost/graph/graphml.hpp>
#include <boost/core/demangle.hpp>
using namespace boost;

using Graph = adjacency_list<vecS, vecS, undirectedS>;
using Vertex = Graph::vertex_descriptor;
using Edge = Graph::edge_descriptor;

struct MyGraph {
    Graph g;
    dynamic_properties dp { [=](auto const&... args) { return detect_properties(dp, args...); } };

    using Name = std::string;
    using EdgePropMap = std::map<Edge, std::string>;
    std::map<Name, std::shared_ptr<EdgePropMap> > _edge_properties;

    void read(std::istream& graphml) {
        ::boost::read_graphml(graphml, g, dp);
    }
  private:
    boost::shared_ptr<boost::dynamic_property_map> detect_properties(dynamic_properties& dp, Name const& name, boost::any const& key, boost::any const& value) {
        auto value_type = core::demangled_name(value.type());

        if (key.type() == typeid(Graph)) {
            std::cout << "Vertex property: " << name << ", " << value_type << "\n" << std::flush;
            //dp.property(name, boost::make_vector_property_map<Graph>(identity_property_map{}));
            //return dp.lower_bound(name)->second;
        }
        else if (key.type() == typeid(Edge)) {
            std::cout << "Edge property: " << name << ", " << value_type << "\n" << std::flush;
            if (value.type() == typeid(std::string)) {
                auto& map = *_edge_properties.emplace(name, std::make_shared<EdgePropMap>()).first->second;
                dp.property(name, boost::make_assoc_property_map(map));
                return dp.lower_bound(name)->second;
            } else {
                std::cerr << "Value type (" << value_type << ") not supported\n";
            }
        }
        else if (key.type() == typeid(Vertex)) {
            std::cout << "Vertex property: " << name << ", " << value_type << "\n" << std::flush;
            /*if (value.type() == typeid(std::string))*/ {
                dp.property(name, boost::make_vector_property_map<std::string>(get(vertex_index, g)));
                return dp.lower_bound(name)->second;
            }
        } else {
            std::cout << "Unknown property (" << core::demangled_name(key.type()) << ") " << name << ", " << value_type << "\n" << std::flush;
        }
        return nullptr;
    }
};

int main() {
    MyGraph g;
    g.read(std::cin);
}

例如,使用 这个 graphml 的样本输入,输出是 (Live On Coliru):

Vertex property: color, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
Edge property: weight, double
Value type (double) not supported
Edge property: weight, double
Value type (double) not supported
Edge property: weight, double
Value type (double) not supported
Edge property: weight, double
Value type (double) not supported

改进...

设置边缘 属性 映射或者当您希望使用它们支持不同的值类型时,它会变得有点复杂。我建议使用 dynamic_properties 作为对动态 属性 映射的唯一访问,因为这样我们就可以使用 shared_ptr to erase the mapped types of _edge_properties:

Live On Coliru

#include <boost/graph/graphml.hpp>
#include <boost/core/demangle.hpp>
using namespace boost;

using Graph = adjacency_list<vecS, vecS, undirectedS>;
using Vertex = Graph::vertex_descriptor;
using Edge = Graph::edge_descriptor;

struct MyGraph {
    Graph g;
    dynamic_properties dp { [=](auto const&... args) { return detect_properties(args...); } };

    using Name = std::string;
    std::map<Name, std::shared_ptr<void> > _edge_properties;

    void read(std::istream& graphml) {
        ::boost::read_graphml(graphml, g, dp);
    }
  private:
    template<typename Map>
    auto add_property(const std::string& name, Map pmap)
    {
        boost::shared_ptr<dynamic_property_map> pm(
                boost::static_pointer_cast<dynamic_property_map>(
                    boost::make_shared<detail::dynamic_property_map_adaptor<Map> >(pmap)));
        dp.insert(name, pm);
        return pm;
    }

    template <typename V>
    auto add_edge_property(std::string const& name) {
        auto map = std::make_shared<std::map<Edge, V> >();
        _edge_properties.emplace(name, map);
        return add_property(name, boost::make_assoc_property_map(*map));
    }

    template <typename V>
    auto add_vertex_property(std::string const& name) {
        // NOTE, if vertex_index isn't present you might want to use
        // make_assoc_property_map as with the edge properties
        return add_property(name, boost::make_vector_property_map<V>(get(vertex_index, g)));
    }

    boost::shared_ptr<dynamic_property_map> detect_properties(Name const& name, boost::any const& key, boost::any const& value) {
        auto value_type = core::demangled_name(value.type());

        if (key.type() == typeid(Graph)) {
            std::cout << "Graph property detected: " << name << ", " << value_type << "\n" << std::flush;
            //dp.property(name, boost::make_vector_property_map<Graph>(identity_property_map{}));
            //return dp.lower_bound(name)->second;
        }
        else if (key.type() == typeid(Edge)) {
            std::cout << "Edge property detected: " << name << ", " << value_type << "\n" << std::flush;
            if (value.type() == typeid(std::string)) {
                return add_edge_property<std::string>(name);
            } else if (value.type() == typeid(double)) {
                return add_edge_property<double>(name);
            } else {
                std::cerr << "Value type (" << value_type << ") not supported\n";
            }
        }
        else if (key.type() == typeid(Vertex)) {
            std::cout << "Vertex property detected: " << name << ", " << value_type << "\n" << std::flush;
            if (value.type() == typeid(std::string)) {
                return add_vertex_property<std::string>(name);
            } else if (value.type() == typeid(double)) {
                return add_vertex_property<double>(name);
            } else {
                std::cerr << "Value type (" << value_type << ") not supported\n";
            }
        } else {
            std::cout << "Unknown property (" << core::demangled_name(key.type()) << ") " << name << ", " << value_type << "\n" << std::flush;
        }
        return nullptr;
    }
};

int main() {
    MyGraph g;
    g.read(std::cin);
}

现在打印:

Vertex property detected: color, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
Edge property detected: weight, double

备注:

第二个版本实际上比第一个 "mis-used" lower_bound 更安全,可以找到刚刚按名称添加的 属性。如果有 Edge/Vertex 个属性 同名 .

,这可能 严重破坏

第二个版本添加了一个帮助函数来避免这种不准确 (add_property)。