Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/163.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在结构上应用std算法?_C++_Multithreading_Struct_Boost_Tuples - Fatal编程技术网

C++ 在结构上应用std算法?

C++ 在结构上应用std算法?,c++,multithreading,struct,boost,tuples,C++,Multithreading,Struct,Boost,Tuples,我们有,我们有。如果我们将两者结合起来,我们可能有一种方法可以在结构上应用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;}); 这将允许我们使用较新的功能来处理无序结构或并行结构。您已经可以使用pfr::struct

我们有,我们有。如果我们将两者结合起来,我们可能有一种方法可以在结构上应用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;});

这将允许我们使用较新的
功能来处理无序结构或并行结构。

您已经可以使用
pfr::structure\u tie
拥有现有功能,这将产生一个引用元组

这将允许我们使用较新的特性来处理无序或并行的结构

只有当每个元素的处理具有相当大的执行成本时,这才有意义。在这种情况下,你可能已经可以定制了

boost::pfr::for_each_field(var, [](auto& field) { field += 1; });

调用

为了说明我在中尝试提出的观点,让我们编写一些类似于您的转换的内容:

直接实施 我跳过了迭代器和标准库的概念,因为它充满了整个“迭代器值类型必须固定”和其他负担

相反,让我们“按功能”来做

让我们演示一下,突出显示混合类型成员、非对称类型以及混合长度结构:

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);
}
查看它

分解已经表明,一切都在内联和优化。字面上只剩下
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}]
所以这两个操作都是右折叠乘法赋值。给出了一些明确选择的初始值设定项

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...
演示在启用输出的情况下,在禁用输出的情况下显示拆解:

main:
        mov     eax, 13
        ret
哇! 神圣的烟。这就是优化。整个程序是静态计算的,只返回退出代码13。让我们看看这是否是正确的退出代码:

已启用输出: 因此,返回值应该是,袖珍计算器确认为13

从这里开始:并行任务等。 您最初的动机包括免费从标准库获得并行执行选项。如你所知,这很有用

但是,几乎没有什么可以阻止您从该算法中获得这种行为:

boost::asio::thread_pool ctx; // or, e.g. system_executor

auto run_task = [&](auto&... fields) {
    boost::asio::post(ctx, [=] { long_running_task(fields...); });
};

希望这是一个很好的启示。谢谢你让我看PFR。这很好。

看起来像是用PFR函数(在该元组迭代器中)简单地替换标准元组函数就可以了。但是请注意,
a
将是一个
std::variant
。是的,我希望有人能做出更实质性的东西:)不错,但元组迭代器只是一个玩具实现。我把一些东西放在一起,它为我的目的工作。看起来它假设所有成员都是(可转换为)第一个元素的类型?你没有提到这样一个大大简化的要求。也许我误读了不,你没有,我只是得出了和你一样的结论,任何选择都太慢了。一种变体会让事情变得缓慢。如果你有一个很好的简化假设的解决方案,我很有兴趣看到它。“一个变体会把事情拖慢到爬行。”-需要证据。我说:当然不是。给定静态类型,编译器可以100%内联和解约,因为它分配内存,这对性能是致命的。这里有一个黑客版本,只是为了表明
std::variant
不需要产生分配开销:(不要使用此代码,对于泛型使用它是不一致的;特别是我“模拟的”
*=
将结果赋回最左边的操作数的行为)。优化程度较低,但看不到堆。(boost::variant也是如此,尽管我不得不对三元调用进行注释,因为
boost::apply\u visitor
不支持这一点)这确实是一个令人大开眼界的答案。就
std::execution::par
而言,我同意你的怀疑,但
std::execution::unseq
有时会产生令人瞠目结舌的SIMD代码。
#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...
main:
        mov     eax, 13
        ret
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}
boost::asio::thread_pool ctx; // or, e.g. system_executor

auto run_task = [&](auto&... fields) {
    boost::asio::post(ctx, [=] { long_running_task(fields...); });
};