Im working on a project which was developed using c++20 standard ,but I need to build it on an old embedded linux device which the highest available standard is c++17(gcc 8.3.0). analyzing the code base, I realized the only c++20 feature requirement that forbids the code to be compiled is the lake of the std::transform_reduce
that gets the following error:
error: ‘transform_reduce’ is not a member of ‘std’
the referred part of code is:
auto addresses_string = std::transform_reduce(
localAddresses.begin(), localAddresses.end(), std::string(""),
[](const std::string& a, const std::string& b) {
return a + " | " + b;
},
[](InetAddress addr) { return addr.str(); });
but as I check the std documentations it seems the c++17 has most of the std::transform_reduce signatures implemented. this project is a CMake based project and it contains the following variables set to use the c++17 standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
I was wondering if im doing something wrong or is there an possible alternative implementation available for the missing function in c++17 or how can I implement the same logic without changing the programs high level behavior.
A solution that doesn't sacrifice expressiveness involves writing your transform_reduce
as a simple std::accumulate
. To do this you need a utility to generate a map_reduce
operator:
template <class M, class R>
auto map_reduce(M&& map, R&& reduce) {
return [m = std::forward<M>(map),
r = std::forward<R>(reduce)](auto& acc, auto&& val) mutable {
return r(acc, m(std::forward<decltype(val)>(val)));
};
}
your call can then be is written in C++17 as:
std::accumulate(
localAddresses.begin(), localAddresses.end(), std::string(""),
map_reduce(
[](InetAddress addr) { return addr.str(); }, // transformer
[](const std::string& a, const std::string& b) { // accumulator
return a + " | " + b;
});
Demo (using numbers converted to strings instead of addresses).
Reduction (std::accumulate
/ std::reduce
) can pretty much do anything if you figure out how to express your accumulator. To get a sense of how deep the rabbit hole goes, check this presentation.
A more mundane solution would be to "borrow" the implementation of transform_reduce
(say from here) and make it available in a header only if
#if __cplusplus < 202002L // Using standard older than C++20
// implementation goes here
#endif
Of course in a cross platform project, you'd have to account for the combination of language standard and compiler since some library features are implemented out of standard order.