公众号
qq群 手机qq点击进入
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
感谢 YellyHornby fengyiee木马 不语 赞助
加了个互动栏目,欢迎各位投稿
欢迎投稿
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-10-04 第222期
cppcon 2023 已经发了四个视频了,BS又在讲安全,另外几个过于抽象,所以我本期还是主要更新cppcon 2022 的内容
九月邮件列表 https://isocpp.org//blog/2023/10/2023-09-mailing-available
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名字更合适一些
这哥们等不及标准库 自己实现了一波constexpr
其实社区也有人做了 https://github.com/kthohr/gcem
基础知识
基础知识,值得看一遍
直接贴代码吧,感觉都见过了
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 用宏之类的拿到字符串,然后映射成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);
}
测下来发现基本没差,与其压测时间,不如看cycle
绑定指针毕竟是C里面的老OOP技巧,虚指针不过是自动化了这个动作
BS还在safe 草药老师还在那里cpp2,我劝cpp这帮老头务实一点,别搞这些虚头八脑的
想看的 b站搜 BV1BC4y1R7iL
这个感觉说过
就是各种各样的歧义(别名)问题,多个指针(引用)指向同一个地址,然后误用了
比如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
错误处理,不要搞全局绕过的,比如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啊虚表布局啊之类的
你觉得有啥面试题是真的好的面试题,有啥面试题是垃圾面试题?欢迎投稿/评论区反馈