layout | title |
---|---|
post |
第140期 |
公众号
qq群 手机qq点击进入
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
最近在找工作准备面试题,更新可能有些拖沓,见谅
本周内容比较少
本期文章由 YellowHornby HNY 不语 黄亮Anthony 赞助
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-11-29 第230期
能返回值优化,不move
定位到 mmap_sem 互斥锁冲突,查找来源
哪里会有mmap?
- 内存分配器 ptmalloc / tcmalloc 大页
- fopen/fseek?
逐个排除定位到是fseek内部有mmap,新版本glibc已经去掉了mmap改了回来,打patch
那么为什么fseek会调用mmap呢?就不能malloc吗 buffer能多大?
本台记者Anien分析是历史原因,为了初始化不清空0,用mmap偷懒了一下。原来大家都偷懒
densemap就是flatmap-
看个乐,别学。还是friend注入那套玩意儿
整型溢出似乎没有好的解决办法,除了多注意/自定义类型加边界检查
介绍了其他语言的解决方法,比如rust 的 overflowing_mul overflowing_add
google内部也有类似的库设计 https://github.com/chromium/subspace/pull/410/files
似乎除了这些,没有更好的办法?
一个幽默的typemap
说到typemap映射,第一反应是字符串/typeindex?是的他就这么实现的,不过用了匿名的类
typeindex计算是很慢的。数量大不建议这么玩,这里展示这种可能性
#include <map>
#include <string>
#include <iostream>
#include <typeindex>
#include <any>
#include <optional>
using dict = std::map<std::type_index, std::any>;
template <class Name, class T>
struct key final { explicit key() = default;};
template <class Name, class T>
auto get(const dict& d, key<Name, T> k) ->std::optional<T> {
if (auto pos = d.find(typeid(k)); pos!=d.end()) {
return std::any_cast<T>(pos->second);
}
return std::nullopt;
}
template <class Name, class T, class V>
void set(dict& d, key<Name, T> k, V&& v) {
constexpr bool convertible = std::is_convertible_v<V, T>;
static_assert(convertible);
if constexpr (convertible) {
d.insert_or_assign(typeid(k), T{std::forward<V>(v)});
}
}
// key里面的类可以只声明不实现,当tag用,只要唯一就行
using age_k = key<struct _age_, int>;
using gender_k = key<struct _gender_, std::pair<float,float>>;
using name_k = key<struct _name_, std::string>;
constexpr inline auto age = age_k{};
constexpr inline auto gender = gender_k{};
constexpr inline auto name = name_k{};
int main() {
auto person = dict();
set(person, age, 14);
set(person, gender,std::pair{0.5,0.5});
set(person, name,"ted");
const auto a = get(person, age);
const auto g = get(person, gender);
const auto n = get(person, name);
std::cout <<*a <<g->first << g->second << *n<<"\n";
}
https://godbolt.org/z/z1hvxzf1e 可以自己玩一下
如果真要考虑用,首先不能用typeindex,得实现一个类转字符串,还得保证唯一性,然后用hashmap存就行
另外不用std::any,用create函数之类的代替
这种需求感觉游戏行业有这种场景
对于array,编译期检查越界应该是可能的
int get_last_elem(const std::array<int, 5>& arr) {
return arr.at(5); // oops, off-by-one
}
这种明显越界,编译期能不能抓到?能,但不报错,会直接抛异常,编译器比较信任你,觉得你喜欢异常
get_last_elem(std::array<int, 5ul> const&): # @get_last_elem(std::array<int, 5ul> const&)
push rax
lea rdi, [rip + .L.str]
mov esi, 5
mov edx, 5
xor eax, eax
call std::__throw_out_of_range_fmt(char const*, ...)@PLT
flux库写了个编译期检查的设计
int get_last_elem(const std::array<int, 5>& arr) {
return flux::read_at(arr, 5); // oops, off-by-one
}
编译期就抓到直接报错,如何实现?
[[gnu::error("out-of-bounds sequence access detected")]]
void static_bounds_check_failed();
template <typename Index>
void bounds_check(Index idx, Index limit)
{
if (__builtin_constant_p(idx) && __builtin_constant_p(limit)) {
if (idx < Index{0} || idx >= limit) {
/* ...report error at compile time... */
}
} else {
/* ...perform normal run-time bounds check... */
}
但存在问题,__builtin_constant_p并不是那么可信
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112296
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89029
只能说,缘分相信编译器,可能帮你一下
安全加固的编译配置
-O2 -Wall -Wformat=2 -Wconversion -Wtrampolines -Wimplicit-fallthrough \
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \
-D_GLIBCXX_ASSERTIONS \
-fstrict-flex-arrays=3 \
-fstack-clash-protection -fstack-protector-strong \
-Wl,-z,nodlopen -Wl,-z,noexecstack \
-Wl,-z,relro -Wl,-z,now
FORTIFY有性能影响
这些还是要关注一下性能影响,中间三行有影响。如果某些行业必须加固那也没办法
tag pointer
指针一般8字节64bits,只用48bits表示内存空间,剩下的16bits有很多可以倒腾的空间,嵌入一些信息
提问: 48bit能表示多大内存?
剩下的这16bit能做什么文章?
- x86_64 加标志位区分内核内存还是用户态内存
- rsicv也有类似设计
- arm是49位内存,其他设计也是类似的
各种硬件系统还有自己的其他设计,利用bit,这里就不展开了
- Intel 有 Linear Address Masking (LAM)
- AMD有 Upper Address Ignore
- rsicv有 pointer masking extension
- arm有 Top Byte Ignore (TBI)
这波报菜名大部分都没什么接触的机会。
考虑自定义的场景
-
fixie trie 用不到的16位用来做bitmap
-
llvm pointpairpointer 其实有点union双关用法了 https://github.com/llvm/llvm-project/blob/dc8b055c71d2ff2f43c0f4cac66e15a210b91e3b/llvm/include/llvm/ADT/PointerIntPair.h#L64
- 暴论 tag pointer 就是union
-
显然 ZGC的pointer设计也是tag pointer https://dinfuehr.github.io/blog/a-first-look-into-zgc/
-
显然 objc和go的tag pointer自然都是,这俩比较有名,就不列了
-
当然 v8 中的pointer compression 自然也是 tag pointer https://v8.dev/blog/pointer-compression
-
那么异或链表自然也是tag pointer
-
当然酒店传单也是tag pointer,不仅可以订餐还可以点按摩是不是
-
Lightning Talk: When C++ Managers Fail... Richard Shepherd - C++ on Sea 2023
重新考虑单例实现
static 单例 static保证 成功
创建
一次
那么存在构造函数抛异常的可能性。注意你的单例的T是不是可能构造抛异常
可能挽救一下就成了这个德行
class Manager {
Resouce* resource_;
Manager() : resource_{CreateResource()} {
if (!resource_) {
throw std::exception("Not ready")
}
}
public:
static Manager* Instance() {
try {
static Manager s;
return &s;
} catch (...) {
return nullptr;
}
}
};
简单加个优先级,是的,没错,用priority_queue
#include <coroutine>
#include <iostream>
#include <queue>
#include <utility>
struct Task {
struct promise_type {
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Task get_return_object() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
void return_void() {}
void unhandled_exception() {}
};
Task(std::coroutine_handle<promise_type> handle): handle{handle}{}
auto get_handle() { return handle; }
std::coroutine_handle<promise_type> handle;
};
class Scheduler {
std::priority_queue<std::pair<int, std::coroutine_handle<>>> _prioTasks;
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
void schedule() {
while(!_prioTasks.empty()) {
auto [prio, task] = _prioTasks.top();
_prioTasks.pop();
task.resume();
if(!task.done()) {
_prioTasks.push(std::make_pair(prio, task));
}
else {
task.destroy();
}
}
}
};
Task createTask(const std::string& name) {
std::cout << name << " start\n";
co_await std::suspend_always();
std::cout << name << " execute\n";
co_await std::suspend_always();
std::cout << name << " finish\n";
}
int main() {
Scheduler scheduler1;
scheduler1.emplace(0, createTask("TaskA").get_handle());
scheduler1.emplace(1, createTask(" TaskB").get_handle());
scheduler1.schedule();
Scheduler scheduler2;
scheduler2.emplace(1, createTask("TaskA").get_handle());
scheduler2.emplace(0, createTask(" TaskB").get_handle());
scheduler2.schedule();
}
-
asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线
-
Unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
-
https://gitee.com/okstar-org/ok-edu-desktop 一个IM通信软件,做IM的可以关注,现在正在做全面整合阶段,开始组建商业团队阶段,年底开始融资,你参加了就快发财了,会的快来
-
https://github.com/hanickadot/cthash 编译期sha算法
github上闲逛看到个44个commit 1800 star的项目,震惊,asteria我看就写的不错,上千次提交了才几百星
可能star主要和曝光度有关了,说明吹牛逼还是有用的朋友们
另外突击检查!手写upper bound,写不出的深蹲十个