C++ 中文周刊 2024-03-03 第150期

周刊项目地址

公众号

qq群 点击进入

RSS

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

提交 issue 或评论区留言

本期文章由 黄亮Anthony Amnesia 赞助

最近沸沸扬扬的白宫发文,转向更安全的语言,明示c++不行

除了把NSA之前的观点重新提出来之外,没有任何新东西,

就像个想离婚的在这里埋怨不想过了,死鬼你也不改你看人家xx语言

要我说这就是美帝不行的原因,从上到下都没有耐性我靠

最近很忙视频都没来得及看。后面慢慢补吧,视频可能单独发总结


资讯

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

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-02-21 第242期

本台记者 kenshin报道,visual studio最近更新了非常有用的功能,分析编译时间之类,分析字段内存布局,分析include等等

感兴趣 可以更新一下 What’s New for C++ Developers in Visual Studio 2022 17.9

另外clang gcc有单独的工具,比如ftime-trace,比如 这个

xmake 2.8.7发布

https://github.com/xmake-io/xmake/wiki/Xmake-v2.8.7-released,-Add-cosmocc-toolchain-support,-build%E2%80%90once-run%E2%80%90anywhere

boost新parser正在review中 https://lists.boost.org/Archives/boost/2024/02/255957.php

类似boost spirit,代码在这里 https://github.com/tzlaine/parser

think-cell出了个意见,他们在自己的库里维护了boost spirit,觉得重新造轮子不太合理,详情见 https://www.think-cell.com/en/career/devblog/parsers-vs-unicode

文章

Rage Against The Glue: Beyond Run-Time Media Frameworks with Modern C++

音视频领域有个 M x N问题

不同的media processors 在N种平台上导致api复杂度上升不可维护

考虑一种接口设计方法,让代码更简练

琢磨半天结果是concept + boost pfr之类的检测接口/策略模版

代码在这里

还有一些其他的想法 在这里

constexpr and consteval functions

记住这段代码就行了

// This is a pure compile-time function.
// Any evaluation is fully done at compile-time;
// no runtime code will be generated by the compiler, just like `static_assert`.
consteval size_t strlen_ct(const char* s) {
    size_t n = 0;
    for (; s[n] != '\0'; ++n);
    return n;
}

// This is a pure runtime function, which can only be invoked at runtime.
size_t strlen(const char* s);

// This function can be invoked both at both compile-time and at runtime,
// depending on the context.
constexpr size_t strlen_dual(const char* s) {
    if consteval {
        return strlen_ct(s); // compile-time path
    } else {
        return strlen(s);    // runtime path
    }
}

constexpr最好两种分支都实现,避免意外的问题

How to debug C and C++ programs with rr

其实rr和gdb record差不多,感觉可以用这个文档例子做个视频,这里标记一个TODO

Undefined behavior in C and C++

讲UB产生的场景,以及如何避免,给的方案编译flag ubsan以及换个语言,我谢谢你

Measuring energy usage: regular code vs. SIMD code

lemire新活,只要你的代码足够快,即使是simd这种费电的指令相比而言整体费电也不多

代码在这里

值得复现一下

My late discovery of std::filesystem - Part I

介绍fs相关api,比如遍历之类的,我就不列出来了

How to write unit tests in C++ relying on non-code files?

介绍非代码文件怎么和代码编译到一起的,bazel/cmake都有类似configfile的方法。embed赶紧来吧

为什么用它,用int32 int64之类的类型,reinterpret cast可能会有fpermissive报错,比如


#include <cstdint>

namespace HAL {
class UART {
public:
 explicit UART(std::uint32_t base_address); 
 void write(std::byte byte);
 std::byte read() const;
 // ...
private:
 struct Registers* const registers;
};
}

constexpr std::uint32_t com1_based_address = 0x4002'0000U;

int main() {
 HAL::UART com1{com1_based_address}; 
 com1.write(std::byte{0x5A});
 auto val = com1.read();
 ...
}
namespace {
 struct Registers // layout of hardware UART
{ 
  std::uint32_t status;     // status register
  std::uint32_t data;      // data register
  std::uint32_t baud_rate;    // baud rate register
  ...
  std::uint32_t guard_prescaler; // Guard time and prescaler register
 };
 static_assert(sizeof(Registers) == 40, "Registers struct has padding");
 Registers Mock_registers{};
} 

// doctest
TEST_CASE("UART Construction") {
  constexpr std::uint32_t baud_115k = 0x8b;
  constexpr std::uint32_t b8_np_1stopbit = 0x200c;

  HAL::UART com3 {reinterpret_cast<std::uint32_t>(&Mock_registers)}; // 不行

  CHECK(Mock_registers.baud_rate == baud_115k);
  CHECK(Mock_registers.ctrl_1 == b8_np_1stopbit);
}

但如果把UART构造函数改成intptr就没问题

#include <cstdint>

namespace HAL {
class UART {
public:
 explicit UART(std::uintptr_t base_addr); 
 ...
};
}

// doctest
TEST_CASE("UART Construction") {
  constexpr std::uint32_t baud_115k = 0x8b;
  constexpr std::uint32_t b8_np_1stopbit = 0x200c;

  HAL::UART com3 {reinterpret_cast<std::uintptr_t>(&Mock_registers)};

  CHECK(Mock_registers.baud_rate == baud_115k);
  CHECK(Mock_registers.ctrl_1 == b8_np_1stopbit);
}

为什么不用intptr? 如果涉及到负数移动,或者做差有负数之类的,可以用intptr,其他场景还是uintptr更合适

