C++ 中文周刊 第132期

周刊项目地址

公众号

qq群 手机qq点击进入

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

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

提交 issue

感谢 Amnesia 不语 高博 Yin YellyHornby 404 赞助

本周内容太少了,加点bug反馈,欢迎各位后台评论投稿遇到的奇怪bug,我发出来征集思路


奇妙的BUG

tls被优化 How to disable clang expression elimination for thread_local variable

新版本clang gcc thread local识别不出 fiber切换的场景,会优化,寄存器暂存,而不是从内存load同步一下

受害者很多,只要用boost context和tls,就可能遇到,比如 https://github.com/userver-framework/userver/issues/242

简单的规避手段

thread_local int* tls = nullptr;

[[gnu::noinline]] int* getTls() {
    asm volatile("");
    return tls;
}

[[gnu::noinline]] void setTls(int* val) {
    asm volatile("");
    tls = val;
}

难受

知乎网友@张威补充 brpc也有类似问题

资讯

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

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-09-27 第221期

riscv如火如荼啊

cppcon 2023 十一开始,我这几天在疯狂看去年的

文章

Did you know that C++26 changed arithmetic overloads of std::to_string and std::to_wstring to use std::format?

int main() {
    setlocale(LC_ALL, "C");
    std::cout << std::to_string(42); // prints 42
    std::cout << std::to_string(.42); // prints 0.42
    std::cout << std::to_string(-1e7); // prints -1e+07
}

XMake:现代化的C++构建工具

xmake确实好用

雾里看花:真正意义上的理解C++模板(Template)

看个乐

《产生式元编程》第一章 宏编程计数引原理

看一乐

c++23中的新功能之十六std::forward_like

给forword查缺补漏的

视频

cppcon 2022 看了几个都很没意思

靠tuple+ typeindex,设计的过于复杂了

感觉有点类似boost::ext:;di

剩下有意思的我攒一攒再发

cppnow 2023 PPT放出来了

https://github.com/boostcon/cppnow_presentations_2023

简单看一眼好玩的,视频太慢了,看不懂再看视频,感觉最近视频有点多看不过来

介绍carbon的好几个,介绍safety的好几个,介绍module的好几个,还有几个抽象的,我都不介绍了

介绍协程的有俩之前提过,视频链接就不贴了感兴趣的youtube搜标题就行

剩下的说实话有意思的也不多

设计思路,使用模板参数作为实现,不同实现拆分开

比如这种

namespace logging {
namespace null {
struct config {
    struct {
        template <level L, typename... Ts>
        // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
        constexpr auto log(Ts &&...) const noexcept -> void {}
    } logger;
};
} // namespace null

template <typename...> inline auto config = null::config{};

template <level L, typename... Ts, typename... TArgs>
static auto log(TArgs &&...args) -> void {
    auto &cfg = config<Ts...>;
    cfg.logger.template log<L>(std::forward<TArgs>(args)...);
}
}

实现放在config里,然后config有不同的初始化的值,其实就相当于CPO定制点

比如想用其他实现,就特化config初始值

namespace my_logger {
struct config {
  struct {
    template <logging::level L, typename... Ts>
    auto log(Ts &&...ts) -> void {
      // log the ts... according to my mechanism
    }
  } logger;
};
}

// use my logger
template <>
inline auto logging::config<> = my_logger::config{};

代码在这 https://github.com/intel/compile-time-init-build/tree/main/include/log

作者的意思是这种思路可以定制系统API,比如write之类的,然后测试使用不同的实现,方便mock

讲了一圈汇编基础知识 ELF VDSO知识,挺有意思的,可以看一乐

这个是讲fmap的。我不懂这个,说实话这玩意在16年那会很热闹

不过到现在我还是不太懂

介绍英伟达那个execution实现 std::exec的例子代码,建议直接看代码不用看视频

这个之前介绍过,就是switch case 或者 bit或模式太难看,想解决办法,比如这种

