diff --git a/README.md b/README.md index 86e9ae5..f29351b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ RSS使用仓库的release RSS [链接](https://github.com/wanghenshui/cppweeklyn ## 2024 -| [171](./posts/171.md) | | | | | | | | | | +| [171](./posts/171.md) | [172](./posts/172.md) | | | | | | | | | | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | | [161](./posts/161.md) | [162](./posts/162.md) | [163](./posts/163.md) | [164](./posts/164.md) | [165](./posts/165.md) | [166](./posts/166.md) | [167](./posts/167.md) | [168](./posts/168.md) | [169](./posts/169.md) | [170](./posts/170.md) | | [151](./posts/151.md) | [152](./posts/152.md) | [153](./posts/153.md) | [154](./posts/154.md) | [155](./posts/155.md) | [156](./posts/156.md) | [157](./posts/157.md) | [158](./posts/158.md) | [159](./posts/159.md) | [160](./posts/160.md) | diff --git a/posts/172.md b/posts/172.md new file mode 100644 index 0000000..8ee14c5 --- /dev/null +++ b/posts/172.md @@ -0,0 +1,559 @@ +--- +layout: post +title: 第172期 +--- +# C++ 中文周刊 2024-11-16 第172期 + + +[周刊项目地址](https://github.com/wanghenshui/cppweeklynews) + +公众号 + + + +点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了 + +qq群 [点击进入](https://qm.qq.com/q/6NGizNPyG4) 满了加这俩 729240657 866408334 + +[RSS](https://github.com/wanghenshui/cppweeklynews/releases.atom) + +欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言 + +本期文章由 HNY 赞助 在此表示感谢 + +--- + +## 资讯 + +标准委员会动态/ide/编译器信息放在这里 + +[编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-11-13 第280期 ](https://mp.weixin.qq.com/s/4XWPWIaQ22t1V4UuP0qxWA) + +[clang增加了一个safebuffer模式 ](https://clang.llvm.org/docs/SafeBuffers.html) + +可以使用 `-Wunsafe-buffer-usage` 目前还在开发中 + +[clang增加了函数分析能力 ](https://clang.llvm.org/docs/FunctionEffectAnalysis.html) + +在noexcept 基础上增加了noblocking noallocating + +更准确分析函数行为,可以配合之前介绍的RealTimeSan使用 + +主要问题是函数指针不行,函数指针/function自动丢弃上述属性 + +另外存在属性覆盖,noblocking noallocating的必要条件是noexcept,权限大于,大家懂我意思吧 + +函数指针怎么绕过 + +```C++ +std::sort(vec.begin(), vec.end(), + [](const Elem& a, const Elem& b) [[clang::nonblocking]] { return a.mem < b.mem; }); // OK + +static bool compare_elems(const Elem& a, const Elem& b) [[clang::nonblocking]] { + return a.mem < b.mem; }; // 不行 属性会丢 + +std::sort(vec.begin(), vec.end(), compare_elems); + +template +class nonblocking_fp; + +template +class nonblocking_fp { +public: + using impl_t = R (*)(Args...) [[clang::nonblocking]]; + +private: + impl_t mImpl{ nullptr_t }; +public: + nonblocking_fp() = default; + nonblocking_fp(impl_t f) : mImpl{ f } {} + + R operator()(Args... args) const + { + return mImpl(std::forward(args)...); + } +}; + +// deduction guide (like std::function's) +template< class R, class... ArgTypes > +nonblocking_fp( R(*)(ArgTypes...) ) -> nonblocking_fp; + +// -- + +// Wrap the function pointer in a functor which preserves ``nonblocking``. +std::sort(vec.begin(), vec.end(), nonblocking_fp{ compare_elems }); +``` + +## 文章 + +- [Use std::span instead of C-style arrays ](https://www.sandordargo.com/blog/2024/11/06/std-span) + +~~我用得着你说?~~强调span不容易用错 + + +- [复习一下c++98 SFINAE](https://jguegant.github.io/blogs/tech/sfinae-introduction.html) + +```C++ +template +class HasIsNullMethod { + struct Yes { char unused[1]; }; + struct No { char unused[2]; }; + template struct reallyHas; + template static Yes& test(reallyHas* /*unused*/) { } + template static Yes& test(reallyHas* /*unused*/) { } + // template static Yes test(char(*)[static_cast(&C::isNull == false) + 1]) {} + template static No test(...); +public: + static const bool Value = (sizeof(test(0)) == sizeof(Yes)); +}; + +struct A { + char isNull() {} +}; +struct B { + void isNull() {} +}; + + +struct C { + char isNull; +}; + +bool fA() { + return HasIsNullMethod::Value; +} + +bool fB() { + return HasIsNullMethod::Value; +} + +bool fC() { + return HasIsNullMethod::Value; +} +// Type your code here, or load an example. +bool fint() +{ + return HasIsNullMethod::Value; +} +#include +int main() { + std::cout << fint() << "\n"; + std::cout << fA()<< "\n"; + std::cout << fB()<< "\n"; + std::cout << fC()<< "\n"; + return 0; +} +``` + +[godbolt](https://godbolt.org/z/hTaGja6cW) 不会也没啥,糟粕 喜欢怀旧可以看一下 + + +- [unique_ptr and the pointer to implementation idiom](https://andreasfertig.blog/2024/11/unique_ptr-and-the-pointer-to-implementation-idiom/) + + +当实现pimpl惯用法的时候,使用unique_ptr通常因为看不到完整实现(析构)调用失败 + +作者给的办法是手动加上deleter + +不要听他的,直接用shared_ptr,不要多此一举好吧 + +- [What is the current time around the world? Utilizing std::chrono with time zones in C++23 ](https://www.cppstories.com/2024/chrono_dates_zones/) + + +介绍timezone + +我记得有一个date库就做了这个活,用不上可以直接用那个date + +简单贴一下代码,介绍一下接口 + +```C++ +#include +#include + +int main() { + const auto now = std::chrono::system_clock::now(); + auto zt_local = std::chrono::zoned_time{ std::chrono::current_zone(), now }; + std::print("now is {} UTC and local is: {}\n", now, zt_local); + + constexpr std::string_view Warsaw{ "Europe/Warsaw" }; + constexpr std::string_view NewYork{ "America/New_York" }; + constexpr std::string_view Tokyo{ "Asia/Tokyo" }; + + try + { + const std::chrono::zoned_time zt_w{Warsaw, now}; + std::print("Warsaw: {0:%F} {0:%R}\n", zt_w); + const std::chrono::zoned_time zt_ny{NewYork, now}; + std::print("New York: {0:%F} {0:%R}\n", zt_ny); + const std::chrono::zoned_time zt_t{Tokyo, now}; + std::print("Tokyo: {0:%F} {0:%R}\n", zt_t); + } + catch (std::runtime_error& ex) + { + std::print("Error: {}", ex.what()); + } +} +/* +now is 2024-11-15 22:31:24.193993753 UTC and local is: 2024-11-15 22:31:24.193993753 +Warsaw: 2024-11-15 23:31 +New York: 2024-11-15 17:31 +Tokyo: 2024-11-16 07:31 +*/ +``` +[godbolt](https://godbolt.org/z/rEbfj69qf) 还有其他代码,就不贴了 + +- [C++, Complexity, and Compiler Bugs](https://azeemba.com/posts/cpp-complexity-compiler-bugs.html) + + + +这人不懂c++大惊小怪,就是简单的const延长生命周期 + +~~不看代码了。单纯喷他一下~~ + +- [Memory error checking in C and C++: Comparing Sanitizers and Valgrind ](https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind) + +老文章,valgrind不如sanitizer直接/快,且有遗漏 + +- [UB鉴赏环节](https://pvs-studio.com/en/blog/posts/cpp/1178/) + +死循环优化 + +```C++ +#include + +int fermat () { + const int MAX = 100; + int a=1,b=1,c=1; + int iter = 0; + while (1) { + if ( (a*a*a) == (b*b*b) + (c*c*c) ) { + std::cout << "Found!\n"; + return 1; + } + a++; + if (a>MAX) { + a=1; + b++; + } + if (b>MAX) { + b=1; + c++; + } + if (c>MAX) { + c=1; + } + ++iter; + } + return 0; +} + +int main () { + if (fermat()) { + std::cout << "Fermat's Last Theorem has been disproved.\n"; + } else { + std::cout << "Fermat's Last Theorem has not been disproved.\n"; + } + return 0; +} +``` + +打印 +```txt +Found! +Fermat's Last Theorem has been disproved. +``` + +由于return是死循环唯一出口,编译器激进到直接return 1 + +[这个可以看lancern文章有介绍过 ](https://zhuanlan.zhihu.com/p/391088391) 感谢zwuis提醒 + + +[析构栈溢出一个例子 ](https://godbolt.org/z/baaqjj39e) + +```C++ +#include +#include +#include + +struct Node { + int value = 0; + std::vector childrens; +}; + +struct List { + int value = 0; + std::unique_ptr next; + + ~List() { + while (next) { + // The destructor is still recursive, + // but now the recursion depth is 1 call. + next = std::move(next->next); + } + } + + List() noexcept = default; + List(List&&) noexcept = default; + List& operator=(List&&) noexcept = default; +}; + +int main() { + List dummynode; + List* l = &dummynode; + + int BOUND = 1000; + int SUB_BOUND = 100; + for (int i = 1; ivalue = i; + l->next = std::make_unique(); + l = l->next.get(); + } + /* + // 这个析构会栈溢出 + Node n; + auto tmp = &n; + for (int i = 1; ichildrens[j] = c; + // tmp = &tmp->childrens[j]; + } + } + */ +} + +``` + +noexcept问题 noexcept=noexcept(true), noexcept(cond) 可以自己定制 + +成员函数除了析构都是noexcept(false) ,如果析构函数抛异常需要显式指明 + +```C++ +struct SoBad { + // invoke std::terminate + ~SoBad() { + throw std::runtime_error("so bad dtor"); + } +}; + +struct NotSoBad { + // OK + ~NotSoBad() noexcept(false) { + throw std::runtime_error("not so bad dtor"); + } +}; +``` + +使用noexcept需要你写异常验证代码,避免terminate爆炸 + +缓冲区溢出问题 + +一大堆傻逼函数 scanf strcpy strcat gets strncat等等,哦还有memcpy + +引申出数组越界问题,数组越界会被激进优化,一定要注意 + +其实有点和前面的例子很相似 + +```C++ +const int N = 10; +int elements[N]; + +bool contains(int x) { + for (int i = 0; i <= N; ++i) { + if (x == elements[i]) { + return true; + } + } + return false; +} + +int main() { + for (int i = 0; i < N; ++i) { + std::cin >> elements[i]; + } + return contains(5); +} +``` + +存在越界 -> 越界是UB,编译器认为代码中没有UB,说明越界肯定不可达,说明提前返回 所以直接优化成true + + +类似例子 + +```C++ +const int N = 10; +int main() { + int decade[N]; + for (int k = 0; k <= N; ++k) { + printf("k is %d\n",k); + decade[k] = -1; + } +} +``` + +k越界,越界是UB,编译器认为代码中没有UB,说明永远到不了N,直接死循环 + +- [ Analyzing the Performance of the “Proxy” Library ](https://devblogs.microsoft.com/cppblog/analyzing-the-performance-of-the-proxy-library/) + +鉴定为不如varint,评论区有人指出性能比varint不太行,测试代码我拿来在7950x WSL跑了一下 + +```txt +2024-11-16T23:09:24+08:00 +Running ./benchmarks/msft_proxy_benchmarks +Run on (32 X 4499.92 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x16) + L1 Instruction 32 KiB (x16) + L2 Unified 1024 KiB (x16) + L3 Unified 32768 KiB (x1) +Load Average: 0.65, 0.21, 0.11 +--------------------------------------------------------------------------------------- +Benchmark Time CPU Iterations +--------------------------------------------------------------------------------------- +BM_SmallObjectInvocationViaProxy 4648790 ns 5071349 ns 136 +BM_SmallObjectInvocationViaVirtualFunction 11986710 ns 13076183 ns 54 +BM_SmallObjectInvocationViaVariant 5044399 ns 5495963 ns 127 +BM_LargeObjectInvocationViaProxy 7689574 ns 8388641 ns 83 +BM_LargeObjectInvocationViaVirtualFunction 9397069 ns 10251350 ns 68 +BM_LargeObjectInvocationViaVariant 5046724 ns 5490317 ns 127 +BM_SmallObjectManagementWithProxy 1603751 ns 1749509 ns 402 +BM_SmallObjectManagementWithUniquePtr 8952997 ns 9766806 ns 72 +BM_SmallObjectManagementWithSharedPtr 11484836 ns 12528993 ns 56 +BM_SmallObjectManagementWithSharedPtr_Pooled 14436982 ns 15749468 ns 44 +BM_SmallObjectManagementWithAny 6424830 ns 7008942 ns 96 +BM_SmallObjectManagementWithVariant 514705 ns 561491 ns 1199 +BM_LargeObjectManagementWithProxy 39497657 ns 43086918 ns 17 +BM_LargeObjectManagementWithProxy_Pooled 30985407 ns 33760433 ns 21 +BM_LargeObjectManagementWithUniquePtr 40788280 ns 44496508 ns 13 +BM_LargeObjectManagementWithSharedPtr 50875412 ns 55500655 ns 11 +BM_LargeObjectManagementWithSharedPtr_Pooled 30422979 ns 33188319 ns 21 +BM_LargeObjectManagementWithAny 32133635 ns 35037580 ns 20 +BM_LargeObjectManagementWithVariant 12639262 ns 13788144 ns 50 +``` + +这明显不如varint啊。还是观望吧 + + +- [How can I explicitly specialize a templated C++ constructor?](https://devblogs.microsoft.com/oldnewthing/20241011-00/?p=110365) + + +如何显式指定构造函数模板形参?怎样让模板构造函数识别不同类型 + +实际上就是让构造函数作为工厂函数接口,工厂模式大家都熟悉,但是问题在于构造函数不能指定类型 + +比如这种实例 + +```C++ +// Assume derived classes by convention have a constructor +// whose first parameter is an ObjectManager&. +struct CommonBase +{ + virtual ~CommonBase(){} + virtual void initialize(int reason) = 0; +}; + +struct Widget : CommonBase +{ + Widget(int param) {} + Widget(Widget&&) {} + void initialize(int reason) { + std::cout << "ok\n"; + } + +}; + +struct ObjectManager +{ + // Concrete should derive from CommonBase + template + ObjectManager(int reason, + std::in_place_type_t, + Args&&...args) : + m_base(std::make_unique( + std::forward(args)...)) + { + m_base->initialize(reason); + } + template + static ObjectManager make(int reason, Args&&...args) + { + return ObjectManager(reason, + std::in_place_type_t{}, + std::forward(args)...); + } + std::unique_ptr m_base; +}; + +struct ObjectManager0 +{ + // Concrete should derive from CommonBase + template + ObjectManager0(int reason, Args&&...args) : + m_base(std::make_unique( std::forward(args)...)) + { + m_base->initialize(reason); + } + + std::unique_ptr m_base; +}; + +// auto m0 = ObjectManager0(9,42); +// auto m0 = ObjectManager0::ObjectManager0(9,42); +auto m2 = ObjectManager::make(9, 42); +``` +make显然就是工厂函数那种类型构造,但是构造函数没法指定类型 + +我们怎么给构造函数传递类型?用in_place_type_t + +上面的代码还算容易看懂,其实就是tag把类型带过来,有点像identity_type那种玩法 + + +不过构造函数模板不如类模板来的直观一些,这么玩有点复杂, 代码 [godbolt](https://godbolt.org/z/qj7YGox9a) + +- [The Big Array Size Survey for C](https://thephd.dev/the-big-array-size-survey-for-c) + +数组长度命名,纠结老半天,了解历史可以看 ~~(没有看的必要)~~ + +- [Non-allocating Circular Buffer in C++ ](https://mobiarch.wordpress.com/2024/11/15/non-allocating-circular-buffer-in-c/) + +数组 + 循环index,单线程。玩具,[代码 ](https://github.com/bibhas2/CircusBuff/blob/master/circ-buff.h) ~~自己看吧。懒得贴了。不值一看~~ + +- [Unit Testing Numerical Routines](https://buchanan.one/blog/testing-numerical-algorithms/) + +介绍一些代码测试经验,测试数据从何而来,如何更准确的测试接口,如何让测试代码更好的解释函数 + +感觉做图形学的哥们值得讲一下对应经验 + + +## 开源项目介绍 + +- [asteria](https://github.com/lhmouse/asteria) 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线 +- [spdlog 1.15](https://github.com/gabime/spdlog/releases/tag/v1.15.0) 更新,修了一堆bug,没啥重大修复,升不升都行 + +## 热门库最近更新了什么 + +[brpc 最近的MR ](https://github.com/apache/brpc/pull/2819) + +介绍一下背景 + +brpc内部是由bthread驱动事件,可以理解为小的线程,类似boost fiber + +bthread的调度是wait free的,设计了work steal,如果没有任务就去其他worker线程去偷 + +问题在于bthread本身是没有优先级的,epoll唤醒和普通IO事件并没有做区分,唤醒的等级应该是最高的 + +这个MR就是做了个flag区分,让唤醒优先级更高一些 + + +## 工作招聘 + +年底目前没有啥好工作推荐,我在公众微信号会单独发 + +## 互动环节 + +本周绝对不鸽好吧 + +--- + +[上一期](https://wanghenshui.github.io/cppweeklynews/posts/171.html) [下一期](https://wanghenshui.github.io/cppweeklynews/posts/173.html)