C++ 中文周刊 第137期

2024-07-30 14:41:37 浏览数 (2)

RSS https://github.com/wanghenshui/cppweeklynews/releases.atom

欢迎投稿,推荐或自荐文章/软件/资源等

请提交 issue[2]

最近在找工作准备面试题,更新可能有些拖沓,见谅

以后可能要改变一下内容

一周发文章总结,一周发视频总结,这样两种内容交叉一下

本期发文章

本期由 不语 cqs 赞助

资讯

CLion的新beta版本:CLion Nova https://blog.jetbrains.com/clion/2023/11/clion-nova/

mick投稿,说挺好用

标准委员会动态/ide/编译器信息放在这里 十月邮件 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/#mailing2023-10

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-11-08 第227期

文章

  • • The power of ref-qualifiers[3]

其实就是限定接口

代码语言:javascript复制
class Keeper {
  std::vector<int> data{2, 3, 4};

public:
  ~Keeper() { std::cout << "dtorn"; }

  auto& items() & { return data; }

  //For rvalues, by value with move
  auto items() && { return std::move(data); }
};

之前也介绍过这种限定接口不容易用错

  • • Clang/GCC Target Attributes [4]

-march可以根据架构更精细配置

比如-march=znver4

那怎么保证代码移植兼容性呢?不同的架构有不同配置?宏?太不优雅

可以使用arrtribute(target) 分别实现,也可以用target_clones多个架构共用一个版本

代码语言:javascript复制
#include <array>
#include <cstdio>

using Vector = std::array<float, 2>;
using Matrix = std::array<float, 4>;

__attribute__((target_clones("default","arch=core2","arch=znver2")))
Vector multiply(const Matrix& m, const Vector& v) {
    Vector r;
    r[0] = v[0] * m[0]   v[1] * m[2];
    r[1] = v[0] * m[1]   v[1] * m[3];
    return r;
}
代码语言:javascript复制
#include <array>
#include <cstdio>

using Vector = std::array<float, 2>;
using Matrix = std::array<float, 4>;

Vector multiply(const Matrix& m, const Vector& v);

int main() {
    Matrix m{1,2,3,4};
    Vector v{1,2};
    Vector r = multiply(m,v);
    printf( "%f %fn", r[0], r[1] );
}

目前clang会编译报错。应该是bug,我看maskray在研究 https://github.com/llvm/llvm-project/issues/61219

  • • xmake 2.8.5[5]

测试增强。挺好的

  • • Link-time optimisation (LTO) [6]

介绍LTO的

  • • 协程利器,std::generator for C 23用法入门与实现剖析(一)[7]

介绍generator实现的,可以体验一下这个 https://godbolt.org/z/5hcaPcfvP

  • • 论文阅读:Mimalloc Free List Sharding in Action[8]

介绍mimalloc实现的

  • •printing aggrrgates [9]

一个简单的结构化打印例子,使用boost pfr/magic_get

代码语言:javascript复制

template <class T, typename = void>
consteval std::string_view type_name() {
    std::string_view s = std::source_location::current().function_name();
    auto i0 = s.find('T')   4;
    auto i1 = s.find(';');
    return s.substr(i0, i1 - i0);
}

template <class T>
    requires(std::is_class_v<T> && std::is_aggregate_v<T>)
std::ostream& operator<<(std::ostream& os, const T& x) {
    os << type_name<T>();
    os << '(';
    boost::pfr::for_each_field(x, [&](const auto& field_val, auto field_idx) {
        if (field_idx > 0) {
            os << ", ";
        }
        os << boost::pfr::get_name<field_idx, T>() << '=' << field_val;
    });
    os << ')';
    return os;
}

struct Point {
    int x, y, z;
};

Point p(1, 2, 3);
std::cout << p << 'n';
// Point(x=1, y=2, z=3)

struct Line {
    Point a, b;
};
Line l(Point(1, 2, 3), Point(4, 5, 6));
std::cout << l << 'n';
// Line(a=Point(x=1, y=2, z=3), b=Point(x=4, y=5, z=6))
  • • Compile time string literals processing, but why?[10]

介绍一些编译期字符串组件,source_location magic_enum之类的 不知道magic_num的看这里 https://github.com/Neargye/magic_enum

  • • Generic Topological Sort[11]

拓扑排序。感觉这个可以出算法题了

首先要明白什么是拓扑排序,可能直观印象就是leetcode那些题。这里的例子可能不太一样

那么std::sort能不能用于拓扑排序?

比如

代码语言:javascript复制
/// `edge(u, v)` returns true if and only if there is an edge from `u` to `v`
template< class RandomIt, class F >
void topological_sort( RandomIt first, RandomIt last, F edge ) {
    std::sort(first, last, edge);
}

