C++ 中文周刊 第140期

周刊项目地址

公众号

qq群 手机qq点击进入

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

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

提交 issue

最近在找工作准备面试题,更新可能有些拖沓,见谅

本周内容比较少

本期文章由 YellowHornby HNY 不语 黄亮Anthony 赞助


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-11-29 第230期

文章/视频

能返回值优化,不move

定位到 mmap_sem 互斥锁冲突,查找来源

哪里会有mmap?

逐个排除定位到是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能做什么文章?

各种硬件系统还有自己的其他设计,利用bit,这里就不展开了

这波报菜名大部分都没什么接触的机会。

考虑自定义的场景

重新考虑单例实现

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();
}

开源项目介绍

互动环节

github上闲逛看到个44个commit 1800 star的项目,震惊,asteria我看就写的不错,上千次提交了才几百星

可能star主要和曝光度有关了,说明吹牛逼还是有用的朋友们

另外突击检查!手写upper bound,写不出的深蹲十个

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