3

When loading a .dot file I want to read all properties without defining them before. In the code snippet below I don't want to specify to read weight for example. Other datasets have other properties and I don't want to keep adding those as variables to structs.

boost::dynamic_properties dp;
dp.property( "node_id", get( &Vertex::name, _graph.m_graph ) );
//dp.property("weight", get(&Edge::weight, _graph.m_graph));
boost::read_graphviz( file, _graph.m_graph, dp );

I found an other answer doing something similar, but I do not get it to work for my case. In my dataset the properties are on the edges and they are are numbers like [weight=1]. I tried editing this line: return boost::make_shared<dynamic_property_map_impl<unsigned int, std::string>>(); with different template types but I only get node_id:

return boost::make_shared<dynamic_property_map_impl<std::string, std::string>>();
return boost::make_shared<dynamic_property_map_impl<std::string, float>>();

The full code snippets looks as follows:

#include <boost/graph/graphviz.hpp>
#include <boost/program_options.hpp>
#include <boost/exception/exception.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include "read_graphviz_new.cpp"

template<typename TKey, typename TValue>
class dynamic_property_map_impl : public boost::dynamic_property_map {
    std::map<TKey, TValue>  m_map;

public:
    boost::any get(const boost::any& key) override { return m_map[boost::any_cast<TKey>(key)]; }
    std::string get_string(const boost::any& key) override { std::ostringstream s; s << m_map[boost::any_cast<TKey>(key)]; return s.str(); }
    void put(const boost::any& key, const boost::any& value) override { m_map[boost::any_cast<TKey>(key)] = boost::any_cast<TValue>(value); }
    const std::type_info& key() const override { return typeid(TKey); }
    const std::type_info& value() const override { return typeid(TValue); }
};

boost::shared_ptr<boost::dynamic_property_map>
handle_custom_properties(const std::string&,
    const boost::any&,
    const boost::any&) {

    return boost::make_shared<dynamic_property_map_impl<unsigned int, std::string>>();
}

struct Vertex
{
    std::string name;
};

struct Edge
{
    float weight = 1.f;
};

typedef boost::property<boost::graph_name_t, std::string> graph_p;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge, graph_p> graph_u;

void Graph::Load(std::string filepath) {
    std::ifstream file(filepath);
    graph_u graph;

    try {
        boost::dynamic_properties dp(handle_custom_properties);
        dp.property("node_id", get(&Vertex::name, graph));
        //dp.property("weight", get(&Edge::weight, graph));
        boost::read_graphviz(file, graph, dp);
    }
    catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    catch (boost::exception& e) {
        std::cerr << boost::diagnostic_information(e) << std::endl;
    }
}

My dataset looks as follows:

strict graph  {
1;
2;
3;
...
1 -- 2  [weight=1];
2 -- 3  [weight=8];
...
}
dotdolfin
  • 105
  • 7

1 Answers1

3

I've written similar answers before:

Kudos for getting this far with your own dynamic property map subclass. That's a nice approach as well.

Your problem is that unsigned int only hard-codes for vertex descriptors (edge descriptors are not compatible). Similarly, std::string hard-codes for string values. You need to make both flexible:

boost::shared_ptr<boost::dynamic_property_map>
handle_custom_properties(std::string const&, boost::any const& k, boost::any const& v) {
    using V = graph_u::vertex_descriptor;
    using E = graph_u::edge_descriptor;

    if (k.type() == typeid(V)) {
        if (v.type() == typeid(std::string))
            return boost::make_shared<dynamic_property_map_impl<V, std::string>>();
        if (v.type() == typeid(double))
            return boost::make_shared<dynamic_property_map_impl<V, double>>();
    } else if (k.type() == typeid(E)) {
        if (v.type() == typeid(std::string))
            return boost::make_shared<dynamic_property_map_impl<E, std::string>>();
        if (v.type() == typeid(double))
            return boost::make_shared<dynamic_property_map_impl<E, double>>();
    }
    throw std::runtime_error("Unsupported " + boost::core::demangle(v.type().name()));
}

This will work:

Live On Coliru

#include <boost/exception/diagnostic_information.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/lexical_cast.hpp>

template <typename TKey, typename TValue>
class dynamic_property_map_impl : public boost::dynamic_property_map {
    std::map<TKey, TValue> m_map;

    using any = boost::any;
    TValue& lookup(TKey const& key) { return m_map[key];                  } 
    TValue& lookup(any const& key)  { return lookup(any_cast<TKey>(key)); } 

  public:
    any get(any const& key) override                    { return lookup(key);                                   } 
    std::string get_string(any const& key) override     { return boost::lexical_cast<std::string>(lookup(key)); } 
    void put(any const& key, any const& value) override { lookup(key) = any_cast<TValue>(value);                } 

    std::type_info const& key()   const override { return typeid(TKey);   } 
    std::type_info const& value() const override { return typeid(TValue); } 
};

struct Vertex { std::string id; };
struct Edge {};

using graph_p = boost::property<boost::graph_name_t, std::string>;
using graph_u = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge, graph_p>;

boost::shared_ptr<boost::dynamic_property_map>
handle_custom_properties(std::string const&, boost::any const& k, boost::any const& v) {
    using V = graph_u::vertex_descriptor;
    using E = graph_u::edge_descriptor;

    if (k.type() == typeid(V)) {
        if (v.type() == typeid(std::string))
            return boost::make_shared<dynamic_property_map_impl<V, std::string>>();
        if (v.type() == typeid(double))
            return boost::make_shared<dynamic_property_map_impl<V, double>>();
    } else if (k.type() == typeid(E)) {
        if (v.type() == typeid(std::string))
            return boost::make_shared<dynamic_property_map_impl<E, std::string>>();
        if (v.type() == typeid(double))
            return boost::make_shared<dynamic_property_map_impl<E, double>>();
    }
    throw std::runtime_error("Unsupported " + boost::core::demangle(v.type().name()));
}

int main() try {
    graph_u g;

    boost::dynamic_properties dp(handle_custom_properties);
    dp.property("node_id", get(&Vertex::id, g));

    {
        std::istringstream file(R"(strict graph  {
            1;
            2;
            3;
            1 -- 2  [weight=1];
            2 -- 3  [weight=8];
            })");

        read_graphviz(file, g, dp);
    }

    write_graphviz_dp(std::cout, g, dp);
} catch (boost::exception const& e) {
    std::cerr << diagnostic_information(e) << std::endl;
} catch (std::exception const& e) {
    std::cerr << e.what() << std::endl;
}

Prints

graph G {
1;
2;
3;
1--2  [weight=1];
2--3  [weight=8];
}
sehe
  • 374,641
  • 47
  • 450
  • 633