C++17 std::transform_reduce的替代实现

huangapple go评论70阅读模式
英文:

C++17 alternative implementation for std::transform_reduce

问题

我正在处理一个项目,该项目使用C++20标准开发,但我需要在一个旧的嵌入式Linux设备上构建它,该设备支持的最高标准是C++17(gcc 8.3.0)。在分析代码基础时,我意识到导致代码无法编译的唯一C++20功能要求是缺少std::transform_reduce,并出现以下错误:

error: ‘transform_reduce’ is not a member of ‘std’

引用的代码部分如下:

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(); });

但是,当我查看C++17的标准文档时,似乎已经实现了大部分std::transform_reduce的签名。该项目是基于CMake的项目,并包含以下设置,以使用C++17标准:

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

我想知道是否我做错了什么,或者是否有可能在C++17中提供了缺失功能的替代实现,或者如何在不更改程序高级行为的情况下实现相同的逻辑。

英文:

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.

答案1

得分: 4

以下是翻译的内容:

一个不牺牲表达能力的解决方案涉及将您的 transform_reduce 编写为一个简单的 std::accumulate。为此,您需要一个生成 map_reduce 运算符的实用程序:

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)));
    };
}

然后,您的调用可以在 C++17 中编写如下:

std::accumulate(
    localAddresses.begin(), localAddresses.end(), std::string(""),
    map_reduce(
        [](InetAddress addr) { return addr.str(); },     // 转换器    
        [](const std::string& a, const std::string& b) { // 累加器
            return a + " | " + b;
        });

演示(使用数字转换为字符串而不是地址)。

缩减(std::accumulate / std::reduce)几乎可以做任何事情,只要您弄清楚如何表达累加器。要了解兔子洞有多深,可以查看此演示


一个更普通的解决方案是“借用” transform_reduce 的实现(例如,从这里获取),并且仅在以下情况下将其提供为头文件:

#if __cplusplus < 202002L // 使用早于 C++20 的标准
  // 实现放在这里
#endif

当然,在跨平台项目中,您需要考虑语言标准和编译器的组合,因为某些库功能是按照非标准顺序实现的。

英文:

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 &lt;class M, class R&gt;
auto map_reduce(M&amp;&amp; map, R&amp;&amp; reduce) {
    return [m = std::forward&lt;M&gt;(map), 
            r = std::forward&lt;R&gt;(reduce)](auto&amp; acc, auto&amp;&amp; val) mutable {
        return r(acc, m(std::forward&lt;decltype(val)&gt;(val)));
    };
}

your call can then be is written in C++17 as:

std::accumulate(
    localAddresses.begin(), localAddresses.end(), std::string(&quot;&quot;),
    map_reduce(
        [](InetAddress addr) { return addr.str(); },     // transformer    
        [](const std::string&amp; a, const std::string&amp; b) { // accumulator
            return a + &quot; | &quot; + b;
        });

<kbd>Demo</kbd> (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 &lt; 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.

huangapple
  • 本文由 发表于 2023年6月30日 04:02:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76584283.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定