C++ 中文周刊 第133期

2024-07-30 14:27:33 浏览数 (3)

周刊项目地址[1]

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

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

请提交 issue[2]

感谢 YellyHornby fengyiee木马 不语 赞助

加了个互动栏目,欢迎各位投稿

奇妙的BUG

欢迎投稿

资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-10-04 第222期

cppcon 2023 已经发了四个视频了,BS又在讲安全,另外几个过于抽象,所以我本期还是主要更新cppcon 2022 的内容

九月邮件列表 https://isocpp.org//blog/2023/10/2023-09-mailing-available

libcurl 最近cve很严重,推荐升级8.4.0

文章

Did you know about C 26 proposal - inplace_vector? [3]
代码语言:javascript复制
int main() {
    std::inplace_vector<int, 2> v{};
    assert(v.empty());

    v.push_back(1);
    assert(1 == v.size());

    v.push_back(2);
    assert(2 == v.size());

    v.push_back(3); // throws
}
代码语言:javascript复制

就是boost static_vector 可以尝鲜使用

不明表标准委员会对于这个static_vector有啥不满意的,inplace_vector这名也莫名其妙

C :constexpr的数学库[4]
C :constexpr的数学库(二)[5]

这哥们等不及标准库 自己实现了一波constexpr

其实社区也有人做了 https://github.com/kthohr/gcem

C 成员指针完全解析(pointer to member)[6])

基础知识

现代 C 及其在 ClickHouse 中的应用[7]

基础知识,值得看一遍

C 实用技巧之 defer[8]

直接贴代码吧,感觉都见过了

代码语言:javascript复制
template <typename Fn>
class Defer {
    const Fn& fn_;

public:
    Defer(const Fn& fn) noexcept : fn_{fn} {}
    ~Defer() noexcept {
        fn_();
    }
    Defer(Defer&&) = delete;
};

#define DEFER_PASTE_(x, y) x##y
#define DEFER_CAT_(x, y) DEFER_PASTE_(x, y)
#define DEFER(...) Defer DEFER_CAT_(defer_, __LINE__){__VA_ARGS__}
void f() {
    auto res = somelib_alloc();
    DEFER([res]() { somelib_free(res); });
    // do with res
}
Type Sequence and Factory Method[9]

之前写过类似的,是根据type 用宏之类的拿到字符串,然后映射成type map

string name -> id -> class

他这个实现比较简单,就是就是string -> class指针,不是映射成type的形式

构造的时候批量构造,免得一个一个注册

核心代码

代码语言:javascript复制

代码语言:javascript复制
template <class... T>
struct type_sequence{};
// AA trick 看这个 https://www.youtube.com/watch?v=va9I2qivBOA

using dinasour_types = type_sequence<
    Diplodocus,
    Stegosaurus,
    Tyrannosaurus
>;


template <class T>
std::unique_ptr<Dinosaur> make_unique_dino() {
    return std::make_unique<T>();
}

template <class... Ts>
std::unique_ptr<Dinosaur> make_dinosaur_from(type_sequence<Ts...>, std::string_view name) {
    static std::unordered_map dinosaur_map {
        std::pair {get_typename<Ts>(), &make_unique_dino<Ts>} ...
    };
    if (auto it = dinosaur_map.find(std::string(name)); it != dinosaur_map.end()) {
        auto fn = it->second;
        return fn();
    }
    return nullptr;
}

std::unique_ptr<Dinosaur> make_dinosaur(std::string_view name) {
    return make_dinosaur_from(dinasour_types{}, name);
}
Are Function Pointers and Virtual Functions Really Slow? [10])

测下来发现基本没差,与其压测时间,不如看cycle

绑定指针毕竟是C里面的老OOP技巧,虚指针不过是自动化了这个动作

视频

cppcon 2023

BS还在safe 草药老师还在那里cpp2,我劝cpp这帮老头务实一点,别搞这些虚头八脑的

想看的 b站搜 BV1BC4y1R7iL

cppcon 2022

  • • Aliasing-Roi-Barkan

这个感觉说过

就是各种各样的歧义(别名)问题,多个指针(引用)指向同一个地址,然后误用了

比如string

代码语言:javascript复制
std::string s{“hello, ”};
s  = s;

比如指针

代码语言:javascript复制
// 这个函数设计就很坑爹
auto minmax = [](const string& i, const string& j, string* out_min, string* out_max) {
    *out_min = min(i, j); *out_max = max(i, j);
};
array<string, 2> arr{"22222", "11111"};
minmax(arr[0], arr[1], &arr[0], &arr[1]);

再比如引用,当然引用也是指针

代码语言:javascript复制
auto concat = [](string& result, const auto&... args) {
    ((result  = args), ...);
};

string x{"hello "}, y{"world "};
concat(x, y, x);

再比如成员变量

代码语言:javascript复制

complex<int> x{2, 2};
x *= reinterpret_cast<int*>(&x)[0];

再比如lambda的引用捕获,还是指针

