c++ style guide

风格指南

Google C++ Style Guide

工程中使用统一代码风格

.clang-format

安装工具

1
brew install clang-format

导出指定格式的 .clang-format 配置文件

1
2
clang-format -style=Google -dump-config > .clang-format
clang-format -style=llvm -dump-config > .clang-format

格式化文件

1
2
3
4
5
6
7
8
$ cat t.cpp
#include "iostream"
int main() {
return 0;
}
$ clang-format -style=Google t.cpp
#include "iostream"
int main() { return 0; }

Inplace 格式化文件

1
$ clang-format -style=Google -i t.cpp

在 IDE 中使用 .clang-format

  • CLion 可以识别并应用项目根目录下的 .clang-format 文件,如果没有的话,可以点击右下角的 spaces 信息框,再点击 Enable ClangFormat

image-20231110182136072

c++ 各标准版本特性

版本说明

WG21(The ISO C++ committee)有严格的时间限制,每 3 年推出一版新的标准。最新的版本信息参考 WG21 官网。每个正式标准确定前,会使用草案名(draft),比如 c++1zc++2a

各版本特性

C++ 20(C++2a)

语言特性

库特性

C++ 17(C++1z)

语言特性

库特性

C++ 14(C++1y)

语言特性

库特性

C++ 11(C++0x / C++1x)

语言特性

库特性

参考

读书 - 信息流广告入门

自从开始工作,一致在做推荐相关的事情,做程序化广告交易平台之后,买了《信息流广告入门》这本书。读完有一段时间了,再简单说下,有必要的话再补充。

这本书从产品、投放角度来阐述信息流广告相关的事情,没有聊技术。多是一些广告相关的概念和基础,投放相关的知识。

对于想从事广告投放相关的工作,可以作为入门书籍来读。对于从事广告交易平台相关的技术工作,也可以读下,从产品和投放视角了解广告,里面的一些基础概念,对理解业务还是有帮助的。

整体来说,内容还是比较浅显易懂,适合入门。

小狗钱钱

当利息降到零,我们的投资回报甚至会更丰厚。你要知道:唯一不变的就是变化。

前言

有了足够的金钱,我们能更有尊严地生活,更好地对待自己和他人。

个人化的经验很难效仿,但最基本的真理却能被普遍运用。

追问我们自己到底想要什么样的人生,这需要勇气。

不是因为困难重重,所以我们心生畏惧,而是因为我们心生畏惧,所以事情变得困难重重。

第一章 一只白色的拉布拉多犬

我也认为金钱不是生命中最重要的东西。可如果时时处处都缺钱,那么钱就变得举足轻重了。

只有你自己真正渴望学习,我才能帮得了你。

第二章 梦想存储罐和梦想相册

不是试一试,而是去行动。所谓尝试,只不过是在为失败提前找借口、为自己找退路。

在行动之前,不要评判。不去想象成功的美好,就不能达成目标。精力集中在哪,哪就会开花结果。只不过,大部分人总是把功夫花在并不喜欢的事情上,而不去想自己真正渴望拥有的东西。

注意到了吗?你往往先找各种理由来证明一件事做不成。

钱的多少并不是最关键的,更重要的是,我们拿钱来做什么。

在你展翅飞翔之前,你就必须相信自己一定能到达目的地。你想象得越多,你的愿望就会变得越发强烈。然后,你就会开始寻找机会去实现愿望。吉雅,机会到处都是,但只有去寻找,才能发现。只有拥有强烈的渴望,才会去寻找。而只有不断地去想象,才会拥有强烈的渴望。

第三章 能挣很多钱的男孩达瑞

你都没试过,却总是先想着行不通,这样肯定不会成功啊。

是否自信决定着你是否敢去做某些事情,如果没有这份自信,你就不会开始去做。而如果不动手去做,就什么也不会发生。

要始终去帮助别人解决问题,才能挣到钱。要把精力始终集中在你知道的、能做到的和拥有的东西上。

第四章 堂哥的致富经

不要总是想那些做不成的事情嘛,你得多想想什么能做成。

不能光指望着一份工作,因为它可能比你预想中更快结束,你得抓紧时间接着去寻找下一个工作机会。

第五章 钱钱以前的主人

