C++ 中文周刊 第133期

周刊项目地址

公众号

qq群 手机qq点击进入

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

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

提交 issue

感谢 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

文章

Did you know about C++26 proposal - inplace_vector?

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
}

就是boost static_vector 可以尝鲜使用

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

群友补充:llvm::SmallVector名字更合适一些

C++:constexpr的数学库

C++:constexpr的数学库(二)

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

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

C++成员指针完全解析(pointer to member))

基础知识

现代 C++ 及其在 ClickHouse 中的应用

基础知识,值得看一遍

C++实用技巧之 defer

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

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

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

string name -> id -> class

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

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

核心代码


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? )

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

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

视频

cppcon 2023

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

想看的 b站搜 BV1BC4y1R7iL

cppcon 2022

这个感觉说过

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

比如string

std::string s{hello, };
s += s;

比如指针

// 这个函数设计就很坑爹
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]);

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

auto concat = [](string& result, const auto&... args) {
    ((result += args), ...);
};

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

再比如成员变量


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

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

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*更指针的玩意了

#include <algorithm>
#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 ? "Good\n" : "Bad\n");
}

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迭代器,当然迭代器也是指针


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

void foo(std::vector<double>& v, const double& coeff) {
for (auto& item : v) item *= std::sin(coeff);
}

改成传值性能翻倍

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

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

这哥们是 c++ software design的作者

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

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

还是上面那个哥们

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

看几段代码

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;
}
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 这样所有的就都串联起来了

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

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

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

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

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

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

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

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

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

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

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

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

尽量避免传指针

fuzzer接口

尽可能constexpr

jason讲的还算有意思

这个讲的不如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多个没看完,下期再说

开源项目需要人手


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

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

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账