boost graph library, depth_first_search not calling finish_edge in msvc

Viewed 127

I'm using Boost 1.70.0, with vs-2017. When using depth_first_search I've observed that the finish_edge function in the visitor is not called when compiled with msvc compiler. With gcc (8.3) the finish_edge function is called properly

the sample code:

struct DfsVisitor : public boost::default_dfs_visitor
{
    template <class Graph>
    void
    finish_edge(typename Graph::edge_descriptor ed, const Graph& g)
    {
        std::cout << "Finish edge " << boost::source(ed, g) << "->" << boost::target(ed, g) << std::endl;
    }
};

DfsVisitor dfs;
boost::depth_first_search(g, boost::visitor(dfs)); // g is graph, adjacency_list
2 Answers

Do you have a SSCCE that we can actually run to observe the behavior? That will save a lot of time

I just did that: Just did that: repro Boost 1.60 on msvc

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <iostream>

struct DfsVisitor : public boost::default_dfs_visitor {
    template <class Graph> void finish_edge(typename Graph::edge_descriptor ed, const Graph &g) {
        std::cout << "Finish edge " << boost::source(ed, g) << "->" << boost::target(ed, g) << std::endl;
    }
};

int main() {
    boost::adjacency_list<> g(4);
    add_edge(0,1,g);
    add_edge(1,2,g);
    add_edge(2,3,g);

    DfsVisitor dfs;
    boost::depth_first_search(g, boost::visitor(dfs));

    std::cout << "Done\n";
}

Just prints:

Done

For comparison,

So apparently it's more related to the boost version than the compiler. Godbolt doesn't go farther back than Boost 1.64 (still ok: https://godbolt.org/z/Ld8-8d) but wandbox does:

Checking the release notes for Boost 1.62.0 doesn't seem to mention something, but using the github history does uncover:

$ git clone https://github.com/boostorg/graph
$ cd graph
$ git log --oneline --graph --left-right --cherry-pick boost-1.61.0...boost-1.62.0 | grep -i finish
> a14f8df8 Fixed bug 10231 partly: If finish_edge was called, then now correctly. (#16)
> d6b7a717 Add finish_edge test case from Alex Lauser.
> 6a2d45ae Condition TTI finish_edge on supported compilers.
> 0e1414f4 Fix type traits so finish_edge is called when defined.

I had the same problem with boost-1.70 and VS-2013 (x64, 18.00). Looking at the implementation in boost, there is a different implementation for other compilers than GCC, Clang or Intel compiler:

    template <typename E, typename G, typename Vis>
    void call_finish_edge(Vis& vis, E e, const G& g) { // Only call if method exists
#if ((defined(__GNUC__) && (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9))) || \
    defined(__clang__) || \
    (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1200)))
    do_call_finish_edge<
        has_member_function_finish_edge<Vis, void,
        boost::mpl::vector<E, const G&> >::value>::call_finish_edge(vis, e, g);
#else
    do_call_finish_edge<has_member_function_finish_edge<Vis, void>::value>::call_finish_edge(vis, e, g);
#endif
    }

For other compilers, the arguments are not given as template arguments when calling method has_member_function_finish_edge (produced by the macro BOOST_TTI_HAS_MEMBER_FUNCTION(finish_edge))

So we can make boost detect it by adding another empty method with no argument:

template <class Edge, class Graph>
void finish_edge(Edge e, Graph& g) {
     std::cout << "finish_edge e=" << e << std::endl;
} 

void finish_edge() {}

The empty method never gets called, but it's enough for boost to detect it. It would be nice not having to do this.

Related