while (auto header = parse_header(reader)) {
    switch (header.type)
    {
        case header_type::integer:
            parse_integer(reader);
            break;
        case header_type::string:
            parse_string(reader, header.length); break;
        ...
    }
}

或者

while (*ip != bytecode::exit) {
    switch (*ip)
    {
    case bytecode::add:
    ...
    case bytecode::push:
    ... ...
    }
}

case下面不同函数确实不好搞,作者一个函数尾递归调用搞定,期间各种优化benchmark调优

具体就不展开了,感觉这个我说了好几遍了

另外用到了https://github.com/sharkdp/hyperfine来测试命令行IO,挺有意思

介绍 编译期矩阵计算的

这个是介绍编译期hash string 来替换掉二进制中的特殊字符串,避免泄密

这个是llvm实现libc遇到的一些挑战和设计,还算有意思

string_view的妙用


LLVM_LIBC_FUNCTION(char *, getenv, (const char *name)) {
    char **env_ptr = reinterpret_cast<char **>(__llvm_libc::app.envPtr);
    if (name == nullptr || env_ptr == nullptr)
        return nullptr;
    __llvm_libc::cpp::string_view env_var_name(name);
    if (env_var_name.size() == 0)
        return nullptr;
    for (char **env = env_ptr; *env != nullptr; env++) {
        __llvm_libc::cpp::string_view cur(*env);
        if (!cur.starts_with(env_var_name))
            continue;
        if (cur[env_var_name.size()] != '=')
            continue;
        // Remove the name and the equals sign.
        cur.remove_prefix(env_var_name.size() + 1);
        // We know that data is null terminated, so this is safe.
        return const_cast<char *>(cur.data());
    }
    return nullptr;
}

再比如

LIBC_INLINE void write_to_stderr(cpp::string_view msg) {
    __llvm_libc::syscall_impl(SYS_write, 2 /* stderr */,
        msg.data(), msg.size());
}

使用 expect包装errno

template <class T>
using ErrorOr = cpp::expected<T, int>;

class Dir {
    static ErrorOr<Dir *> open(const char *path);
    ErrorOr<struct ::dirent *> read();
    // Returns the error number to indicate the success or failure.
    int close();
};

LLVM_LIBC_FUNCTION(::DIR *, opendir, (const char *name)) {
    auto dir = Dir::open(name);
    if (!dir) {
    libc_errno = dir.error();
        return nullptr;
    }
    return reinterpret_cast<DIR *>(dir.value());
}

使用bit_cast

template <typename T>
class FPBits {
    UIntType bits;
    LIBC_INLINE T get_val() const {
        return cpp::bit_cast<T>(bits);
    }
    LIBC_INLINE void set_val(T value) {
        bits = cpp::bit_cast<UIntType>(value);
    }

};

考虑一下实现日历,有哪些可以优化的地方,开头还能看懂,比如

判断闰年

return Y % 4 == 0 && (Y % 100 != 0 || Y % 400 == 0);

就可以优化成

if (Y % 100 != 0)
    return Y % 4 == 0;
return Y % 400 == 0;

最后一行可以优化成 Y%16

还可以继续优化成

int d = Y % 100 != 0 ? 4 : 16;
return (Y % d) == 0;

还可以优化成

int d = Y % 100 != 0 ? 4 : 16;
return (Y & (d - 1)) == 0;

判断最后一天

if (M == 2) return is_leap(Y) ? 29 : 28;
unsigned last[] = {
31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31
};
return last[M - 1];

由于天数大部分都是 30 31,重复度高,完全可以改成位运算

if (M == 2)
return is_leap(Y) ? 29 : 28;
return 30 | (M ^ (M >> 3));
//return 30 | (9 * M / 8);

后面就看不太懂了

介绍concept的

也是介绍日历的,chrono和date

介绍他们公司遇到的代码问题以及从维护角度如何复现,定位

这个之前也讲过,数据冷热,冷数据用指针指出去indirect_value封装,不需要的话甚至可以不创建,让数据集小点

开源项目需要人手


本文永久链接

如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论

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