Objective:
I want to wrap a Boost Graph into a class on which I have a bit more control, reduce the noise in the main function, and print a graphviz format.
Problem
The code fails to compile, returning a include/boost/graph/detail/adjacency_list.hpp:2601:27: error: cannot form a reference to 'void'
Minimal reproducible example
The code compiles on Compiler Explorer https://godbolt.org/z/9beKovxc9 But if you uncomment the last line of main the error is reproduced.
// BGL
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/graphviz.hpp>
#include <iostream> // std::cout
#include <functional> // std::function
#include <utility> // std::pair
///
/// @brief Defines the desired properties and constraints for a coalescent graph
///
struct tree_traits
{
///
/// @brief Trees are sparse graph in nature, adjacency_matrix would not be justified here
///
template<class... Types>
using implementation = boost::adjacency_list<Types...>;
///
/// @brief We want to enforce avoiding multi-graphs (edges with same end nodes)
///
using out_edge_list_type = boost::setS;
///
/// @brief We are prioritizing speed for space
///
using vertex_list_type = boost::listS;
///
/// @brief Coalescent trees are directed acyclic graphs
///
using directed_type = boost::directedS ;
}; // end tree_traits
///
/// @brief A graph with properties suited to represent a coalescent tree
///
template<class VertexProperties=boost::no_property, class EdgeProperties=boost::no_property>
class Tree
{
public:
///
/// @brief Properties of an edge, like the demes visited
/// @see https://www.boost.org/doc/libs/1_80_0/libs/graph/doc/bundles.html
///
using edge_properties = EdgeProperties;
///
/// @brief Properties of a vertex, like the mutational state
/// @see https://www.boost.org/doc/libs/1_80_0/libs/graph/doc/bundles.html
///
using vertex_properties = VertexProperties;
///
/// @brief The type of graph hold by the tree class
///
using graph_type = tree_traits::implementation
<
tree_traits::out_edge_list_type,
tree_traits::vertex_list_type,
tree_traits::directed_type,
VertexProperties,
EdgeProperties
>;
private:
graph_type _graph;
static constexpr bool vertex_prop_undefined = std::is_same<vertex_properties,boost::no_property>::value;
static constexpr bool edge_prop_undefined = std::is_same<edge_properties,boost::no_property>::value;
public:
/// @note adds an object-oriented layer
auto add_vertex(auto&&... args)
{
return boost::add_vertex(std::forward<decltype(args)>(args)..., _graph);
}
/// @note adds an object-oriented layer
/// @note order of vertices determines edge orientation
auto add_edge(auto&&... args)
{
return boost::add_edge(std::forward<decltype(args)>(args)..., _graph);
}
void to_graphviz(std::ostream& out) const
{
using namespace boost;
return boost::write_graphviz(out, _graph,
boost::make_label_writer(boost::get(vertex_bundle, this->_graph)),
boost::make_label_writer(boost::get(edge_bundle, this->_graph))
);
}
};
int main()
{
using q_tree_type = Tree<std::string, double>;
q_tree_type graph;
// We insert the root of tree
auto root = graph.add_vertex("first");
//graph.to_graphviz(std::cout); // <--- huho
}
What I can think of
- I thought that (maybe) having a pure
std::string
asVertexProperties
rather than a custom structure confuses the compiler. I tried to disambiguate withif constexpr
, to use adefault_writer
if theVertexProperties
type evaluated tono_property
. But no success on this front, but here is the embarassingly naive code I wrote
// member of Tree<...> class
void to_graphviz(std::ostream& out) const
{
using namespace boost;
if constexpr (vertex_prop_undefined)
{
if constexpr (edge_prop_undefined)
return write_graphviz(out, _graph);
return boost::write_graphviz(out, _graph, default_writer(), boost::make_label_writer(boost::get(edge_bundle, this->_graph)));
}
if constexpr (edge_prop_undefined)
return write_graphviz(out, _graph, boost::make_label_writer(boost::get(vertex_bundle, this->_graph)));
return boost::write_graphviz(out, _graph, boost::make_label_writer(boost::get(vertex_bundle, this->_graph)), boost::make_label_writer(boost::get(edge_bundle, this->_graph)));
}