公众号
点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
qq群 点击进入 满了加这个 729240657
欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
本期文章由 YellyHornby,qlql 赞助 在此表示感谢 祝老板身体健康事业顺利
资讯
标准委员会动态/ide/编译器信息放在这里
c++26 最新进展!Hagenberg会议之前微信过,这里补一下
本文来自前线记者Mick
Hagenberg (2025-02)总结
这次会议是C++26周期第六次会议,也是C++26 Feature Freeze;到这一步,所有没有进入Stage 3 (wording)的提案都只能推迟到C++29了,因此我们也确定了C++26所可能拥有的特性的最大范围。
这次会议同样是整个周期最重要的会议之一,我们迎来了C++20之后第一个T0级语言特性的通过,迎来了多个历经十年长跑的提案的修成正果,也迎来了C++在安全问题上迈出的第一大步。作为一个不在主场进行的会议,本次会议的参会人数只有200人(比上次少20人左右),但是依然迎来了大量重要的进展。
先来看看这次会议通过进入C++26的提案。语言方面,最重要的提案无疑是P2900 Contracts,这一重量级特性是对C assert()宏的正式承认和发展,加入了新的contract_assert宏和pre/post指示来明确实现函数的前置/后置条件。与assert宏依赖的NDEBUG不同,这些新的Contracts指示将会通过编译选项(-fcontract-semantic=...)指示,并提供了更加细致的四种不同语义(ignore,observe,quick_enforce,enforce)来满足各种场景的需求。Contracts是一个非常大的话题,在这里也没法用几句话囊括它的一切,但作为C++最重要的安全特性之一后面应该会有大量的教程来讲解这个特性
另一个重要的通过的语言特性是P2786 trivially relocatable。平凡迁移指的是那些“移动+析构=memcpy”的类型,而满足这个条件的类型出乎意料地多(例如三家里有两家的vector和string都能平凡迁移)有了语言层面的探测平凡迁移类型的手段之后,vector::resize之类的函数就可以将元素数组整体memcpy过去而不是一个个移动了。遗憾的是,在EWG的长期争吵之后,P2786最终选择了trivially_relocatable_if_eligible和replaceable_if_eligible这两个超级长的关键字名字作为标记一个类型可以平凡迁移的手段,并且为了安全这些关键字只有当所有成员都能平凡迁移的情况下才有效,极大限制了它们的可用性。
除此之外,这次会议还通过了P1976 #embed。这是一个C23的新预处理宏,允许将任意文件作为逗号分隔的列表引入程序。这是对于传统的#include "some_data.txt"的更高级解决方案,速度更快,也没有撞到字符串极限的可能性。它的功能也更强,可以通过#embed "something" limit(5)来限制最大可引入的元素数。
这次会议还通过了P3475(废除没人能懂的memory_order::consume),P2841(允许template<template concept C>和template<template bool B>等构造)
标准库方面,这次会议通过的最重要特性无疑是P3471 Standard Library Hardening,在标准中规定了“强化实现”的概念。在强化实现中,标准库中的各个operator[]和其他常见函数将会使用Contracts强制检查参数没有出界,从而能够有效避免一些最常见的越界UB的发生。和P2900结合,这两个提案是对近期爆发的对C++安全性的批评的有力回应。在P3100/P3599得到实现后,在语言和库方面我们终于对于常见UB有了有效的解决方案。
另一个重要提案是P0447 std::hive。这是一个悲情的提案,初版提出于2016年,经过9年争吵,整整29个revision,以及改名(colony -> hive),以及在2023年险些被直接拒绝,最终还是在这次会议修成正果。这个新的标准顺序容器是一个很有意思的容器,利用特殊的跳表+链状数组结构实现了所有元素的绝对迭代器稳定性(只要不被删除就永远有效)+均摊O(1)的插入/删除/迭代这样看似不可能达成的复杂度。这一特性使得它非常适合在各类游戏项目中储存entity;这些游戏元素很多时候会被不断插入和删除,但是既有的元素的位置不能变。
第三组重要的通过提案是P3372 constexpr容器和P3378 constexpr异常类型。在上次会议允许了编译期抛异常之后,这次将大多数标准异常类型(比如out_of_range,invalid_argument)完成了constexpr化,从而让编译期抛出这些异常成为可能。P3372也让list map deque等容器在编译期的使用成为可能,不过遗憾的是非透明编译期内存分配仍然没有任何解决的希望,从而这些容器的constexpr使用总是让人觉得有些别扭。
除此之外,这次会议还通过了多个SIMD bugfix提案,P3137 views::to_input,P2846 ranges::reserve_hint等Ranges增强;另外P3019 indirect and polymorphic在上次通过后发现了一些问题,这次会议再次通过了一下。
在各特性的进展方面,这次会议是Stage 2 -> 3的deadline,现在还没forward的就都被推迟到29周期去了。
EWG方面,这次会议P2996 静态反射的review已经接近尾声,基本确定将会在下次会议进入Plenary。这次会议通过了几个针对P2996的bugfix和扩展,包括P3547 access_context来限制反射体的获取可能性,以及P3096函数参数的反射。除此之外,BS和HS提出的重量级安全特性Profiles(其实就是写[[profiles::enforce(xxx)]]然后禁掉一些不安全特性,reinterpret_cast之类的)在讨论了一天半之后最终决定不进入C++26,而是改为发布一个白皮书(轻量级TS)来更快推进。(被忽略好几年的TM TS可能也会转换成白皮书)
其他大特性方面,模式匹配最终小比分落败,没能进入C++26,但是在最后一天EWG保住了P1306 Expansion Statement这个重要的反射的补充特性,也算保住了底线吧。接下来CWG将会有比较艰巨的任务,完成反射这个巨大提案的review的同时还被塞了30+篇小提案,最后可能不得不要掉出去一堆了。
LEWG方面,这次会议通过了大量S&R的附属提案,包括P3149 async_scope和P3296 let_async_scope这两个重量级的Structured Concurrency提案(遗憾的是std::task/lazy依然没有太大进展)和一个标准化的系统调度器(P2079)。除此之外,几个著名的提案包括P2988 std::optional<T&>,P3179 Ranges化的多线程算法,P2019线程名称与与栈大小,以及P0260多线程队列也得到了forward的机会,从而有机会进入C++26。接下来LWG的任务较为轻松,只要把那几个S&R的提案搞定其实就差不多了。
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2025-01-08 第288期
文章
API design note: Beware of adding an “Other” enum value
考虑这种枚举定义
enum class WidgetFlavor
{
Vanilla,
Chocolate,
Strawberry,
Other,
};
有个Other,也许有一天,你新添加了一个Flavor,Mint,那么这个Other会不会包含这个新加的Mint?
永远不要用这种歧义的玩意,将来会引入维护性问题
Investigating an argument-dependent lookup issue and working around it
现场长这样
namespace winrt::impl
{
⟦ ... ⟧
template <typename Delegate, typename... Arg>
bool invoke(Delegate const& delegate, Arg const&... args) noexcept;
⟦ ... ⟧
template <typename Derived, typename AsyncInterface, typename TProgress = void>
struct promise_base : implements<Derived, AsyncInterface, Windows::Foundation::IAsyncInfo>
{
⟦ ... ⟧
void set_completed() noexcept
{
async_completed_handler_t<AsyncInterface> handler;
AsyncStatus status;
⟦ ... ⟧
if (handler)
{
invoke(handler, *this, status);
}
}
}
}
没有调用内部的invoke,被ADL引入了std::invoke
加上内部命名空间解决
The Weirdest MSVC Address Sanitizer Bug
#include <memory>
#include <type_traits>
template <typename stream>
struct io {
io(stream) : b(false) {}
alignas(64) bool b;
};
int main() {
auto pi = std::make_unique<int>(543);
using iio = io<int>;
io x(7);
static_assert(std::is_same_v<decltype(x), iio>);
}
msvc用 /fsanitize=address会误报。
ODR, libc++ hardening, Profiles and Contracts
简单介绍一下 WG21 和安全profile constract可能对ODR规则造成影响,对于标准库实现存在挑战
Eliminating redundant bound checks
跳转表可能会有边界检查没有优化掉
#include <array>
#include <cstdint>
#include <cstdlib>
static constexpr std::array<size_t, 256> TABLE = {
798, 553, 541, 345, 276, 698, 861, 448, 898, 588, 678, 593, 265, 611, 915, 835, 893, 3, 411,
769, 792, 115, 526, 836, 356, 454, 279, 876, 924, 644, 449, 682, 727, 267, 665, 889, 75, 825,
667, 222, 603, 43, 376, 655, 221, 811, 35, 182, 550, 112, 660, 374, 241, 589, 993, 428, 747,
673, 11, 954, 296, 181, 842, 91, 764, 805, 155, 916, 431, 380, 860, 970, 998, 982, 445, 144,
199, 587, 400, 356, 283, 645, 575, 288, 521, 861, 175, 289, 616, 766, 48, 29, 265, 492, 832,
412, 766, 827, 22, 924, 766, 559, 1012, 826, 391, 254, 871, 347, 615, 529, 789, 606, 259, 4,
924, 988, 2, 833, 582, 366, 402, 186, 328, 181, 58, 124, 478, 380, 782, 582, 993, 536, 790,
657, 558, 829, 637, 129, 177, 72, 847, 916, 236, 398, 37, 932, 844, 938, 580, 784, 58, 713,
490, 12, 680, 525, 940, 30, 241, 345, 1019, 0, 742, 169, 660, 253, 187, 545, 288, 333, 137,
587, 731, 660, 600, 128, 389, 44, 109, 401, 195, 147, 631, 690, 191, 614, 797, 744, 28, 946,
348, 851, 889, 279, 521, 724, 897, 92, 773, 635, 212, 339, 978, 639, 282, 414, 691, 365, 706,
953, 754, 976, 482, 727, 257, 673, 443, 99, 341, 540, 247, 427, 312, 713, 943, 815, 595, 611,
191, 827, 179, 827, 432, 472, 237, 163, 218, 35, 475, 800, 299, 185, 779, 27, 924, 981, 11,
504, 160, 9, 342, 938, 69, 745, 575, 791,
};
static size_t first_idx_to_second_idx(uint8_t first_idx)
{
switch (first_idx) {
case 0: return 798; case 1: return 553; case 2: return 541; case 3: return 345;
case 4: return 276; case 5: return 698; case 6: return 861; case 7: return 448;
case 8: return 898; case 9: return 588; case 10: return 678; case 11: return 593;
case 12: return 265; case 13: return 611; case 14: return 915; case 15: return 835;
case 16: return 893; case 17: return 3; case 18: return 411; case 19: return 769;
case 20: return 792; case 21: return 115; case 22: return 526; case 23: return 836;
case 24: return 356; case 25: return 454; case 26: return 279; case 27: return 876;
case 28: return 924; case 29: return 644; case 30: return 449; case 31: return 682;
case 32: return 727; case 33: return 267; case 34: return 665; case 35: return 889;
case 36: return 75; case 37: return 825; case 38: return 667; case 39: return 222;
case 40: return 603; case 41: return 43; case 42: return 376; case 43: return 655;
case 44: return 221; case 45: return 811; case 46: return 35; case 47: return 182;
case 48: return 550; case 49: return 112; case 50: return 660; case 51: return 374;
case 52: return 241; case 53: return 589; case 54: return 993; case 55: return 428;
case 56: return 747; case 57: return 673; case 58: return 11; case 59: return 954;
case 60: return 296; case 61: return 181; case 62: return 842; case 63: return 91;
case 64: return 764; case 65: return 805; case 66: return 155; case 67: return 916;
case 68: return 431; case 69: return 380; case 70: return 860; case 71: return 970;
case 72: return 998; case 73: return 982; case 74: return 445; case 75: return 144;
case 76: return 199; case 77: return 587; case 78: return 400; case 79: return 356;
case 80: return 283; case 81: return 645; case 82: return 575; case 83: return 288;
case 84: return 521; case 85: return 861; case 86: return 175; case 87: return 289;
case 88: return 616; case 89: return 766; case 90: return 48; case 91: return 29;
case 92: return 265; case 93: return 492; case 94: return 832; case 95: return 412;
case 96: return 766; case 97: return 827; case 98: return 22; case 99: return 924;
case 100: return 766; case 101: return 559; case 102: return 1012; case 103: return 826;
case 104: return 391; case 105: return 254; case 106: return 871; case 107: return 347;
case 108: return 615; case 109: return 529; case 110: return 789; case 111: return 606;
case 112: return 259; case 113: return 4; case 114: return 924; case 115: return 988;
case 116: return 2; case 117: return 833; case 118: return 582; case 119: return 366;
case 120: return 402; case 121: return 186; case 122: return 328; case 123: return 181;
case 124: return 58; case 125: return 124; case 126: return 478; case 127: return 380;
case 128: return 782; case 129: return 582; case 130: return 993; case 131: return 536;
case 132: return 790; case 133: return 657; case 134: return 558; case 135: return 829;
case 136: return 637; case 137: return 129; case 138: return 177; case 139: return 72;
case 140: return 847; case 141: return 916; case 142: return 236; case 143: return 398;
case 144: return 37; case 145: return 932; case 146: return 844; case 147: return 938;
case 148: return 580; case 149: return 784; case 150: return 58; case 151: return 713;
case 152: return 490; case 153: return 12; case 154: return 680; case 155: return 525;
case 156: return 940; case 157: return 30; case 158: return 241; case 159: return 345;
case 160: return 1019; case 161: return 0; case 162: return 742; case 163: return 169;
case 164: return 660; case 165: return 253; case 166: return 187; case 167: return 545;
case 168: return 288; case 169: return 333; case 170: return 137; case 171: return 587;
case 172: return 731; case 173: return 660; case 174: return 600; case 175: return 128;
case 176: return 389; case 177: return 44; case 178: return 109; case 179: return 401;
case 180: return 195; case 181: return 147; case 182: return 631; case 183: return 690;
case 184: return 191; case 185: return 614; case 186: return 797; case 187: return 744;
case 188: return 28; case 189: return 946; case 190: return 348; case 191: return 851;
case 192: return 889; case 193: return 279; case 194: return 521; case 195: return 724;
case 196: return 897; case 197: return 92; case 198: return 773; case 199: return 635;
case 200: return 212; case 201: return 339; case 202: return 978; case 203: return 639;
case 204: return 282; case 205: return 414; case 206: return 691; case 207: return 365;
case 208: return 706; case 209: return 953; case 210: return 754; case 211: return 976;
case 212: return 482; case 213: return 727; case 214: return 257; case 215: return 673;
case 216: return 443; case 217: return 99; case 218: return 341; case 219: return 540;
case 220: return 247; case 221: return 427; case 222: return 312; case 223: return 713;
case 224: return 943; case 225: return 815; case 226: return 595; case 227: return 611;
case 228: return 191; case 229: return 827; case 230: return 179; case 231: return 827;
case 232: return 432; case 233: return 472; case 234: return 237; case 235: return 163;
case 236: return 218; case 237: return 35; case 238: return 475; case 239: return 800;
case 240: return 299; case 241: return 185; case 242: return 779; case 243: return 27;
case 244: return 924; case 245: return 981; case 246: return 11; case 247: return 504;
case 248: return 160; case 249: return 9; case 250: return 342; case 251: return 938;
case 252: return 69; case 253: return 745; case 254: return 575; case 255: return 791;
// Unreachable, because every possible uint8_t value already has its switch case, but Clang
// emits a warning otherwise.
default: return 0;
}
}
void increment_table(std::array<uint8_t, 1024> &data, uint8_t first_idx)
{
size_t second_idx = TABLE.at(first_idx);
data.at(second_idx)++;
}
void increment_switch(std::array<uint8_t, 1024> &data, uint8_t first_idx)
{
size_t second_idx = first_idx_to_second_idx(first_idx);
data.at(second_idx)++;
}
强制优化,switch展开 不过gcc优化不掉
A Clang regression related to switch statements and inlining
压测发现inline和noinline差距非常离谱,发现是clang的bug
What You Need to Know when Optimizations Changes the Behavior of Your C++
改变了编译期选项,行为变了,说明什么?有UB呗,还是那几套检查工具
-fsanitize=undefined,address
-fwrapv
-fno-strict-aliasing
constexpr带来的UB
reinterpret_cast带来的UB
使用harden https://libcxx.llvm.org/Hardening.html
静态分析工具等
#embed教程
Contracts 教程
代码鉴赏: 单次set同时判断' ' \r \t
sonic_force_inline uint64_t GetNonSpaceBits(const uint8_t *data) {
// const simd::simd8x64<uint8_t> v(data);
// const auto whitespace_table =
// simd128<uint8_t>::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100,
// '\t', '\n', 112, 100, '\r', 100, 100);
__m128i whitespace_table =
_mm_setr_epi8(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112,
100, '\r', 100, 100);
__m128i v0 = _mm_loadu_si128((__m128i const *)data);
__m128i v1 = _mm_loadu_si128((__m128i const *)(data + 16));
__m128i v2 = _mm_loadu_si128((__m128i const *)(data + 32));
__m128i v3 = _mm_loadu_si128((__m128i const *)(data + 48));
v0 = _mm_cmpeq_epi8(v0, _mm_shuffle_epi8(whitespace_table, v0));
v1 = _mm_cmpeq_epi8(v1, _mm_shuffle_epi8(whitespace_table, v1));
v2 = _mm_cmpeq_epi8(v2, _mm_shuffle_epi8(whitespace_table, v2));
v3 = _mm_cmpeq_epi8(v3, _mm_shuffle_epi8(whitespace_table, v3));
uint64_t m0 = uint32_t(_mm_movemask_epi8(v0));
uint64_t m1 = _mm_movemask_epi8(v1);
uint64_t m2 = _mm_movemask_epi8(v2);
uint64_t m3 = _mm_movemask_epi8(v3);
uint64_t space = m0 | (m1 << 16) | (m2 << 32) | (m3 << 48);
return ~space;
}
感谢Caturra投稿
开源项目介绍
- asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线
- https://github.com/yosefk/funtrace 一个小的tracer库