2

The problem is that, I can not write a customly created graph using write_graphviz_dp.

I have create a graph with custom properties:

typedef enum { UNSET = 0, RED = 1, GREEN = 2, BLUE = 3} Color;

enum vertex_one_t { vertex_one };
namespace boost {
  BOOST_INSTALL_PROPERTY(vertex, one);
}

enum vertex_two_t { vertex_two };
namespace boost {
  BOOST_INSTALL_PROPERTY(vertex, two);
}

enum vertex_three_t { vertex_three };
namespace boost {
  BOOST_INSTALL_PROPERTY(vertex, three);
}

typedef boost::property< boost::vertex_index_t, int,
          boost::property< vertex_one_t, Color*,
            boost::property< vertex_two_t, Color*, 
              boost::property< vertex_three_t, Color* > > > > DualVertexProperty;

and I am trying to write the graph using

// Graph structure with dynamic property output
template<typename Graph>
void
write_graphviz_dp(std::ostream& out, const Graph& g,
                  const dynamic_properties& dp,
                  const std::string& node_id = "node_id");

But I am getting an error during boost::dynamic_properties creation.

boost::dynamic_properties dpDual;

boost::property_map<DualGraph, vertex_index_t>::type vIndex = get(boost::vertex_index, g);
    boost::property_map<DualGraph, vertex_one_t>::type vOne = get(vertex_one, g);

dpDual.property("node_id", vIndex);
dpDual.property("Color_1", get(vOne, g));

More specifically a compilation error is on the last line. I am not posting the error message because it is too long.

Angelos
  • 533
  • 6
  • 19

1 Answers1

4

Firstly, you have to pass the property map to dynamic_properties:

dpDual.property("Color_1", vOne); // or maaaaybe
dpDual.property("Color_1", boost::get(vertex_one, g));

Secondly, Graphviz is inherently a text format. Have you defined text-conversions for Color*? If not, how would you like the properties to be presented?

Simple Solution

Why are the properties pointers? Color* is more wasteful than Color (int is potentially smaller than a pointer).

You can leave all the lifetime issues behind and have your wish:

inline static std::ostream& operator<<(std::ostream& os, Color color) {
    return os << static_cast<int>(color);
}

inline static std::istream& operator>>(std::istream& is, Color& color) {
    int dummy;
    is >> dummy;
    color = static_cast<Color>(dummy);
    return is;
}

See Live On Coliru

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

typedef enum { UNSET = 0, RED = 1, GREEN = 2, BLUE = 3} Color;

enum vertex_one_t { vertex_one };
namespace boost { BOOST_INSTALL_PROPERTY(vertex, one); }

enum vertex_two_t { vertex_two };
namespace boost {
    BOOST_INSTALL_PROPERTY(vertex, two);
}

enum vertex_three_t { vertex_three };
namespace boost {
    BOOST_INSTALL_PROPERTY(vertex, three);
}

typedef boost::property< boost::vertex_index_t, int,
        boost::property< vertex_one_t, Color,
        boost::property< vertex_two_t, Color, 
        boost::property< vertex_three_t, Color > > > > DualVertexProperty;

inline static std::ostream& operator<<(std::ostream& os, Color color) {
    return os << static_cast<int>(color);
}

inline static std::istream& operator>>(std::istream& is, Color& color) {
    int dummy;
    is >> dummy;
    color = static_cast<Color>(dummy);
    return is;
}

int main() {
    using DualGraph = boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, DualVertexProperty, boost::no_property>;

    boost::dynamic_properties dpDual;
    DualGraph g;
    std::mt19937 rng { std::random_device{}() };
    boost::generate_random_graph(g, 5, 5, rng);

    boost::property_map<DualGraph, boost::vertex_index_t>::type vIndex = boost::get(boost::vertex_index, g);
    boost::property_map<DualGraph, vertex_one_t>::type vOne = boost::get(vertex_one, g);

    dpDual.property("node_id", vIndex);
    dpDual.property("Color_1", vOne);

    boost::write_graphviz_dp(std::cout, g, dpDual);
}

More complex

You can (try) to make the pointer streamable, but you'll have to think of a way to make the lifetimes and ownership make sense.

Bonus:

Consider bundled properties. Unless you have very specific requirements, there is no need to keep using the archaic internal properties like your code does.

Live On Coliru (compilation timed out)

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

typedef enum { UNSET = 0, RED = 1, GREEN = 2, BLUE = 3} Color;

struct DualVertexProperty {
    Color one, two, three;
};

inline static std::ostream& operator<<(std::ostream& os, Color color) { return os << static_cast<int>(color); }
inline static std::istream& operator>>(std::istream& is, Color& color) { int dummy; is >> dummy; color = static_cast<Color>(dummy); return is; }

int main() {
    using DualGraph = boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, DualVertexProperty, boost::no_property>;

    boost::dynamic_properties dpDual;
    DualGraph g;
    std::mt19937 rng { std::random_device{}() };
    boost::generate_random_graph(g, 5, 5, rng);

    for (auto v: boost::make_iterator_range(boost::vertices(g)))
        g[v] = DualVertexProperty { RED, GREEN, BLUE };

    dpDual.property("node_id", boost::get(boost::vertex_index, g));
    dpDual.property("Color_1", boost::get(&DualVertexProperty::one, g));

    boost::write_graphviz_dp(std::cout, g, dpDual);
}

That's a lot simpler in many ways!

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I am using pointers because neighbour vertices are having share properties, and to avoid the synchronization between them I used pointers. – Angelos May 31 '16 at 22:15
  • ok. That would make it very hard to read in - if at all possible, using read_graphviz. I suppose you could be looking for a lattice, did you see [grid_graph.hpp](http://www.boost.org/doc/libs/1_61_0/libs/graph/doc/grid_graph.html)? – sehe May 31 '16 at 22:32
  • I do not need more than two dimensions, and I will not use read_graphviz, for that graph, I only want to export it. I liked your "new" syntax but still I did not figure out how to use pointers as properties – Angelos May 31 '16 at 22:57
  • I didn't mean more than 2 dimensions (I don't think I implied that). For exporting you should be fine just ostreaming the pointer (by dereferencing it e.g.) – sehe May 31 '16 at 23:00
  • I had tried that `inline static std::ostream& operator<<(std::ostream& os, Color *color) { return os << static_cast(*color); }` but it did not works – Angelos May 31 '16 at 23:08
  • oops I misspoke: `BOOST_STATIC_ASSERT_MSG( (!boost::is_pointer::value), "boost::lexical_cast can not convert to pointers" );` this means it's not supported. Maybe you can get somehwere with references or `reference_wrapper` instead of pointers. – sehe May 31 '16 at 23:09
  • Ok, where did you find that, and what is not supported? (pointers?) – Angelos May 31 '16 at 23:13
  • The compiler found it (for you too) in boost_1_61_0/boost/lexical_cast/detail/converter_lexical_streams.hpp:565. It means `lexical_cast` doesn't support pointers. And dynamic_properties uses `lexical_cast`. Sadly this is related to https://stackoverflow.com/a/34161292/85371 – sehe May 31 '16 at 23:20
  • Is it possible to do it with customized template `VertexPropertyWriter` and use `// Graph structure with customized property output template < typename VertexAndEdgeListGraph, typename VertexPropertyWriter > void write_graphviz(std::ostream& out, const VertexAndEdgeListGraph& g, VertexPropertyWriter vpw);` – Angelos Jun 01 '16 at 11:14
  • Yes. But your question presupposed dynamic properties – sehe Jun 01 '16 at 13:24