我们考虑一个场景

代码语言:javascript复制
A -----> C -----> D
|
-------> B

一个简单的关系,ACDB ABCD ACBD 也就这三种走向,怎么写条件呢

代码语言:javascript复制
#include <algorithm>
#include <iostream>
#include <string>

int main() {
    std::string vertices{'A', 'B', 'C', 'D'};
    auto edge = [](char u, char v) {
        return (u == 'A' && v == 'B')
            || (u == 'A' && v == 'C')
            || (u == 'C' && v == 'D')
            ;
    };

    do {
        auto sorted = vertices;
        std::sort(sorted.begin(), sorted.end(), edge);
        std::cout << vertices << " --> " << sorted << 'n';
    } while (std::next_permutation(vertices.begin(), vertices.end()));
}

讲道理,任何一种ABCD排列最终都会转化成上面列的三种,对不对?

但执行上面的例子你会发现有几个场景并不能转化成上面的三种

代码语言:javascript复制
DABC --> CDAB
DACB --> CDAB
DBAC --> CDAB

为什么?sort排序的条件显然不能这么写,并没有一个有序的传递性

拓扑排序没有排序的传递性,所以被迫需要整体的视角,

而排序is sorted只要保证 左右和自己就能把这个传递性推广开

那只好遍历了

代码语言:javascript复制
/// topological sort, the brute force
template <std::random_access_iterator I, class S, class F>
void topological_sort(I first, S last, F edge) {
    for (; first != last;   first) {
        // check if *first is a source.
        for (auto other = std::next(first); other != last;   other) {
            if (edge(*other, *first)) {
                // *first is not a source; *other may be
                std::swap(*other, *first);
                // IMPORTANT! have to do the full search again for the new *first
                other = first;
            }
        }
    }
}

显然这个复杂度不能接受,哈哈接下来可能就要重新发行khan算法和DFS算法了。这里把khan代码列出来

代码语言:javascript复制
/// topological sort, Kahn's algorithm
template <std::random_access_iterator I, class S, class F>
void topological_sort(I first, S last, F edge) {
    std::size_t n = std::ranges::distance(first, last);
    std::vector<std::size_t> in_degree(n);

    for (std::size_t i = 0; i < n;   i) {
        for (std::size_t j = 0; j < n;   j) {
            in_degree[i]  = bool(edge(first[j], first[i]));
        }
    }
    
    // [s_first, s_last) are the sources of the sub-graph [s_first, last)
    auto s_first = first;
    auto s_last = s_first;

    for (std::size_t i = 0; i < n;   i) {
        if (in_degree[i] == 0) {
            std::swap(first[i], *s_last);
            std::swap(in_degree[i], in_degree[s_last - first]);
              s_last;
        }
    }

    for (; s_first != s_last;   s_first) {
        for (auto t_it = s_last; t_it != last;   t_it) {
            if (edge(*s_first, *t_it) && --in_degree[t_it - first] == 0) {
                std::swap(*t_it, *s_last);
                std::swap(in_degree[t_it - first], in_degree[s_last - first]);
                  s_last;
            }
        }
    }
}

说不定将来std::graph能进标准库,这些内建支持

如果你像我一样不知道上面说的是啥, 说明你该刷算法题了

比如看看这个 https://algo.itcharge.cn/08.Graph/02.Graph-Traversal/05.Graph-Topological-Sorting

  • • The dangers of releasing the last strong reference from within its own callback[12]

考虑类中注册回调函数,析构可能就要把注册的回调函数去掉,有一种可能,回调函数在调用的途中,类自己已经析构了,回调函数也unregister了

如何解决这种问题?

直观的解决办法就是析构的时候等待调用结束,其实就是引用计数,调用的时候 1 调用结束-1

这其实引入了shared_ptr循环依赖的的问题,导致永远不能析构,死锁

如何解决这种死锁问题?考虑引入weakptr?

考虑析构拆出来,弄个final_release静态方法? https://learn.microsoft.com/zh-cn/windows/uwp/cpp-and-winrt-apis/details-about-destructors#deferred-destruction

或者把callback和类自身生命周期绑定在一起,结合weakptr 这种设计比较常见

代码语言:javascript复制
class MyObject {
    MyObject()
    {
        m_widget.prime();
        // Do this last: Don't register for callbacks  
        // until all members are ready.                
        m_callback.register(&MyObject::callback, this);
    }

    ~MyObject()
    {
        // Do this first: Ensure all outstanding
        // callbacks have completed.            
        m_callback.reset();                     
        m_widget.disable();
    }
};
void MyObject::Callback(CallbackData* data, void* context)
{
    auto self = reinterpret_cast<MyObject*>(context);

    [](auto weak, auto important) -> fire_and_forget {
        co_await resume_background();
        if (auto strong = weak.get()) {
            ⟦ do stuff with "strong" ⟧
        }
    }(get_weak(), data->important);
}
How to use std::span from C 20 [13]

