gpt4 book ai didi

c++ - 在结构上应用标准算法?

转载 作者:行者123 更新时间:2023-12-04 07:49:59 28 4
gpt4 key购买 nike

我们有 Boost.PFR我们有 tuple iterator .如果我们将两者结合起来,我们可能有一种在结构上应用 std 算法的方法。是否已经存在解决方案?我正在寻找的是:

S a, b;
auto const ra(to_range(a)), rb(to_range(b));
std::transform(ra.begin(), ra.end(), rb.begin(), [](auto&& a)noexcept{return a;});
这将允许我们使用较新的 <execution>不按顺序或并行处理结构的功能。

最佳答案

所以为了说明我试图在 comments to the other answer 中提出的观点,让我们写一些像你的转换:
直接实现
我跳过了迭代器和标准库的概念,因为它被整个“迭代器值类型必须固定”和其他负担所拖累。
相反,让我们“从功能上”来做。

#include <boost/pfr.hpp>
namespace pfr = boost::pfr;

template <typename Op, typename... T>
void transform(Op f, T&&... operands) {
auto apply = [&]<int N>() {
f(pfr::get<N>(std::forward<T>(operands))...);
return 1;
};

constexpr auto size = std::min({pfr::tuple_size<std::decay_t<T>>::value...});
// optionally assert that sizes match:
//static_assert(size == std::max({pfr::tuple_size<std::decay_t<T>>::value...}));

[=]<auto... N>(std::index_sequence<N...>) {
return (apply.template operator()<N>() + ...);
}
(std::make_index_sequence<size>{});
}
我已经通过不固定 arity 进行了一些概括。它现在更像是一个 n-ary zip 或访问者。要获得您想要的转换,您可以向它传递一个操作,例如
auto binary = [](auto const& a, auto& b) {
b = a;
};
让我们演示一下,突出显示混合类型成员、非对称类型以及混合长度结构:
struct S1 { int a; double b; long c; float d; };
struct S2 { double a; double b; double c; double d; };
struct S3 { double a; double b; };
测试用例:
int main() {

auto n_ary = [](auto&... fields) {
puts(__PRETTY_FUNCTION__);
return (... = fields);
};

S1 a;
S2 b;
S3 c;

// all directions
transform(binary, a, b);
transform(binary, b, a);

// mixed sizes
transform(binary, b, c);
transform(binary, c, a);

// why settle for binary?
transform(n_ary, a, b);
transform(n_ary, a, b, c);
transform(n_ary, c, b, a);
}
看看 Live On Compiler Explorer
已经,拆机提示 一切正在内联和优化。字面上地。只有 puts电话仍然存在:
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call puts
mov edi, OFFSET FLAT:.LC1
call puts
mov edi, OFFSET FLAT:.LC2
call puts
...
...
xor eax, eax
add rsp, 8
ret
给出输出
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = int; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = long int; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = float; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = int]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = long int]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = float]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = double]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = int]
main()::<lambda(const auto:12&, auto:13&)> [with auto:12 = double; auto:13 = double]
main()::<lambda(auto:14& ...)> [with auto:14 = {int, double}]
main()::<lambda(auto:14& ...)> [with auto:14 = {double, double}]
main()::<lambda(auto:14& ...)> [with auto:14 = {long int, double}]
main()::<lambda(auto:14& ...)> [with auto:14 = {float, double}]
main()::<lambda(auto:14& ...)> [with auto:14 = {int, double, double}]
main()::<lambda(auto:14& ...)> [with auto:14 = {double, double, double}]
main()::<lambda(auto:14& ...)> [with auto:14 = {double, double, int}]
main()::<lambda(auto:14& ...)> [with auto:14 = {double, double, double}]
工作证明
让我们做一些我们可以检查的“有用”计算。此外,重命名 transform函数到 nway_visit只是反射(reflect)它更通用的方向:
auto binary = [](auto& a, auto& b) { return a *= b; };
auto n_ary = [](auto&... fields) { return (... *= fields); };
所以这两个操作都进行右 fold 乘法赋值。给定一些明确选择的初始值设定项
S1 a {1,2,3,4};
S2 b {2,3,4,5};
S3 c {3,4};
我们希望能够看到通过数据结构的数据流。所以,让我们有选择地做一些调试跟踪:
#define DEMO(expr)                                                             \
void(expr); \
if constexpr (output_enabled) { \
std::cout << "After " << std::left << std::setw(26) << #expr; \
std::cout << " a:" << pfr::io(a) << "\tb:" << pfr::io(b) \
<< "\tc:" << pfr::io(c) << "\n"; \
}

DEMO("initialization");

// all directions
DEMO(nway_visit(binary, a, b));
DEMO(nway_visit(binary, b, a));

// mixed sizes
DEMO(nway_visit(binary, b, c));
DEMO(nway_visit(binary, c, a));

// why settle for binary?
DEMO(nway_visit(n_ary, a, b));
DEMO(nway_visit(n_ary, a, b, c));
DEMO(nway_visit(n_ary, c, b, a));

return long(c.a + c.b) % 37; // prevent whole program optimization...
作为顶级樱桃让我们成为 绝对有把握 (禁用输出)编译器无法优化整个程序,因为没有可观察到的效果:
return long(c.a + c.b) % 37; // prevent whole program optimization...
演示是 Live On Compiler Explorer 启用输出,一次禁用输出显示反汇编:
main:
mov eax, 13
ret

WOW


圣烟。这就是优化。整个程序是静态评估的,只返回退出代码 13。让我们看看这是否是正确的退出代码:
输出启用:
After "initialization"           a:{1, 2, 3, 4} b:{2, 3, 4, 5}  c:{3, 4}
After nway_visit(binary, a, b) a:{2, 6, 12, 20} b:{2, 3, 4, 5} c:{3, 4}
After nway_visit(binary, b, a) a:{2, 6, 12, 20} b:{4, 18, 48, 100} c:{3, 4}
After nway_visit(binary, b, c) a:{2, 6, 12, 20} b:{12, 72, 48, 100} c:{3, 4}
After nway_visit(binary, c, a) a:{2, 6, 12, 20} b:{12, 72, 48, 100} c:{6, 24}
After nway_visit(n_ary, a, b) a:{24, 432, 576, 2000} b:{12, 72, 48, 100} c:{6, 24}
After nway_visit(n_ary, a, b, c) a:{1728, 746496, 576, 2000} b:{12, 72, 48, 100} c:{6, 24}
After nway_visit(n_ary, c, b, a) a:{1728, 746496, 576, 2000} b:{12, 72, 48, 100} c:{124416, 1289945088}
所以,返回值应该是 (124416 + 1289945088) modulo 37 ,袖珍计算器确认为 13。
从这里:并行任务等。
您最初的动机包括免费从标准库中获取并行执行选项。如您所知 I'm skeptical的用处。
但是,几乎没有阻止您从算法中获得这种行为:
boost::asio::thread_pool ctx; // or, e.g. system_executor

auto run_task = [&](auto&... fields) {
boost::asio::post(ctx, [=] { long_running_task(fields...); });
};
希望这是很好的灵感。谢谢你让我看看 PFR。它很甜。

关于c++ - 在结构上应用标准算法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67033605/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com