代码语言:javascript复制
auto add_to_all = [](auto& v, const auto& val) {
    for_each(begin(v), end(v), [&](auto& x) { x  = val; });
};
vector<int> v{1, 2, 3};
add_to_all(v, v[0]);

经典的buffer重叠,没有什么比char*更指针的玩意了

代码语言:javascript复制
#include <cstring>
#include <iomanip>
#include <iostream>
#include <string>
using namespace std;
using namespace std::literals;
template <typename Fun>
void test(string_view name, Fun F) {
    char buffer[50] = "hello ";
    F(buffer   1, buffer, 6);
    buffer[0] = ' ';
    cout << name << " [" << buffer << "] "
         << (" hello "sv == buffer ? "Goodn" : "Badn");
}

void loopcpy(char* dst, const char* src, int size) {
    while (size--) *dst   = *src  ;
}
int main() {
    test("NOP    ", [](auto...) {});
    test("loopcpy", loopcpy);
    test("strcpy ", [](auto dst, auto src, auto...) { strcpy(dst, src); });
    test("strncpy ", strncpy);
    test("memcpy ", memcpy);
    test("memmove", memmove);
    test("copy_n ",
         [](auto dst, auto src, auto size) { copy_n(src, size, dst); });
    return 0;
}

不同编译器行为不同,有的判断重叠了有的没判断 https://godbolt.org/z/Er4dfPPqb

再比如STL迭代器,当然迭代器也是指针

代码语言:javascript复制
erase(v, *max_element(begin(v), end(v))); //remove也有类似的问题,但是有NB说明
copy(begin(v),end(v)-1, begin(v) 1); //copy_backward补救一下,这个场景其实类似上面的重叠
auto max = ranges::max_element(a);
stable_partition(begin(a),end(a),[=](const auto&x) {return x != *max;});

除了引入bug,歧义别名问题还会引发性能问题,c里为了这个问题引入了restrict

代码语言:javascript复制
void foo(std::vector<double>& v, const double& coeff) {
    for (auto& item : v) item *= std::sin(coeff);
}

改成传值性能翻倍

其他的就没啥了。讲了一些标准委员会的提案,我觉得没啥说的

这个哥们讲过很多次这个问题,这次是又重新汇总了一波

  • • Breaking-Dependencies-The-Visitor-Design-Pattern-Klaus-Iglberger

这哥们是 c software design的作者

对于数组多对象调用不同接口的多态模式,

std::visit std::variant相比要快一点,代码看上去也更优雅。仅作参考

  • • Back-to-Basics-Value-Semantics

还是上面那个哥们

这个比较简单,举了几个引用导致错误数据的例子,然后说值语义很有用,但没人用,介绍了几个组件 optional expected之类的

  • • a_pattern_language_for_expressing_concurrency

看几段代码

代码语言:javascript复制
ex::sender auto schedule_request_start(read_requests_ctx ctx) { … }
ex::sender auto validate_request(const http_request& req) { … }
ex::sender auto handle_request(const http_request& req) { … }
ex::sender auto send_response(const http_response& resp) { … }
ex::sender auto request_pipeline(read_requests_ctx ctx) {
return
    schedule_request_start(ctx)
        | ex::let_value(validate_request)
        | ex::let_value(handle_request)
        | ex::let_value(send_response)
;
}

auto handle_connection(const conn_data& cdata) {
    return read_http_request(cdata.io_ctx_, cdata.conn_)
        | ex::transfer(cdata.pool_.get_scheduler())
        | ex::let_value([&cdata](http_server::http_request req) {
            return handle_request(cdata, std::move(req));
            })
        | ex::let_error([](std::exception_ptr) { return just_500_response(); })
        | ex::let_stopped([]() { return just_500_response(); })
        | ex::let_value([&cdata](http_server::http_response r) {
            return write_http_response(cdata.io_ctx_, cdata.conn_, std::move(r));
            });
}


auto read_http_request(io::io_context& ctx, const io::connection& conn)
-> task<http_server::http_request> {
    http_server::request_parser parser;
    std::string buf;
    buf.reserve(1024 * 1024);
    io::out_buffer out_buf{buf};
    while (true) {
        std::size_t n = co_await io::async_read(ctx, conn, out_buf);
        auto data = std::string_view{buf.data(), n};
        auto r = parser.parse_next_packet(data);
        if (r)
            co_return {std::move(r.value())};
    }
}