这正是很多不富裕的人会犯的错误。他们总是有很多十万火急的事情要做,却没有时间考虑真正重要的问题。

第一,即使遇到了困难和问题,也得实施你的计划。如果事情一切顺利,谁都能完成任务,只有在出现真正的问题时,谁强谁弱才能见分晓。只有少部分人能继续坚定不移地贯彻执行他们的计划。而那些特别富有的人,更是擅长在最困难的时候做出最漂亮的成绩。第二,当一切进展顺利时,你也应当坚持做下去。

苦难和问题总是层出不穷,尽管如此,你仍然要每天坚持下去,坚持去做对你的未来意义重大的事情。它们最多花掉你十分钟的时间,能给你带来真正的改变。大部分人总是日复一日地停留在原地。他们总是期待周围环境会为自己发生改变,却忘记了首先应该改变的就是他们自己。

总有千百种事情可能分散你的注意力。

72小时法则。当你决定做一件事情时,必须在 72 小时之内开始行动,否则就很有可能再也不会做了。

第六章 债务-爸爸妈妈犯过的错误

大部分人都认为工作是很艰苦的,是一种负担。其实,只有做自己真正喜欢的事情,才可能真正获得成功。

在我看来,所有的消费贷款都是不明智的。聪明的人只是把以前积攒的财富用于支出。

(针对债务危机)把扣除生活费用后所剩的一半存下来,剩下的一半用于支付消费贷款。最好不要去申请消费贷款。每个月应该尽可能少地偿还贷款。

第七章 拜访金先生

金先生不是个寻常人,做事也总是不同寻常。他不在会别人做什么,他只做自己认为正确的事情。

我也有自己的秘密,所以和我说话的人当然也可以有自己的秘密。

疯狂的目标也不见得就比普通的、微小的目标更难实现。如果你树立了远大的目标,那毫无疑问,你就必须为此付出极大的努力。

为什么你做了喜欢的事情,就不应当获得金钱上的回报呢?正因为你真心付出了,你的工作才有价值。

我自己的习惯是,不论挣到多少钱,都为我的鹅存下收入的 50%,为梦想储蓄罐存下 40%,剩下的 10% 用于开销。

我越是关注自己身上的伤痛,疼痛就会越剧烈。谈论伤痛,就像在伤口上再撒上一把盐。所以,从很多年以前,我就不再抱怨了。

第八章 特伦夫太太的邀请

用最少的还款额度偿还贷款,才是最明智的做法。

第九章 冒险开始了

等待是世界上最愚蠢的事情。

第十一章 爸爸妈妈不明白的事情

仔细观察,幸运只不过是充分准备加上努力工作的结果。

勇敢不是毫不畏惧。勇敢的意思是,一个人尽管心怀恐惧,但仍能克服恐惧向前走

工作本身往往最多只值报酬的一半,另一半价值来源于你的想法和实施这个想法的勇气。

爸爸是个好人,可惜他有个坏毛病,就是总将自己的境况归咎于别人和外部环境,仿佛只有他自己是个牺牲品,而别人都是幸运儿。

笨人只有一次好运,聪明人永远都有好运。

你们总是想着怎么才能暂时应付过去,而更聪明的做法是要找到长期又有效的解决办法啊。

第十二章 特伦夫太太归来

金钱只会留在那些为之做好了准备的人身边,用非法手段得到不义之财的人,反而会过得比没钱时更糟糕。

想要过得幸福而充实,就得先改变自己,钱可不能为他们代劳。

金钱会暴露一个人的品行。金钱就像放大镜一样,它会让你把自己看得更清楚。

以前,我喜欢首先关注自己做不成的事情,现在回更专注于我能做成的事情。这样一来,我就能更多地去寻找解决问题的办法,而不是寻找逃避问题的借口。

也许金钱不是生活中最重要的东西,但如果处处缺钱的话,它就变得无比重要了,生活会为钱所累,人会变得很不正常,会跟身边的人吵架,会觉得挫败沮丧,觉得自己一无是处。

没有人能强迫你做自己不愿做的事情,只有你自己能强迫自己去做。

我生命中最美好的事情之所以发生,都是因为我做了原本不敢做的事情。

最珍贵的礼物都是自己送给自己的。一旦克服了丢面子的恐惧,世界就会对你敞开大门。

