I have some nameless lambda function which I need to apply to many ranges and don't like to repeat it to avoid code duplication (it consists of 5+ lines of code).
What is simplest way to implement this?
I want to have something like in the code below with some reservation:
std
algorithms.for_each_range({r1,r2,r3},lambda)
, but I can't figure out how to get these { } working here. The std::initializer_list seems to be not my way, since it requires const data and I have to change the values in the ranges.#include <algorithm>
#include <iostream>
#include <numeric>
#include <ranges>
#include <vector>
template<typename Func, typename... Args>
void for_each_range(Func func, Args&&... ranges)
{
(func(ranges),...);
}
int main()
{
std::vector<float> weights = { 1.0f, 3.0f, 5.0f };
std::vector<float> masks = { 1.0f, 0.0f, 1.0f };
for_each_range([](auto& v) {
std::iota(v.begin(), v.end(), 0.0f);
}, weights, masks);
std::ranges::copy
(
weights,
std::ostream_iterator<float>{std::cout, ", "}
);
std::cout << '\n';
std::ranges::copy
(
masks,
std::ostream_iterator<float>{std::cout, ", "}
);
std::cout << '\n';
}
Demo.
I don't want to make it named lambda because I don't see any reason for code extraction (too many parameters will have to be passed and many of them will be updated) and it is hard to find good name for the fragment of code.
Is there a way to do this simpler, safer and more readable?
Or, maybe something already in place in std
?
As a second thought, maybe it worth to std::view::zip
vectors and unpack with std::get<n>
(still not sure how to get the number of ranges in for_each_range
in this case and get back from elements (tuples) to ranges. So, most likely this shouldn't work.
you can get really close with std::tie
#include <algorithm>
#include <iostream>
#include <numeric>
#include <ranges>
#include <vector>
template<typename Func, typename... Args>
void for_each_range(std::tuple<Args&...> ranges, Func func)
{
std::apply([&](auto&&...ranges)
{
(func(ranges),...);
}, ranges);
}
int main()
{
std::vector<float> weights = { 1.0f, 3.0f, 5.0f };
std::vector<float> masks = { 1.0f, 0.0f, 1.0f };
for_each_range(std::tie(weights, masks), [](auto& v) {
std::iota(v.begin(), v.end(), 0.0f);
});
std::ranges::copy
(
weights,
std::ostream_iterator<float>{std::cout, ", "}
);
std::cout << '\n';
std::ranges::copy
(
masks,
std::ostream_iterator<float>{std::cout, ", "}
);
std::cout << '\n';
}
unfortunately the compiler really doesn't like doing both template deduction and type deduction for the brace initializer, so i cannot get the compiler to produce code with just the braces.
you can add an overload for just 1 range when tie
isn't needed.
template<std::ranges::range Range, typename Func>
void for_each_range(Range&& range, Func func)
{
func(range);
}