1

Suppose you want to read in a .dot graph into boost where there could be properties you do not recognize. It is easy to ignore them (by passing ignore_other_properties to the dynamic_properties constructor) but what if you wanted all the properties added to dynamic_properties instead?

The below code demonstrates the problem. The handle_custom_properties is a copy of ignore_other_properties and the code will compile / run, reporting "3 vertices, 2 edges." What needs to be added to handle_custom_properties so that, on return, dp will contain a property "label" and node A will have value "x" for the label property?

#include <iostream>
#include <string>

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

struct vertex_p {
    std::string node_id;
};

typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS, vertex_p> graph_t;

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

    // What goes in here to add dynamic property map for property "s" which key-value pair <k,v>?

    // What ignore_other_properties does
    return boost::shared_ptr<boost::dynamic_property_map>();
}

int main() {
    std::string str(R"(graph {
    A [ label="x" ]
    B
    C

    A -- B
    A -- C
}
)");

    try {
        graph_t g;
        boost::dynamic_properties dp{handle_custom_properties};
        dp.property("node_id", get(&vertex_p::node_id, g));

        if (boost::read_graphviz(str, g, dp)) {
            std::cout << "read_graphviz returned success" << std::endl;
            std::cout << "graph stats:" << std::endl;
            std::cout << "  " << g.m_vertices.size() << " vertices" << std::endl;
            std::cout << "  " << g.m_edges.size() << " edges" << std::endl;

        }
        else {
            std::cout << "read_graphviz returned failure" << std::endl;
        }
    }
    catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    catch (boost::exception& e) {
        std::cerr << boost::diagnostic_information(e) << std::endl;
    }
}
Ken Sykes
  • 21
  • 4

1 Answers1

1

I was not able to find an existing class to solve this but implementing a dynamic_property_map in terms of a std::map worked:

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

public:
    boost::any get(const boost::any& key) override { return map_[boost::any_cast<TKey>(key)]; }
    std::string get_string(const boost::any& key) override { std::ostringstream s; s << map_[boost::any_cast<TKey>(key)]; return s.str(); }
    void put(const boost::any& key, const boost::any& value) override { 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, std::string>>();
}

Changing the graph and printing out the properties shows all the property maps were added:

    std::string str(R"(graph {
    A [ label="x", stuff="y" ]
    B
    C [ happy="yes" ]

    A -- B
    A -- C
}
)");

...
           std::cout << "properties:" << std::endl;
           for (const auto& p : dp) {
                std::cout << "  " << p.first << std::endl;
           }

Outputs

read_graphviz returned success
graph stats:
  3 vertices
  2 edges
properties:
  happy
  label
  node_id
  stuff
Ken Sykes
  • 21
  • 4
  • When running your code snippet. I get a error thrown: `boost::bad_any_cast: failed conversion using boost::any_cast` – dotdolfin Mar 02 '23 at 14:32