能够阻挡你做自己喜欢的事情的,只有恐惧。但是,战胜了恐惧后,你就会成长。

第十三章 严峻的危机

你可以成为这样一个人,有能力帮助别人,也能够让别人信任你,愿意向你求助。

成功会使人骄傲,如果你骄傲自大,就会停止学习,不学习,人就不会成长和进步。

不能在困难面前退缩。对苦难、错误和羞耻的恐惧,毁掉了无数人的生活。

我们的恐惧总是源于对那些不确定的事情的想象,我们越是设想失败的可能性,就会越害怕。一旦你把精力集中在积极的目标上,就不会心生畏惧了。

第十四章 投资俱乐部

我们一致认为,只要学会我们的咒语,就能像变魔法一样从无到有地变出钱来:热爱并渴望金钱;自信,有想法,敢于做自己喜欢的事情;把钱分成三部分,分别用于日常开销、梦想目标和养鹅账户;之后,进行明智的投资;以及享受生活。

投资需要注意三点:应当把钱投资在安全的地方;我们的钱应该生出很多金蛋;我们的投资必须简单易懂。

决定一件东西的价值的唯一要素是,人们愿意支付多少钱来购买它。

不要急着投资,在投资之前,必须先弄清楚自己究竟在做什么。

第十五章 演讲

如果你没有强迫自己,你就永远都不知道自己的能力究竟怎么样。别忘了,最让我们感到骄傲的事情,往往就是那些最难做到的事情。

第十六章 投资俱乐部在行动

一旦计划投资基金,就说明我们打算将一笔钱放在里面至少 10 年。对那些能等待这么长时间的人来说,基金几乎是一种零风险的投资。

筛选优质基金的注意事项:基金应当至少有 10 年的历史;应当是大型的跨国股票基金;找到过去最近 10 年里收益表现最好的基金。

第十七章 爷爷奶奶害怕风险

只有当我们把它卖出的时候,才会有亏损。

当我第一次遇到行情大跌时,反应和你们一模一样。经历过好几次这种所谓的危机,一两年后,市场总会恢复的。每次都是如此。

存折就是个金钱粉碎机。

怎么可以这样随便就下结论呢?下结论之前,至少也看一看我们的投资是怎么回事啊。你们不能因为自己不熟悉,就说这事儿危险哪。

你应该把一部分钱放在绝对无风险的投资中。也得留着备用的资金,这样才能达到分散风险的最佳效果。

第十八章 大冒险的终结

以前他总是怀疑自己能不能独当一面,现在他却知道,只要将自己不喜欢也不擅长的事情交给其他人就行了。

挣来的钱一分为三,其中的 50% 用于养鹅,40% 用于实现我的中短期目标,10% 用于零花。

最重要的是,他始终把钱视为一种再正常和自然不过的事物,在他的影响下,我对金钱的态度也发生了翻天覆地的变化。

重要的是,你是否能够听到并理解我说的话。就像你现在正在写的那本书一样,有些人读到了它,却可能根本听不见它传达的意思,也不会做出任何改变,而另一些人读过后开始学习如何更聪明地和金钱打交道,从而拥有更幸福、更富有的人生。

不要为已失去的东西悲叹,而要对你曾经有由它的时光心存感激。

后记

墙一旦倒塌,视野便会更开阔。

操作系统 - 协程

我们常说的协程是在用户态实现的一种编程范式,严格意义上来说,协程并不属于操作系统的范畴。本质上来说,协程是被协程调度器处理的一段内存数据,他包含协程本身的 Context 信息及我们任务代码的地址信息。

协程和线程的区别

直观上,可以把协程和线程做对比。都有任务调度机制,不过线程是内核级别(抢占式),而协程是用户级别(协作式),所以基于协程的任务切换开销要由于线程。都可以被执行、挂起、恢复,在 IO 密集型的任务中,通常伴随大量的挂起和恢复操作,如果使用协程来实现,可以节省大量内核/用户态切换、线程上下文切换开销。

但是需要注意的是,线程是完全并行的,而协程则不一定,要看协程调度器的实现是如何和线程绑定的,可以做到并发,但不一定能做到并行。对于计算密集型行任务,要充分考虑使用协程是否符合预期。

