3

Problem

I want to create a range from two iterators returned by a function.

I used the answer to a related question to create a new subrange using range-v3:

auto [it1, it2] = out_edges(u, _graph);
return  ranges::subrange(it1, it2) | ranges::views::transform([](auto it){return it->target();});

But my compiler tells me: error: no viable constructor or deduction guide for deduction of template arguments of 'subrange'

I don't understand the error and do not know how I am supposed to define the required deduction guide.

Iterator type

The iterator type is the following:

      class out_edge_iterator
        : public boost::iterator_adaptor<out_edge_iterator,
                                         vertex_descriptor const *,
                                         edge_descriptor,
                                         forward_traversal_tag,
                                         edge_descriptor>
      {
        vertex_descriptor const *last;
        vertex_descriptor source;

      public:
        out_edge_iterator(Vertex const *first, Vertex const *last, Vertex source)
          : out_edge_iterator::iterator_adaptor_(first), last(last),
            source(source)
        {
          BOOST_ASSERT(source != null_vertex());
          post_increment();
        }

      private:
        edge_descriptor dereference() const
        {
          return edge_descriptor(source, *this->base_reference());
        }

        void post_increment()
        {
          while (this->base_reference() != last
                 && *this->base_reference() == null_vertex())
          {
            this->base_reference()++;
          }
        }

        void increment()
        {
          this->base_reference()++;
          post_increment();
        }

        friend class boost::iterator_core_access;
      };

Config:

  • OS Macos Ventura M1.
  • Apple clang version 14.0.3 (clang-1403.0.22.14.1)
  • Target: arm64-apple-darwin22.5.0
  • Thread model: posix
  • range-v3 version 0.12.0
  • C++20
WaterFox
  • 850
  • 6
  • 18
  • 1
    which dialect of C++ are you compiling to? the linked answer says that `subrange` requires C++17. – jwezorek Jun 16 '23 at 11:43
  • @jwezorek thank you, I forgot to add the standard: C++20 (not all C++20 features are not supported by my current version of apple-clang though). – WaterFox Jun 16 '23 at 11:46

1 Answers1

3

I have three version, just using Boost, using std::ranges and using Range-v3.

To cut the corners, I always write boost::make_iterator_range(out_edges(e, g)) which works fine and can treat the pair as a range:

Just Boost

Live On Coliru

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

using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>;

int main() {
    Graph g;

    add_edge(0, 1, g);
    add_edge(1, 2, g);
    add_edge(1, 3, g);
    add_edge(1, 4, g);
    add_edge(2, 5, g);
    add_edge(3, 6, g);

    for (auto v : boost::make_iterator_range(vertices(g))) {
        std::cout << v << " ->";
        for (auto e : make_iterator_range(out_edges(v, g))) {
            std::cout << " " << e;
        }
        std::cout << "\n";
    }
}

Sometimes you might need to

#include <boost/range/iterator_range.hpp>`

std::ranges

In C++20, you can use std::ranges:

Live On Coliru

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

using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>;

int main() {
    Graph g;

    add_edge(0, 1, g);
    add_edge(1, 2, g);
    add_edge(1, 3, g);
    add_edge(1, 4, g);
    add_edge(2, 5, g);
    add_edge(3, 6, g);

    auto [vb,ve] = vertices(g);
    for (auto v : std::ranges::subrange(vb, ve)) {
        std::cout << v << " ->";
        auto [eb, ee] = out_edges(v, g);
        for (auto e : std::ranges::subrange(eb, ee)) {
            std::cout << " " << e;
        }
        std::cout << "\n";
    }
}

Range-V3

Live On Compiler Explorer

#include <boost/graph/adjacency_list.hpp>
#include <iostream>
#include <range/v3/all.hpp>
namespace r = ::ranges::v3;

using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS>;

int main() {
    Graph g;

    add_edge(0, 1, g);
    add_edge(1, 2, g);
    add_edge(1, 3, g);
    add_edge(1, 4, g);
    add_edge(2, 5, g);
    add_edge(3, 6, g);

    auto [vb,ve] = vertices(g);
    for (auto v : r::subrange(vb, ve)) {
        std::cout << v << " ->";
        auto [eb, ee] = out_edges(v, g);
        for (auto e : r::subrange(eb, ee)) {
            std::cout << " " << e;
        }
        std::cout << "\n";
    }
}

All three code samples have identical output:

0 -> (0,1)
1 -> (1,2) (1,3) (1,4)
2 -> (2,5)
3 -> (3,6)
4 ->
5 ->
6 ->
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Thanks @sehe! I can't use std::ranges because apple-clang does not implement it yet :/ So you use boost to limit border effects? It indeed works when I do `return boost::make_iterator_range(out_edges(u, _graph)) | boost::adaptors::transformed([this](auto it){return target(it, _graph); } );` I just hope I will not run into compatibility problems between range-v3 and boost ranges :) – WaterFox Jun 16 '23 at 12:39
  • But for some reason the ranges-v3 approach still does not compile. – WaterFox Jun 16 '23 at 12:48
  • 1
    I use `boost::make_iterator_range` because it works well in all c++11 compilers (c++03 even) and because it avoid the need to even see the iterators. So it's a convenience/minimal dependencies thing. I don't know what you mean by "border issues" but perhaps you mean compatibility. Both std::ranges and range-v3 have fairly clear range concepts and Boost Range (v2) matches them, so no I don't see any issues. – sehe Jun 16 '23 at 12:54
  • 1
    If anything, I think it's pretty unfortunate that `pair` aren't easily used as ranges. Of course, it's only unfortunate due to legacy interfaces returning iterator pairs. – sehe Jun 16 '23 at 12:56
  • 1
    By the way just noticing the transformation itself... Why not just [`adjacent_vertices(v, g)`](http://coliru.stacked-crooked.com/a/3d9696b235e75f32)? It's much clearer, simpler and possibly more efficient :) (You may want to check semantics in the presence of duplicate edges) – sehe Jun 16 '23 at 13:00
  • Indeed, it does not seem to have compatibility issues until now! Thanks for all the knowledge and tips! Regarding `adjacent_vertices`, I did not know yet of this function, but I suspect it makes the assumption I'm working with boost graphs classes, whereas I'm actually extending on a k-ary tree class developed by jeremy murphy ([PR never merged to boost](https://github.com/jeremy-murphy/graph/tree/k-ary-tree)). And it does not seem this class offers this function :) Also my trees have edges oriented from root to leaves, so I suspect `adjacent_vertices` would also iterate on the parent node. – WaterFox Jun 16 '23 at 13:12
  • 1
    Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254102/discussion-between-sehe-and-waterfox). – sehe Jun 16 '23 at 13:23