Search code examples
c++boostgraphvizboost-graphboost-property-map

Overloading streaming operators for a Boost Graph bundle output for GraphViz


Is it possible to use bundled properties in the Boost Graph Library, with a standard library type, while also using that type's overload of the << stream operator to satisfy write_graphviz?

#include <boost/graph/graphviz.hpp>

namespace boost {
  namespace detail {
    namespace has_left_shift_impl {
      template <typename T>
      inline std::ostream &operator<<(std::ostream &o, const std::array<T,2> &a) {
        o << a[0] << ',' << a[1];
        return o;
      }
    }
  }
}

static_assert(boost::has_left_shift<
                std::basic_ostream<char>,
                std::array<double,2>
              >::value,"");

int main()
{
  using namespace boost;
  typedef adjacency_list<vecS, vecS, directedS, no_property, std::array<double,2>> Graph;
  Graph g;
  add_edge(0, 1, {123,456}, g);
  write_graphviz(std::cout, g, default_writer(),
                   make_label_writer(boost::get(edge_bundle,g)));
  return 0;
}

Faced with a Boost static assert, I modified my code to that above; adopting a suggestion from here, wherein the << implementation is defined within the boost::detail::has_left_shift_impl namespace. Alas, I'm now faced with another error:

/usr/include/boost/lexical_cast.hpp:1624:50: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
                 bool const result = !(out_stream << input).fail();

Is there a way to provide an overload of << which can be used by write_graphviz? I'm using Ubuntu 14.10 and GCC 4.9.1.


Solution

  • You couldn't even do

    std::cout << g[add_edge(0,1,{123,456},g)];
    

    without providing e.g.

    inline static std::ostream& operator<<(std::ostream& os, std::array<double, 2> const& doubles) {
        return os << "{" << doubles[0] << "," << doubles[1] << "}";
    }
    

    Now getting these overloads seen by lexical_cast at the right time is exceptionally hard to do portably (mostly because ADL won't help you with std::array and double).

    Instead you can use a value transforming property map (which is readonly of course):

    std::string prettyprint(std::array<double, 2> const& arr) {
        std::ostringstream oss;
        oss << "{" << arr[0] << "," << arr[1] << "}";
        return oss.str();
    }
    

    and then make_transform_value_property_map(prettyprint, get(edge_bundle, g))

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/graphviz.hpp>
    
    using namespace boost;
    
    std::string prettyprint(std::array<double, 2> const& arr) {
        std::ostringstream oss;
        oss << "{" << arr[0] << "," << arr[1] << "}";
        return oss.str();
    }
    
    int main()
    {
        using namespace boost;
        typedef adjacency_list<vecS, vecS, directedS, no_property, std::array<double,2>> Graph;
        Graph g;
        add_edge(0, 1, {123,456}, g);
        write_graphviz(std::cout, g, default_writer(),
                make_label_writer(make_transform_value_property_map(&prettyprint, get(edge_bundle, g))));
    }
    

    Prints

    digraph G {
    0;
    1;
    0->1 [label="{123,456}"];
    }