协程切换同样需要保存协程的运行时上下文信息(栈和寄存器信息)。

协程和函数的区别

  • 协程有自己的上下文(状态、运行指令位置、寄存器信息等)
  • 协程可以被打断,可以主动暂停
  • 协程具备更好的并行、协作能力
  • 协程可以实现 lazy 计算

协程切换过程

参考

protobuf best practice

枚举定义

取值跳过 0

0 是默认值,在 Protobuf 3 中移除了 optional 和 required 关键词,在序列化时没有标记字段是否设置,这会在某些场景带来一些问题。

  • 序列化时跳过为默认值的字段
    • 转换为 Json 时会缺少字段
  • 无法区分未设置和设置为默认值
    • 这两种情况在某些场景下表示完全不同的含义,比如我们用一个字段存储特征,那么这个特征值是默认值和特征不存在是两种情况,不能混为一谈

wireguard

WireGuard

使用步骤

  • 使用 wg genkeywg pubkey 在每个节点创建公钥和私钥
  • 配置
    • 主中继服务器(main relay server)
      • [Interface]
        • 确保在定义服务器可接收的路由地址时,指定整个 VPN 网络的 CIDR 范围
          • Address = 192.0.2.1/24
      • [Peer]
        • 为每个需要加入到 VPN 网络的客户端创建一个 [Peer],使用对应的公钥(public key)
    • 客户端节点(client node)
      • [Interface]
        • 指定单个 IP,不中继流量
          • Address = 192.0.2.3/32
      • [Peer]
        • 为每个可以公开访问的 Peer 创建一个 [Peer]
        • 在定义作为跳板服务器(Bounce Server)的远程对等体时,确保为整个VPN子网指定一个CIDR范围
          • AllowedIPs = 192.0.2.1/24
        • 确保为不中继流量且仅充当简单客户端的远程对等点指定单独的 IP
          • AllowedIPs = 192.0.2.3/32
    • 启动中继节点
      • wg-quick up /full/path/to/wg0.conf
    • 启动客户端节点
      • wg-quick up /full/path/to/wg0.conf

准备

Ubuntu

1
2
3
4
5
6
7
8
$ sudo vim /etc/sysctl.conf
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
$ sudo sysctl -p
$ sudo ufw allow 51820/udp
# 重启 ufw
$ sudo ufw disable
$ sudo ufw enable

配置

字段

  • AllowedIPs : 路由目的网络
1
2
3
4
5
6
7
8
9
# 安装
apt install wireguard -y

# 添加网络接口
ip link add dev wg0 type wireguard
ip address add dev wg0 10.0.2.1/24

# 生成密钥
wg genkey | tee server-private.key | wg pubkey | tee server-public.key

命令

跳板服务器开启中继和转发

1
2
3
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.proxy_arp = 1" >> /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf

生成公钥、私钥

1
2
3
4
5
# generate private key
wg genkey > example.key

# generate public key
wg pubkey < example.key > example.key.pub

启停

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
wg-quick up /full/path/to/wg0.conf
wg-quick down /full/path/to/wg0.conf
# Note: you must specify the absolute path to wg0.conf, relative paths won't work

# start/stop VPN network interface
ip link set wg0 up
ip link set wg0 down

# register/unregister VPN network interface
ip link add dev wg0 type wireguard
ip link delete dev wg0

# register/unregister local VPN address
ip address add dev wg0 192.0.2.3/32
ip address delete dev wg0 192.0.2.3/32

# register/unregister VPN route
ip route add 192.0.2.3/32 dev wg0
ip route delete 192.0.2.3/32 dev wg0

注意

  • 如果一个节点在 NAT 内,在配置远端 Peer 时需要配置 PersistentKeepalive ,这样远端 Peer 才能访问当前 Node
1
2
3
4
5
[Peer]
Endpoint = <ip:port>
PublicKey = <public-key>
AllowedIPs = 10.0.10.1/24
PersistentKeepalive = 25

其他

预设

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# vpn 网络
10.110.10.1/24
# 示例 client 地址
10.110.10.100/24
# 网络接口
ens18
# server 地址
192.168.6.192

