#c #boost #boost-graph
#c #boost #boost-график
Вопрос:
Я хотел бы иметь возможность читать пользовательские файлы graphml с использованием библиотеки Boost read_graphml
. Это, однако, требует, чтобы я заранее указывал имена свойств / атрибутов при чтении файла.
Есть ли способ вместо этого перечислить атрибуты, указанные в файле, или проверить, существует ли определенный атрибут? Вероятно, я могу сделать это, проанализировав файл, но мне было интересно, можно ли это сделать с помощью Boost.
Комментарии:
1. У вас есть образец GraphML? Или фрагмент кода, поведение которого вы хотели бы изменить?
Ответ №1:
У меня есть следующая начальная концепция:
#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 constamp;... 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::istreamamp; graphml) {
::boost::read_graphml(graphml, g, dp);
}
private:
boost::shared_ptr<boost::dynamic_property_map> detect_properties(dynamic_propertiesamp; dp, Name constamp; name, boost::any constamp; key, boost::any constamp; 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)) {
autoamp; 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 supportedn";
}
}
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 для удаления отображенных типов _edge_properties
:
#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 constamp;... args) { return detect_properties(args...); } };
using Name = std::string;
std::map<Name, std::shared_ptr<void> > _edge_properties;
void read(std::istreamamp; graphml) {
::boost::read_graphml(graphml, g, dp);
}
private:
template<typename Map>
auto add_property(const std::stringamp; 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 constamp; 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 constamp; 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 constamp; name, boost::any constamp; key, boost::any constamp; 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 supportedn";
}
}
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 supportedn";
}
} 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
Примечания:
Вторая версия на самом деле намного безопаснее, поскольку первая «неправильно использовалась» lower_bound
для поиска свойства, которое было только что добавлено по имени. Это могло бы сильно сломаться, если бы были свойства ребра / вершины с тем же именем.
Вторая версия добавляет вспомогательную функцию, которая позволяет избежать этой неточности ( add_property
).
Комментарии:
1. Если вы просто хотели «обнаружить» (а не использовать) свойства, то, очевидно, код намного проще.