C++ 中文周刊 第98期

周刊项目地址

公众号

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

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

提交 issue

20230128 唉,不想上班


文章

还是介绍c++23特性,鉴于中文资料还是比较少的,这里推荐一下

之前咱们也讲过很多次别名引入导致的性能低下,编译期不能充分优化的问题。这里又科普一遍,还有谁不知道?

simd指令教学,感兴趣的可以看看

https://godbolt.org/z/PPsn4KM7Y

简单来说还是embed,借助embed实现concept reflexpr

讨论optional各种构造转换

https://godbolt.org/z/zKbWc7MhE

https://godbolt.org/z/5ajjYs754

直接贴代码了。很复杂,值得研究一波

老文,讲了一些c++20的改进,比如constexpr

#include <iostream>
#include <variant>

constexpr std::variant<int, double> foo(int x) {
    std::variant<int, double> oi { x * 0.1 };
    if (x > 10)
        oi = x;
    return oi;
}

int main() {
   constexpr auto dv = foo(1);
   static_assert(std::holds_alternative<double>(dv));
   constexpr auto iv = foo(100);
   static_assert(std::holds_alternative<int>(iv));
}

再比如经典坑

std::variant<string, bool> x = "abc";   // 此时的x是bool,惊不惊喜

修订在这 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0608r3.html

一些维护大项目的经验,比如ccache加速CICD,项目组织以及重用代码,甚至给了一个cmake模板 https://github.com/tfc/cmake_cpp_example

看着还不错


Raymond chen又发了一堆winrt的文章,我不了解,没细读,这里直接贴出来


对OI感兴趣的可以看看,有几个模板库 比如这个https://github.com/emthrm/cp-library

看个乐

感兴趣可以看看

温故知新

树的几种遍历,针对递归型,什么遍历,就在什么位置调用

如果是非递归用堆栈,前序遍历,那就堆栈里放left/right,后序遍历,那堆栈就放当前节点,中序遍历,那就先放left,放完了也就定好顺序了,访问节点和right就行了

我这里描述的非常模糊,边界条件也没说,不懂的话找个代码看看,我说个大概意思

在执行main之前还会执行别的动作。就是利用这个性质来打印。说实话一般

视频

就是基于cling的一个python内调用c++的工具。cling有句讲句活跃程度堪忧

>>> import cppyy
>>> cppyy.cppdef("""
... class MyClass {
... public:
...     MyClass(int i) : m_data(i) {}
...     virtual ~MyClass() {}
...     virtual int add_int(int i) { return m_data + i; }
...     int m_data;
... };""")
True
>>> from cppyy.gbl import MyClass
>>> m = MyClass(42)
>>> cppyy.cppdef("""
... void say_hello(MyClass* m) {
...     std::cout << "Hello, the number is: " << m->m_data << std::endl;
... }""")
True
>>> MyClass.say_hello = cppyy.gbl.say_hello
>>> m.say_hello()
Hello, the number is: 42
>>> m.m_data = 13
>>> m.say_hello()
Hello, the number is: 13
>>> class PyMyClass(MyClass):
...     def add_int(self, i):  # python side override (CPython only)
...         return self.m_data + 2*i
...
>>> cppyy.cppdef("int callback(MyClass* m, int i) { return m->add_int(i); }")
True
>>> cppyy.gbl.callback(m, 2)             # calls C++ add_int
15
>>> cppyy.gbl.callback(PyMyClass(1), 2)  # calls Python-side override
5

鉴于我们是c++周刊,python代码就不多列举了。大家感兴趣的自己玩玩

我没懂,这种和经典singleton有啥不一样吗,哦singleton全局的

gdb结合tui,结合python等等,没总结,感兴趣的可以看看

ppt在这里 https://www.jonathanmueller.dev/talk/meetingcpp2022/

作者在搞一个脚本语言虚拟机,对比switch,改成jumptable带来很大受益,讲的就是这个调优过程

话说回来,不看视频只看ppt的话 https://meetingcpp.com/mcpp/slides/?year=2022 这里有挺有有意思的