span相比string_view有个优点,就是可以改动

Why does unsafe multithreaded use of an std::unordered_map crash more often than unsafe multithreaded use of a std::map?[14]

客户的代码多线程裸奔,使用map来读写,崩溃次数还算少,就没怎么关注,某一天改成unordered_map 崩溃次数多了。只能加锁了

为什么?unordered_map冲突变更导致迭代器失效的概率比map这种有序的低

我觉得这种知识还是不要知道的好

开源项目需要人手

  • • asteria[15] 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线

最近进展,优化JIT/基础组件调优,对于做语言的还是能见识到一点东西的

  • • Unilang[16] deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • • gcc-mcf[17] 懂的都懂
  • • https://github.com/hggq/paozhu 一个网络框架
  • • https://gitee.com/okstar-org/ok-edu-desktop 一个IM通信软件,做IM的可以关注,现在正在做全面整合阶段,开始组建商业团队阶段,年底开始融资,你参加了就快发财了,会的快来
  • • https://github.com/volt-software/Ichor/tree/v0.3.0 一个c 20 服务框架

视频

  • • [MUC ] Elizaveta Shulankina - Analyzing C applications for performance optimization[18]

intel的,主要还是并行化那几样,SOA改成AOS,使用intel的编译器以及SDLT[19]之类的

  • • Tristan Brindle - Iteration Revisited[20]

还是介绍flux的。部分场景比range快(为什么?)

  • • cppclubuk 166唠嗑讨论汇总 166. WG21 September 2023 mailing, Contracts, Modules, Cppfront

https://cppclub.uk/meetings/2023/166/ 会议记录在这里

视频在这里 https://www.youtube.com/watch?v=6L3Vk6Zax_w

我发了一份b站 BV17g4y1Q7XD

互动环节

工作环境还是比较差的,大家尽量不要离职。。

最近面试又遇到个奇葩的题

vector的push_back可以理解成均摊O1 那么考虑扩容的影响,假设扩容按照2备来算,复杂度应该是多少?

我回答N logN他说那是最大的,不对 我说(LogN)^2他也说不对。我不会了

引用链接

[1] 手机qq点击进入: https://qm.qq.com/q/6NGizNPyG4 [2] 提交 issue: https://github.com/wanghenshui/cppweeklynews/issues [3] The power of ref-qualifiers: https://andreasfertig.blog/2022/07/the-power-of-ref-qualifiers/ [4] Clang/GCC Target Attributes : https://lucisqr.substack.com/p/clanggcc-target-attributes [5] xmake 2.8.5: https://github.com/xmake-io/xmake/wiki/Xmake-v2.8.5-released,-Support-for-link-sorting-and-unit-testing [6] Link-time optimisation (LTO) : https://convolv.es/guides/lto/ [7] 协程利器,std::generator for C 23用法入门与实现剖析(一): https://zhuanlan.zhihu.com/p/666365620 [8] 论文阅读:Mimalloc Free List Sharding in Action: https://zhuanlan.zhihu.com/p/665602526 [9] : https://biowpn.github.io/bioweapon/2023/11/11/printing-aggregates.html [10] Compile time string literals processing, but why?: https://a4z.gitlab.io/blog/2023/11/04/Compiletime-string-literals-processing.html [11] Generic Topological Sort: https://biowpn.github.io/bioweapon/2023/11/03/topological-sort.html [12] The dangers of releasing the last strong reference from within its own callback: https://devblogs.microsoft.com/oldnewthing/20230927-00/?p=108831 [13] How to use std::span from C 20 : https://www.cppstories.com/2023/span-cpp20/ [14] Why does unsafe multithreaded use of an std::unordered_map crash more often than unsafe multithreaded use of a std::map?: https://devblogs.microsoft.com/oldnewthing/20231103-00/?p=108966 [15] asteria: https://github.com/lhmouse/asteria [16] Unilang: https://github.com/linuxdeepin/unilang [17] gcc-mcf: https://gcc-mcf.lhmouse.com/ [18] [MUC ] Elizaveta Shulankina - Analyzing C applications for performance optimization: https://www.youtube.com/watch?v=M1D8iez1Ph0 [19] SDLT: https://www.intel.com/content/www/us/en/docs/dpcpp-cpp-compiler/developer-guide-reference/2023-2/simd-data-layout-templates.html [20] Tristan Brindle - Iteration Revisited: https://www.youtube.com/watch?v=bMitr8ReVeg

0 人点赞