My general question is how do you make something like ranges::to<T>()
for classes for which ranges::to<T>()
does not work?
But specifically I am looking for a pipeable way to construct a boost geometry multi_linestring
from a range-v3 range view of boost geometry linestring
's. Somewhat surprisingly ranges::to
just works for constructing linestring's but not for constructing multi_linestring's, as below.
namespace r = ranges;
namespace rv = ranges::views;
namespace bg = boost::geometry;
using point = bg::model::point<double, 2, bg::cs::cartesian>;
using polyline = bg::model::linestring<point>;
using polylines = bg::model::multi_linestring<polyline>;
int main() {
std::vector<point> some_points = { {1,1},{2,2},{3,3},{4,4},{5,5} };
auto poly = some_points | r::to<polyline>(); // <- this works
std::vector<std::vector<point>> vec_of_vec_of_pts = {
{{1,1},{2,2},{3,3},{4,4},{5,5} },
{{6,6},{7,7},{8,8}},
{{9,9},{10,10},{11,11},{12,12}}
};
auto polys = vec_of_vec_of_pts | rv::transform(
[](const auto& v) { return v | r::to<polyline>(); }
) | r::to<polylines>(); // <- this does not compile.
return 0;
}
The particular error message from Visual Studio is
1>[...]: error C2678: binary '|': no operator found which takes a left-hand operand of type 'ranges::transform_view<ranges::ref_view<std::vector<std::vector<point,std::allocator<point>>,std::allocator<std::vector<point,std::allocator<point>>>>>,Arg>' (or there is no acceptable conversion)
1> with
1> [
1> Arg=main::<lambda_1>
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\include\cstddef(42,27): message : could be 'std::byte std::operator |(const std::byte,const std::byte) noexcept' [found using argument-dependent lookup]
1>C:\libraries\range-v3\include\range\v3\view\any_view.hpp(66,24): message : or 'ranges::category ranges::operator |(ranges::category,ranges::category) noexcept' [found using argument-dependent lookup]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\include\regex(1191,1): message : or 'std::_Node_flags std::operator |(std::_Node_flags,std::_Node_flags) noexcept' [found using argument-dependent lookup]
1>[...]: message : while trying to match the argument list '(ranges::transform_view<ranges::ref_view<std::vector<std::vector<point,std::allocator<point>>,std::allocator<std::vector<point,std::allocator<point>>>>>,Arg>, ranges::detail::to_container::closure<meta::id<polylines>,ranges::detail::to_container::fn<meta::id<polylines>>>)'
1> with
1> [
1> Arg=main::<lambda_1>
1> ]
My current work-around is a function template that will do the conversion:
template<typename Rng>
polylines to_polylines(Rng lines) {
polylines polys;
polys.resize(r::distance(lines));
for (auto&& [i, line] : rv::enumerate(lines)) {
polys[i] = line;
}
return polys;
}
which I can use like
auto polys = to_polylines(
vec_of_vec_of_pts |
rv::transform(
[](const auto& v) { return v | r::to<polyline>(); }
)
);
but it can't be a target of a range-v3 pipeline. How can I implement something like the above that can be piped to?
CodePudding user response:
The pipeline is just overloaded operator |
, you can overload it yourself.
struct to_polyline_tag{} to_polylines;
template<typename Range>
polylines opeator | (Range&& lines, to_polyline_tag) {
// the body of your to_polylines()
polylines polys;
polys.resize(r::distance(lines));
for (auto&& [i, line] : rv::enumerate(lines)) {
polys[i] = line;
}
return polys;
}
// use: some_range_like | to_polylines