比如这个 https://meetingcpp.com/mcpp/slides/2022/Basic%20usage%20of%20PMRs%20for%20better%20performance8308.pdf

我觉得非常值得一看

我觉得还是直接看ppt吧,非常容易懂,我记得以前godbolt也讲过类似的主题

https://knatten.org/download/Anders%20Schau%20Knatten%20-%20Just%20Enough%20Assembly%20for%20Compiler%20Explorer%20-%20Meeting%20C++%202022.pdf

我贴一下代码

;--- 经典寄存器使用
mov rdi, 2
mov rsi, 4
add rdi, rsi
;结果 rdi 6, rsi 4

;--- 堆栈push/pop

mov rdi, 0xfedcba9876543210
push rdi
pop rsi

;结果 rsi=rdi=0xfedcba9876543210,经典赋值,两条指令,mov要多用一个寄存器/地址中转

;--- 访问地址

mov rsi, 0x3333333333333333
mov rdi, 0x2222222222222222
mov qword ptr [rsp - 8], rsi
add rdi, qword ptr [rsp - 8] 
;这里数组括号就是访问地址,把rsi弄到rsp-8的位置存一下,然后 add访问哪个地址的值,也就是rsi的值,加到rdi
; rdi 0x5555555555555555, rsi不变
; 为什么-8 因为rsi都是64位的寄存器
; byte: rax 8 eax 4 ax 2 al/ah 1

;来一个32位的例子

mov esi, 0x3333333333333333
mov edi, 0x2222222222222222
mov dword ptr [rsp - 8], esi
add edi, dword ptr [rsp - 8] 
;结果就不说了

;再来一个word例子

mov word ptr [rsp-8], 0x1111
mov word ptr [rsp-6], 0x2222
mov word ptr [rsp-4], 0x3333
mov word ptr [rsp-2], 0x4444
lea rdi, [rsp-8]              ; rdi指向rsp-8这个位置了
mov ax, word ptr [rdi+6]      ; ax 0x4444懂吧
mov rsi, 0
mov ax, word ptr [rdi+rsi*2]  ; 0x1111
inc rsi
mov ax, word ptr [rdi+rsi*2]  ; 0x2222

;---- 条件

 mov rsi, 2
 mov rdi, 3
 cmp rdi, rsi ;和j命令配合不满足继续走到jmp
 jg .greater
 mov rax, 0
 jmp .endif
.greater:
 mov rax, 1
.endif:

;---- 循环
; c代码这样
; int rax=0;
; for (int rcx=1;rcx!=3;++rcx) {rax += rcx;}

 mov rax, 0
 mov rcx, 1
.for
 cmp rcx, 3
 je .endfor
 add rax, rcx
 inc rcx
 jmp .for
.endfor

;---- 来个函数
;int sum(int a, int b){ return a + b;}

sum(int, int):
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-4], edi
  mov DWORD PTR [rbp-8], esi
  mov edx, DWORD PTR [rbp-4]
  mov eax, DWORD PTR [rbp-8]
  add eax, edx
  pop rbp
  ret
;能看懂吧,另外优化后肯定能看懂,省掉把入参传到栈上来回折腾的流程了
sum(int, int):
  lea eax, [rdi+rsi]
  ret

再来个例子,调用函数

;int times_two(int i);
;int sum_times_two(int a, int b){    return times_two(a + b);}
;int times_two(int i){    return i*2;}

sum_times_two(int, int):
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], edi
  mov DWORD PTR [rbp-8], esi
  mov edx, DWORD PTR [rbp-4]
  mov eax, DWORD PTR [rbp-8]
  add eax, edx
  mov edi, eax
  call times_two(int) ;看edi的用法
  leave
  ret
times_two(int):
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-4], edi
  mov eax, DWORD PTR [rbp-4]
  add eax, eax
  pop rbp
  ret

开源项目需要人手

新项目介绍/版本更新

工作招聘

目前没看着有啥好工作,尽量别被开吧


本文永久链接

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

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