C++ 中文周刊 第132期

2024-07-30 14:25:13 浏览数 (1)

周刊项目地址

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

简单的规避手段

代码语言:javascript复制
thread_local int* tls = nullptr;

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

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

难受

资讯

标准委员会动态/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?
代码语言:javascript复制
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 看了几个都很没意思

  • • Using-Modern-C-to-Eliminate-Virtual-Functions-Jonathan-Gopel-CppCon-2022

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

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

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

cppnow 2023 PPT放出来了

https://github.com/boostcon/cppnow_presentations_2023

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

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

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

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

  • • Lightning Talk: Global API Injection in C - Ben Deane - CppNow 2023

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

比如这种

代码语言:javascript复制
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初始值

代码语言:javascript复制
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

  • • Under the Hood Assembly, System Calls, and Hardware

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

  • • APPLICATIVE THE FORGOTTEN FUNCTIONAL PATTERN

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

不过到现在我还是不太懂

  • • Using Sender/Receiver For Async Control Flow

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

  • • A Deep Dive Into Dispatching Techniques

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

代码语言:javascript复制
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;
        ...
    }
}

或者

代码语言:javascript复制
while (*ip != bytecode::exit) {
    switch (*ip)
    {
    case bytecode::add:
    ...
    case bytecode::push:
    ... ...
    }
}

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

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

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

  • • compile-time-is-the-new-constexpr

介绍 编译期矩阵计算的

  • • Personal_Log__Where_No_Init_Has_Gone_Before

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

  • • The_Challenges_of_Implementing_the_C_Standard_Library_in_Cpp

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

string_view的妙用

代码语言:javascript复制

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

再比如

代码语言:javascript复制
LIBC_INLINE void write_to_stderr(cpp::string_view msg) {
    __llvm_libc::syscall_impl(SYS_write, 2 /* stderr */,
        msg.data(), msg.size());
}

使用 expect包装errno

代码语言:javascript复制
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

代码语言:javascript复制
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);
    }
…
};
  • • Speeding_Date_Implementing_Fast_Calendar_Algorithms

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

判断闰年

代码语言:javascript复制
return Y % 4 == 0 && (Y % 100 != 0 || Y % 400 == 0);

就可以优化成

代码语言:javascript复制
if (Y % 100 != 0)
    return Y % 4 == 0;
return Y % 400 == 0;

最后一行可以优化成 Y

还可以继续优化成

代码语言:javascript复制
int d = Y % 100 != 0 ? 4 : 16;
return (Y % d) == 0;

还可以优化成

代码语言:javascript复制
int d = Y % 100 != 0 ? 4 : 16;
return (Y & (d - 1)) == 0;

判断最后一天

代码语言:javascript复制
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,重复度高,完全可以改成位运算

代码语言:javascript复制
if (M == 2)
return is_leap(Y) ? 29 : 28;
return 30 | (M ^ (M >> 3));
//return 30 | (9 * M / 8);

后面就看不太懂了

  • • From_Templates_to_Concepts

介绍concept的

  • • Calendrical_Cpp

也是介绍日历的,chrono和date

  • • Nobody_Can_Program_Correctly

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

  • • Data-Oriented Design and Modern C

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

开源项目需要人手

  • • asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线
  • • Unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • • gcc-mcf 懂的都懂

本文永久链接

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

0 人点赞