Skip to content

feature: add support for stateful visitors for DFS - #614

Merged
Becheler merged 1 commit into
boostorg:developfrom
Becheler:feature/dfs-visitor-std-ref
Oct 8, 2026
Merged

Becheler merged 1 commit into
boostorg:developfrom
Becheler:feature/dfs-visitor-std-ref

Conversation

@Becheler

@Becheler Becheler commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Before submitting

  • This PR targets the develop branch.
  • I searched for an existing PR or issue covering the same change.
  • My contribution is licensed under the Boost Software License 1.0.

Type of change

  • Bug fix
  • New feature or API addition
  • Refactor (no behavior change)
  • Documentation
  • Build, CI, or tooling
  • Other (specify below)

Does this PR introduce a breaking change?

  • Yes (describe migration impact below)
  • No

What this PR does

Brings a trait to unwrap visitors that are passed by copy to algorithms internals, so they can carry state if the caller passed a reference wrapper.

Motivation

Stateful visitors has been a complaint for a while. Capturing state in the visitor as reference/pointers members works but violates guidelines and tooling complains.

There are 3 main designs for stateful visitors. BGL mixes conventions, so we need to pick one.

  1. Passing by reference. Used by a minority of algorithms, not the STL convention: the standard lets algorithms copy function objects freely. A reference in the signature only holds if no internal helper ever copies the visitor: if one does, state is silently lost. It also doesn't bind to temporaries like make_dfs_visitor(...).
  2. Passing by value, returning the visitor. This steals the return type, e.g. bellman_ford_shortest_paths already returns a bool. In the STL only std::for_each does this, because getting the visitor back is essentially its only purpose.
  3. Passing by value, allowing std::reference_wrapper. Default behavior is unchanged (non breaking, temporaries still work), sharing state is an explicit opt in at the call site (std::ref(vis)), and internal copies only copy the wrapper, so state can't be lost. It's the same idiom as std::thread, std::bind and std::make_tuple, and it avoids reference members in visitors, which tooling flags.

Testing

Checklist

  • Existing tests pass (b2 in the test/ directory).
  • New behavior is covered by a test, or this is a docs / build / refactor change.
  • Documentation was updated if user-facing behavior changed.
  • No new compiler warnings on the platforms I built against.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Boost dependency footprint vs develop (auto-generated).
PR run 37762587910 vs develop run 37743356514 (f526341247).

Header-inclusion weights (graph files pulling each direct dependency in):

No header-inclusion-weight changes.

Transitive Boost modules: 47 → 47 (0)

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Compiler-warning counts vs develop (auto-generated).
PR run 37762587152 vs develop run 37743356649 (f526341247).

Job Baseline After Delta
macos (clang, 14) 392 392 0
macos (clang, 17) 391 391 0
macos (clang, 20) 391 391 0
ubuntu (clang-19, 14) 392 392 0
ubuntu (clang-19, 17) 391 391 0
ubuntu (clang-19, 20) 391 391 0
ubuntu (clang-19, 23) 391 391 0
ubuntu (gcc-14, 14) 345 345 0
ubuntu (gcc-14, 17) 341 341 0
ubuntu (gcc-14, 20) 341 341 0
ubuntu (gcc-14, 23) 341 341 0
windows_msvc_14_3 (msvc-14.3) 931 931 0

@Becheler
Becheler force-pushed the feature/dfs-visitor-std-ref branch from 4b1d53b to 6a3dfb8 Compare September 29, 2026 11:23
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Becheler
Becheler force-pushed the feature/dfs-visitor-std-ref branch 2 times, most recently from 9e74c43 to b4df0b2 Compare October 2, 2026 14:41
@Becheler
Becheler marked this pull request as ready for review October 2, 2026 15:38
@Becheler

Becheler commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

@andreacassioli what do you think about this PR ? Don't hesitate to give a review, I'm mostly trying to assess if it's a good way to enable stateful visitors through std::reference_wrappers ?

@mborland mborland left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks generally good to me. I think it would also be beneficial to add this new support to the DFS docs since it looks like the stateful visitor needs to be of a specific form like in your tests:

struct finish_edge_tally : boost::dfs_visitor<>
{
    template < class Edge, class Graph > void finish_edge(Edge, Graph&)
    {
        ++count;
    }
    std::size_t count = 0;
};

{
using type = Visitor;
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I like to do is add at C++14 style helper:

template <class Visitor>
using unwrap_visitor_t = typename unwrap_visitor<Visitor>::type

that way at the call sites below instead of having:

using visitor_type
        = typename ::boost::graph::detail::unwrap_visitor< DFSVisitor >::type;

you have instead

using visitor_type = detail::unwrap_visitor_t<DFSVisitor>;

I find this to be more readable.

You can also do C++17 style helpers if you allow template variables (C++14)

template <typename T>
constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes on all counts !
I avoided touching the documentation for now, as it's basically a design change and I wanted to keep the PR minimal to simplify review ! 🙏🏽 Thanks a lot Matt !

@andreacassioli

Copy link
Copy Markdown
Contributor

@Becheler it is a nice extension, and pretty neat.

I like that it is explicit. Few times I have been fooled thinking that the visitor was passed by reference!

@andreacassioli

Copy link
Copy Markdown
Contributor

btw @Becheler are you aware of #307 ?

@Becheler

Becheler commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

@andreacassioli I was not aware of #307, thanks! It shows the problem well: the docs of depth_first_visit say the visitor is taken by reference, the header takes it by copy.

BGL mixes conventions, so we need to pick one. There are 3 main designs for stateful visitors:

  1. Passing by reference. Used by a minority of algorithms, not the STL convention: the standard lets algorithms copy function objects freely. A reference in the signature only holds if no internal helper ever copies the visitor: if one does, state is silently lost. It also doesn't bind to temporaries like make_dfs_visitor(...).
  2. Passing by value, returning the visitor. This steals the return type, e.g. bellman_ford_shortest_paths already returns a bool. In the STL only std::for_each does this, because getting the visitor back is essentially its only purpose.
  3. Passing by value, allowing std::reference_wrapper. Default behavior is unchanged (non breaking, temporaries still work), sharing state is an explicit opt in at the call site (std::ref(vis)), and internal copies only copy the wrapper, so state can't be lost. It's the same idiom as std::thread, std::bind and std::make_tuple, and it avoids reference members in visitors, which tooling flags.

@Becheler
Becheler force-pushed the feature/dfs-visitor-std-ref branch from b4df0b2 to a35beb2 Compare October 8, 2026 09:54
@cppalliance-bot

cppalliance-bot commented Oct 8, 2026 •

Copy link
Copy Markdown

An automated preview of the documentation is available at https://614.graph.prtest3.cppalliance.org/libs/graph/doc/html/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-10-08 10:24:30 UTC

@Becheler
Becheler force-pushed the feature/dfs-visitor-std-ref branch 2 times, most recently from a35beb2 to f526341 Compare October 8, 2026 10:18
@Becheler Becheler added api algorithm type of issue related to algorithms visitor Type of issue related to visitors labels Oct 8, 2026
@Becheler
Becheler merged commit c9ce0a1 into boostorg:develop Oct 8, 2026
60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

algorithm type of issue related to algorithms api visitor Type of issue related to visitors

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants