I've defined a custom view:
namespace detail {
constexpr auto coord = [](auto&& p) -> decltype(auto) {
return p.coord();
};
struct CoordsView
{
constexpr CoordsView() = default;
template<std::ranges::range R>
friend constexpr auto operator|(R&& r, CoordsView)
{
return std::forward<R>(r) | std::views::transform(coord);
}
};
} // namespace detail
inline constexpr detail::CoordsView coords;
and I am using it in the following way:
struct Point
{
float x, y, z;
};
class Vertex
{
Point p;
public:
Vertex(float x, float y, float z) : p{x, y, z} {}
Point& coord() { return p; }
const Point& coord() const { return p; }
};
int main()
{
std::vector<Vertex> v;
v.push_back(Vertex(-0.5, -0.5, 0.5));
v.push_back(Vertex(0.5, -0.5, 0.5));
v.push_back(Vertex(-0.5, 0.5, 0.5));
v.push_back(Vertex(0.5, 0.5, 0.5));
for (auto& p : v | coords) {
p.x += 1;
}
for (const auto& p : v | coords) {
std::cout << p.x << " " << p.y << " " << p.z << std::endl;
}
return 0;
}
Everything works fine on all the three major compilers when using old header and source files, or in a single main.cpp file: https://godbolt.org/z/M7a4a5YrP
The problems start when using C++20 modules on Clang 18: https://godbolt.org/z/T1cbzY1Mn
views.ixx:
module;
#include <ranges>
export module myviews;
namespace detail {
constexpr auto coord = [](auto&& p) -> decltype(auto) {
return p.coord();
};
struct CoordsView
{
constexpr CoordsView() = default;
template<std::ranges::range R>
friend constexpr auto operator|(R&& r, CoordsView)
{
return std::forward<R>(r) | std::views::transform(coord);
}
};
} // namespace detail
export inline constexpr detail::CoordsView coords;
main.cpp:
#include <iostream>
#include <vector>
import myviews;
struct Point
{
float x, y, z;
};
class Vertex
{
Point p;
public:
Vertex(float x, float y, float z) : p{x, y, z} {}
Point& coord() { return p; }
const Point& coord() const { return p; }
};
int main()
{
std::vector<Vertex> v;
v.push_back(Vertex(-0.5, -0.5, 0.5));
v.push_back(Vertex(0.5, -0.5, 0.5));
v.push_back(Vertex(-0.5, 0.5, 0.5));
v.push_back(Vertex(0.5, 0.5, 0.5));
for (auto& p : v | coords) {
p.x += 1;
}
for (const auto& p : v | coords) {
std::cout << p.x << " " << p.y << " " << p.z << std::endl;
}
return 0;
}
I am getting the error:
views.ixx:20:35: error: invalid operands to binary expression ('std::vector<Vertex>' and '_Partial<_Transform, decay_t<const (lambda at /app/views.ixx:9:24) &>>' (aka '_Partial<std::ranges::views::_Transform, detail::(lambda at /app/views.ixx:9:24)>'))
20 | return std::forward<R>(r) | std::views::transform(coord);
| ~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
which I don't understand.
On MSVC the code works, although I need to include #include <ranges> also on the main file (should this really be necessary?).
What am I doing wrong? Is there a better way to implement this kind of views?
I got it to work by making coord an inline variable (reasoning was: it's at namespace scope, so it's static).
I'm not sure if it's well defined behaviour, though.
Live on Compiler Explorer
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With