0

Background

What I am trying to do is to implement some classes that represents geometry. Any instance of a geometry class has a method called vertices() that returns a non-owning view of vertices. A geometry class can be expressed in terms of multiple other geometry classes, so the geometry class' vertices()-method would ideally just do something like this (pseudocode):

vertices()
{
  return join(part1.vertices(), part2.vertices(), part3.vertices());
}

subject to not copying nor moving vertices.

In C++20 this is something that I believe can be done with ranges & views but I can't figure out how to do it.

My attempt

#include <iostream>
#include <ranges>
#include <vector>

struct Vertex { float x, y, z; };

struct GeometryA {
    auto vertices() {
        return std::ranges::ref_view(v);
    }
    std::vector<Vertex> v {{0.0f, 0.0f, 1.0f}};
};

struct GeometryB {
    auto vertices() {
        return std::ranges::ref_view(v);
    }
    std::vector<Vertex> v {{0.0f, 1.0f, 0.0f}};
};

struct GeometryC {
    auto vertices() {
        // OK: All elements of vector are of same type
        return std::vector{ a.vertices(), b.vertices(), std::ranges::ref_view(v)} | std::views::join;
    }
    GeometryA a;
    GeometryB b;
    std::vector<Vertex> v {{0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}};
};

struct GeometryD {
    auto vertices() {
        // Compilation fails: Elements of vector have different types
        return std::vector{ c.vertices(), std::ranges::ref_view(v)} | std::views::join;
    }
    GeometryC c;
    std::vector<Vertex> v {{1.0f, 0.0f, 1.0f}};
};

int main() {
    GeometryD d;

    for(Vertex const& vertex : d.vertices()) {
        // Should print {0,0,1} {0,1,0} {0,1,1} {1,0,0} {1,0,1}
        std::cout << "{" << vertex.x << "," << vertex.y << "," << vertex.z << "} ";
    }
    
    return 0;
}

Compilation fails in GeometryD::vertices since I am trying to deduce the template parameter T of the outmost vector from the elements at initialization (c.vertices() and std::ranges::ref_view(v)) but these do not have the same type hence T can't be deduced.

I am at a loss on how to approach this problem.

Question

Is it possible to use the standard ranges library to incrementally concatenate ranges?


I suppose I could gather all vertex-data directly or indirectly owned by a geometry class by using some recursive template-trickery and then just use std::views::join once, but before I get my hands dirty with that I'd like to get some input on my current attempt.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Joakim Thorén
  • 1,111
  • 10
  • 17

1 Answers1

2

You can do this using Eric Niebler's Range-v3 library.

Just concat the different range views with ranges::views::concat. E.g., for GeometryC:

return ranges::views::concat(a.vertices(), b.vertices(), v);

[Demo]

Much of the Range-v3's stuff is being gradually adopted by the standard Ranges library, although it seems this feature hasn't made it yet.

rturrado
  • 7,699
  • 6
  • 42
  • 62
  • 1
    Yes, it can be done Range-v3. [Here's a godbolt-example that shows this](https://godbolt.org/z/s97cn78s1). I do however ask for if this can be done with the standard ranges library presently, but I believe that this is not possible (yet). Your answer answer is the best one I am gonna get, accepting. – Joakim Thorén Mar 16 '22 at 12:49
  • 1
    Thanks! Notice the [Demo](https://godbolt.org/z/6dx4Krb3a) link in my answer takes you to a Godbolt exampel as well; although the `vertices` methods in that example return a `ranges::ref_view` instead of a `std::vector&`. `concat` is not yet part of the `std`. Here's a [plan](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2214r0.html) for incorporating some ranges features to the `std`. – rturrado Mar 16 '22 at 13:16