# server 端密钥对
PUBLICKEY = HsNTZwNRji31XuNAhx+eMOD8y7CZEeoPEUzZWZXpqg0=
PRIVATEKEY = UHIqicenzBJQ6gnsSHqrvZbEEbjz6NJfl1qc4UgpMm8=

# client 端密钥对
PUBLICKey = KLL6tm7wiU/ouenCktUwThss5Jw9Xr79C+3u3QRnYCQ=
PRIVATEKEY = oNLv6Ntj5iHulThhcCtZ0jYtRV0CTsC4d6rJkWo21FY=

配置模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Interface]
## Address : A private IP address for wg0 interface.
Address = 10.110.10.1/24

## Specify the listening port of WireGuard, I like port 33333, you can change it.
ListenPort = 51820

## A privatekey of the server ( cat /etc/wireguard/server-private.key)
PrivateKey = {private-key-of-server}

## The PostUp will run when the WireGuard Server starts the virtual VPN tunnel.
## The PostDown rules run when the WireGuard Server stops the virtual VPN tunnel.
## Specify the command that allows traffic to leave the server and give the VPN clients access to the Internet.
## Replace enp1s0 = Your-Network-Interface-Name

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o {Your-Network-Interface-Name} -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o {Your-Network-Interface-Name} -j MASQUERADE
1
2
3
4
5
6
7
[Interface]
Address = 10.110.10.1/24
ListenPort = 51820
PrivateKey = UHIqicenzBJQ6gnsSHqrvZbEEbjz6NJfl1qc4UgpMm8=

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens18 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens18 -j MASQUERADE

服务

1
2
3
4
5
6
systemctl start wg-quick@wg0
systemctl enable wg-quick@wg0
systemctl status wg-quick@wg0.service

# 查看状态
wg show wg0

客户端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 重新生成密钥和公钥
[Interface]
PrivateKey = PrivateKey_of_the_Client
Address = IP_UNDER_VPN

[Peer]
###Public of the WireGuard VPN Server
PublicKey = PublicKey_of_the_Server

### IP and Port of the WireGuard VPN Server
Endpoint = IP_of_the_Sever:Port_VPN_of_the_Server

### Allow all traffic
AllowedIPs = 0.0.0.0/0
1
2
3
4
5
6
7
8
9
# 重新生成密钥和公钥
[Interface]
PrivateKey = oNLv6Ntj5iHulThhcCtZ0jYtRV0CTsC4d6rJkWo21FY=
Address = 10.110.10.100/24

[Peer]
PublicKey = HsNTZwNRji31XuNAhx+eMOD8y7CZEeoPEUzZWZXpqg0=
Endpoint = 192.168.6.192:51820
AllowedIPs = 0.0.0.0/0

服务端添加 Peer

1
2
3
4
wg set wg0 peer {client-public-key} allowed-ips 10.110.10.100

# 示例
wg set wg0 peer KLL6tm7wiU/ouenCktUwThss5Jw9Xr79C+3u3QRnYCQ= allowed-ips 10.110.10.100

访问 Local Network

1
2
3
4
5
6
7
8
9
10
PostUp = ufw route allow in on wg0 out on eth0
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = ip6tables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
PreDown = ufw route delete allow in on wg0 out on eth0
PreDown = iptables -D FORWARD -i wg0 -j ACCEPT
PreDown = ip6tables -D FORWARD -i wg0 -j ACCEPT
PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

注意以下配置

1
2
3
4
5
6
7
8
$ sudo vim /etc/sysctl.conf
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
$ sudo sysctl -p
$ sudo ufw allow 51820/udp
# 重启 ufw
$ sudo ufw disable
$ sudo ufw enable

参考

参考 参考二

端口连通性判断

1
2
$ nc -z -v -u 127.0.0.1 51820
Connection to 127.0.0.1 port 51820 [udp/*] succeeded!

Debug

1
$ sudo wg show 

postgresql - startup

命令

登录

1
2
# login
$ psql -U postgres # 指定用户,无密码

用户操作

1
CREATE USER root WITH CREATEDB CREATEUSER PASSWORD 'root';

role 操作

1
2
3
4
CREATE ROLE root WITH LOGIN CREATEDB CREATEROLE SUPERUSER PASSWORD 'root';

# 查看
SELECT * FROM pg_roles;

数据库操作

1
CREATE DATABASE dolphinscheduler;