auto write_http_response(io::io_context& ctx, const io::connection& conn,
    http_server::http_response resp) -> task<std::size_t> {
    std::vector<std::string_view> out_buffers;
    http_server::to_buffers(resp, out_buffers);
    std::size_t bytes_written{0};
    for (auto buf : out_buffers) {
        while (!buf.empty()) {
            auto n = co_await io::async_write(ctx, conn, buf);
            bytes_written  = n;
            buf = buf.substr(n);
        }
    }
    co_return bytes_written;
}
代码语言:javascript复制
代码语言:javascript复制
ex::sender auto read_from_socket() { … }
ex::sender auto process(in_data) { … }
ex::sender auto write_output(out_data) { … }
io_ontext io_threads;
static_thread_pool work_pool{8};
ex::scheduler auto sch_io = io_threads.get_scheduler();
ex::scheduler auto sch_cpu = work_pool.get_scheduler();
ex::sender auto snd
    = ex::on(sch_io, read_from_socket())
        | ex::transfer(sch_cpu)
        | ex::let_value(process)
        | ex::transfer(sch_io)
        | ex::let_value(write_output)
;
sync_wait(std::move(snd));

其实这玩意类似cpptaskflow在不同的execution上调度,实际上就是换一种写法

sender就是task sender也可以是coroutine 这样所有的就都串联起来了

所谓结构化编程,抽出线程之类的粗糙底座

  • • TS2 Tricks and Tips

介绍hazardptr实现的,有点意思,这个值得展开讲讲。这里标记个TODO

  • • [[likely]] Optimizations, [[unlikely]] Consequences

作者压测了几个场景使用[[likely]] [[unlikely]] 没啥差别,反而影响icache

使用最好压测一下,可能和代码layout相关,导致没啥收益

  • • MODERN C TO IMPRESS YOUR EMBEDDED DEV FRIENDS

嵌入式相关,一些代码优化,比较常规,比如enum class 还有上面提到的constexpr math库等等

  • • Using Modern C to Revive and Old Design

讲的还是结构化编程那玩意

  • • Jason-Turner-API-Design-Back-to-Basics

多用 [[nodiscard]],提示忽略的返回值

多用 noexcept 但是用了noexcept抛异常会直接挂掉

永远不要返回裸指针,起码用个not_null own_ptr之类的包装一下

错误处理,不要搞全局绕过的,比如errno get_last_error这种逼玩意

错误不是返回个错误码就完了,要必须处理,错误别optional 推荐expected

有些接口设计的语义模糊,参数可能窜位置,这种场景主要是implicit convention 隐式转换了,直接标记delete函数,规避这种场景

尽量避免传指针

fuzzer接口

尽可能constexpr

jason讲的还算有意思

  • • cppcon-understanding_allocator_impact_on_runtime_performance

这个讲的不如2017年那个allocator演讲,B站搜 Local (Arena) Memory Allocators,作者john lakos 主要还是要了解pmr 里那几个allocator用途

这个演讲主要是压测了一下 monotonic_buffer_resource 配 内存块(local memory buffer)的收益

不用说你也知道比系统allocator强很多

话说 john lakos这人有点牛逼,很多书你都想象不到是他写的

有个大规模c 系统设计就是他写的,前几年重写了第二版,变成两卷,还有去年的热门书 Embracing modern c safety

今年要出一本关于allocator的书,以及大规模c 系统设计出第三卷。

看视频看多了发现牛逼的人还是比较拔尖的,比如AA还有这个john lakos,演讲多文本也多

还剩40多个没看完,下期再说

开源项目需要人手

  • • asteria[11] 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线
  • • Unilang[12] deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • • gcc-mcf[13] 懂的都懂

读到这里真是辛苦你了,加个额外的互动环节,最近和群友讨论了很多坑爹的c 面试题,考一些边角的c 知识,什么如何禁止new啊虚表布局啊之类的

你觉得有啥面试题是真的好的面试题,有啥面试题是垃圾面试题?欢迎投稿/评论区反馈

我之前拉了个微信群满了,再拉一个应该够了,主要是接受投稿 交流用的,感兴趣的加一下

引用链接

[1] 周刊项目地址: https://github.com/wanghenshui/cppweeklynews [2] 提交 issue: https://github.com/wanghenshui/cppweeklynews/issues [3] Did you know about C 26 proposal - inplace_vector? : https://github.com/tip-of-the-week/cpp/blob/master/tips/348.md [4] C :constexpr的数学库: https://zhuanlan.zhihu.com/p/659750763 [5] C :constexpr的数学库(二): https://zhuanlan.zhihu.com/p/659994358 [6] C 成员指针完全解析(pointer to member): https://zhuanlan.zhihu.com/p/659510753 [7] 现代 C 及其在 ClickHouse 中的应用: https://zhuanlan.zhihu.com/p/655663455 [8] C 实用技巧之 defer: https://zhuanlan.zhihu.com/p/660233833 [9] Type Sequence and Factory Method: https://biowpn.github.io/bioweapon/2023/10/05/type-sequence-factory-pattern.html [10] Are Function Pointers and Virtual Functions Really Slow? : https://lucisqr.substack.com/p/are-function-pointers-and-virtual?r=1ecjkz&utm_campaign=post&utm_medium=web [11] asteria: https://github.com/lhmouse/asteria [12] Unilang: https://github.com/linuxdeepin/unilang [13] gcc-mcf: https://gcc-mcf.lhmouse.com/

0 人点赞