Introduction To Low Latency Programming: Minimize Branching And Jumping

哥们出书了,120页卖10刀有点贵我靠,书在这里 https://a.co/d/0U6KOfb

这个文章是节选,大概思路就是降低跳转和分支

备注

cmov 描述不对,if简单赋值也能生成cmov, cmov是一个值得展开的话题

variant + visit开销部份场景要比虚函数要好,不要因噎废食

感谢 球猫 Anien 指正

一个样例sss

void processThisString(std::string_view input)
{
  if (input == "production") {
    processProd(input);
  } else if (input == "RC") {
    processRC(input);
  } else if (input == "beta")
    processBeta(input);
  }
}
void processThisString(std::string_view input)
{
  constexpr auto i = 0;
  switch (input[i]) {
    case "production"[i]: processProd(input); break;
    case "RC"[i]: processRC(input); break;
    case "beta"[i]: processBeta(input); break;
  }
}

1 « n vs. 1U « n and a cell phone autofocus problem


#include <stdio.h>

static unsigned long set_bit_a(int bit) {
  return 1 << bit;
}

static unsigned long set_bit_b(int bit) {
  return 1U << bit;
}

int main() {
  printf("sizeof(unsigned long) here: %zd\n", sizeof(unsigned long));

  for (int i = 0; i < 32; ++i) {
    printf("1 << %d : 0x%lx | 0x%lx\n", i, set_bit_a(i), set_bit_b(i));
  }

  return 0;
}

64位机器 31会打印什么?https://gcc.godbolt.org/z/qa3o34hrW

非常幽默

1 << 0 : 0x1 | 0x1
1 << 1 : 0x2 | 0x2
1 << 2 : 0x4 | 0x4
1 << 3 : 0x8 | 0x8
1 << 4 : 0x10 | 0x10
1 << 5 : 0x20 | 0x20
...
1 << 29 : 0x20000000 | 0x20000000
1 << 30 : 0x40000000 | 0x40000000
1 << 31 : 0xffffffff80000000 | 0x80000000

m32并没有这个问题

自底向上理解memory_order

理解理解

Using std::expected from C++23

不会的拖出去


#include <charconv>
#include <expected>
#include <string>
#include <system_error> 
#include <iostream>

std::expected<int, std::string> convertToInt(const std::string& input) {
    int value{};
    auto [ptr, ec] = std::from_chars(input.data(), input.data() + input.size(), value);
    
    if (ec == std::errc())
        return value;

    if (ec == std::errc::invalid_argument)
        return std::unexpected("Invalid number format");
    else if (ec == std::errc::result_out_of_range)
        return std::unexpected("Number out of range");

    return std::unexpected("Unknown conversion error");
}

int main() {
    std::string userInput = "111111111111111";

    auto result = convertToInt(userInput);
    if (result)
        std::cout << "Converted number: " << *result << '\n';
    else
        std::cout << "Error: " << result.error() << '\n';
}

c23有一种模拟的方法

#include <stdio.h>

struct { bool success; int value; } parse(const char* s) {
    if (s == NULL)
       return (typeof(parse(0))) { false, 1};

    return (typeof(parse(0))) { true, 1 };
};

int main() {
   auto r = parse("1");
   if (r.success) {
     printf("%d", r.value);
   }
}

不建议使用

LLVM 中的一致性分析框架详解 之 发散源和指令一致性判断

学点llvm

A story of a very large loop with a long instruction dependency chain

简单来说就是拆循环 loop fission + 降低数据buffur大小,小于l1 cacheline,提升性能

需要复现一下

如何发现是内存子系统的问题(buffer大于cacheline)? 使用likwid 查的。这个实验需要复现一下看看

When an instruction depends on the previous instruction depends on the previous instructions… : long instruction dependency chains and performance

简单来说就是通过 interleave 拆分任务,来加速,这个和loop fission还不太一样,loop fission就是单纯的拆循环,interleave又不同任务分发调度的感觉

Atomics And Concurrency

这个比较基础和cpprefence差不多

The Auto macro

#pragma once

template<class L>
class AtScopeExit {
    L& m_lambda;
public:
    AtScopeExit(L& action) : m_lambda(action) {}
    ~AtScopeExit() noexcept(false) { m_lambda(); }
};

#define TOKEN_PASTEx(x, y) x ## y
#define TOKEN_PASTE(x, y) TOKEN_PASTEx(x, y)

#define Auto_INTERNAL1(lname, aname, ...) \
    auto lname = [&]() { __VA_ARGS__; }; \
    AtScopeExit<decltype(lname)> aname(lname);

#define Auto_INTERNAL2(ctr, ...) \
    Auto_INTERNAL1(TOKEN_PASTE(Auto_func_, ctr), \
        TOKEN_PASTE(Auto_instance_, ctr), __VA_ARGS__)

#define Auto(...) \
    Auto_INTERNAL2(__COUNTER__, __VA_ARGS__)

// 使用

bool Mutate(State *state){
    state->DisableLogging();
    Auto(state->EnableLogging());

    if (!state->AttemptOperation1()) return false;
    if (!state->AttemptOperation2()) return false;
    return true;
}
// 还能递归
void Example()
{
    Auto(
        puts("starting the first Auto");
        Auto(puts("cleaning up in the second Auto"));
        puts("getting ready to clean up in the first Auto");
    );
    puts("doing the main operation");
}

libfork

这个完成度看起来比taro强很多,且融合了很多taskflow点子

比如 taskflow调度

文档

代码

另外就是性能要测试一下,这里标记TODO有空测一下

开源项目介绍


上一期

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