如何在两周内学会C++并构建优质的项目

简介 最近因为科研需要,捡起了好几年前大学水平的C++(不忍直视),毫无意外地忘记地一干二净。于是两周后就有了这篇文章,期望能够帮助所有拥有一定编程基础(至少写过一个完整的项目的那种)的同学入门并掌握这一门编程语言。通过阅读这篇文章,你能够学到: 用优质的资源,快速学习C++的语法构成 指针和防止内存泄漏等,较难的需要实战(编码)的内容 C++标准的现代化目录结构 利用CMake来进行跨平台开发/编译,把开发的软件安装到系统中 利用Catch2来编写单元测试,集成到CMake中去 Doxygen自动化生成API文档 Shell脚本与CMake联动,进一步实现自动化(龟速更新中) 跨系统交叉编译,做成Docker镜像(未来用到了再更) 我的开发平台是"macOS Catalina",选择的IDE是"vscode",编译器采用了"g++",在考虑兼容性和新特性之后,我选择了"c++17"版本进行开发。文中许多的资源,是需要梯子才能够访问的,这点请注意。 快速学习C++语法结构 写出你的 “Hello, world!"(VSCODE配置) 首先,让我们从简单难度开始:在你的本地代码仓库中建立一个文件夹,名字随意,创建一个 hello.cc 文件并保存,内容如下(现在不需要去关心这些内容代表什么): #include <iostream> int main() { std::cout << "hello, world!" << std::endl; return 0; } 打开 terminal 以后,你就可以进行手动编译了: # 编译 g++ hello.cc -o hello.o # 运行 ./hello.o 当然,每次进行这样的手动编译,实在繁琐并且无法进行断点调试。在VSCODE中编写C++项目,首先你要安装一个"C/C++“的扩展,然后打开刚才存放 hello.cc 的目录,双击打开 hello.cc 以后,按住 cmd+shift+p 呼出vscode的命令面板,输入 tasks: 索引到 “Tasks: Configure Task”(如图所示)选择编译器,这里我选择了g++作为编译器,之后你能在.vscode目录下发现自动创建出的 “tasks.json”(当然我们要进行修改) 之后选择左侧的调试按钮,创建一个 “launch.json”,附上我的两个配置文件如下: .vscode/task.json { "version": "2.0.0", "tasks": [ { "type": "shell", "label": "C/C++: g++ build active file", "command": "/usr/bin/g++", "args": [ "-std=c++17", "-g", "${file}", "-o", "${fileDirname}/caches/${fileBasenameNoExtension}....

September 20, 2020 · 5 min · Bruce Yip

博客搭建指南(零):基础知识及架构

简介 近期有不少同学提到我写的这个系列写的看不太懂(捂脸.jpg),大概是我没有介绍清楚自己的架构和思路。那么,这篇文章能带给你: 博客和个人服务器的整体架构 域名和现代网页的一些基本常识 服务器和操作系统的选择 注:文章更多的是一些原理和为什么(WHY-TO)我相信你有了这些相关的关键词以后,能够通过搜索引擎找到具体的操作(HOW-TO) 架构及原理 云服务器是什么 首先很容易混淆的概念就是,物理服务器与网页伺服器(大白话) 前者就是一台在远端的带操作系统的电脑,而后者则是在这台电脑上一直跑着的一个软件。 网页伺服器负责接收所有指向到这台电脑的HTTP请求,然后进行处理,返回相应的网页给请求者。从这种意义上来说,你也可以在你的个人电脑上安装一个网页伺服器(例如,Apache,Nginx,IIS,Caddy,等等),然后让你室友来请求你的IP地址,你就可以返回一个标准的网页给TA了。 然而,你的个人电脑不可能保持24小时开机的状态,所以这个时候我们就需要一些商业公司(例如,AWS,Azure,Google Cloud,阿里云,等等)提供的云服务器,我们不再需要关心这台“云电脑”是否会坏,它也能保持一直开机的状态。 现代的云服务器一般都是按小时制来进行结算的,有些提供商也会有包月的套餐选项(包月包年的都是耍流氓,说的就是你,阿里云)。对于我们的需求,一台单核心、1G可运行内存、10G的普通硬盘,就能完全满足了(甚至不如一台老年手机的配置)。 我们需要购买一台云服务器,还要选择一个网页伺服器软件。 关于代理 这是一个相对敏感的话题,所以我只略微提几句。-G-F-W-(不懂就搜索一下)是一个黑盒子,看不到它的代码,我们只能凭借许多人的经历去猜测它的审查机制。 那么为什么我们还能够绕过它呢?因为现代互联网是相对安全的,只要你使用的技术得当,没人能够拆开你的数据包看你到底给接收者发送了什么内容。 可是为什么-G-F-W-还能对各种代理服务器进行封禁操作?因为它钻了现代互联网设计的空子,举个例子:你发送给代理服务器的内容,都是指向到它某个固定的端口(非80,非443)并且它的主端口(80,443)是访问不到任何内容的,再并且它与你的通信数据类型都是非常单一的,那么,你说这台服务器是不是良民?显然不是,-G-F-W-会对你的数据包进行嗅探,会分析你目标服务器的特征,等等。 国家级的安全设施,不是在开玩笑。 国内可以使用的主流手段,从-S-S-演化到-S-S-R-,再到-S-S-R-作者被请去国安局喝茶,最后到现在的-V-2-R-A-Y-,俨然是一部超长的宫斗剧了。前两者早就不好使了,我从2018年开始使用最后者,到现在也没有被封禁过任何一台服务器。你需要记住的是: 无论未来宫斗剧怎么演变,合理的架构设计大于任何混淆协议,因为现代互联网技术,是相对安全的。 域名 你有没有思考过这个问题:当我访问 https://bing.com 的时候,它怎么知道我发起的请求,到底被哪一台真实的物理服务器(电脑)处理并给了我网页呢? 一台真实的物理服务器,它会有自己唯一的一个的全球IP地址(例如:45.77.5.214),这样,我在全球任何一个位置,向这个地址发送消息,都可以定位到这台电脑。所以,你必须要一个第三者,相当于电话号码本,把bing.com查找变成45.77.5.214(这么设计的原因是抽象的数字人们记不住) 我们称bing.com为域名(domain),45.77.5.214为公共的IP地址(public ip address),第三者为DNS服务器(负责把域名翻译成公共IP的)。 那么思路就非常简单了:当你在浏览器里键入bing.com并按下回车键,请求开始,先通过UDP协议向DNS服务器发送请求获得域名的真实IP地址,接着真正的请求通过TCP协议向这个IP地址发送数据包,远端服务器处理好你的数据包,将网页内容再通过原管道塞回给你。 有一些很皮的同学就要问了,那为什么我们在纯粹搭建代理的时候,不能够抛弃域名通过IP地址+443端口的方式来做呢?不行!这个和我们的整体架构有关,先说结论,因为开启HTTPS加密必须需要一张证书,而证书需要用域名来申请! 域名诞生之初,是为了人们能够记住你的网站,而之后域名也被划入了安全性考虑。 架构设计 网页伺服器软件选用的是 Caddy v2,代理选用的是-V-2-R-A-Y-(可能被墙掉了),我们购买了域名通过配置Caddy来自动获取HTTPS证书,以获取TLS加密(例如你打开bing.com,chrome浏览器能看到地址栏最左边有一把锁,说明你很安全)。代理则使用Websocket协议+TLS加密来使它看起来和一般的HTTPS流量特征一致。到达网页伺服器的代理流量,则会被根据请求路径分发到-V-2-R-A-Y-中,由它处理。 上面那一大段出现了一些术语,这里再做一些科普。早期的HTTP协议是无状态的,并且传输时是非加密的,也就是说可能请求会在流到网页伺服器之前就被截获,破译你的消息。想象一下你使用咖啡店里的公共WIFI,在登陆某个网页时被咖啡店里的处于同一网段的某个黑客截获了,那真是太不幸了。 后来设计者们发现这太蠢了,于是就在传输层加入了额外的保障:TLS,它能够保证建立连接的双方是加密通信,并且能够保证信息的完整性。我们现在提到的HTTPS,其实就是 HTTP+TLS 的传输设计,即便你的消息被第三方截获,他也没法进行反向解密(解密需要私钥,而私钥则存在接收者的电脑中)。 另一个HTTP协议的缺点,也来自于它的无状态特性(因为起初的设计是这样:网页伺服器不需要知道到底是谁来请求我,我只要把你请求的内容给你就完了),它把某个文件传输给你以后,就停止工作了,等待下一个请求。所以HTTP协议没法进行状态保持,也就是网页伺服器只能被动接受请求,而没法主动向某个电脑发送一段消息(如果可以的话那简直就是场灾难,黑客可以随意向你发送消息)所以在互联网早期,双向通信的实现非常费劲。 Websocket协议(同理还有类似的HTTP1.1版本中的长连接特性,但我们说不好未来到底是谁获胜)则刚好完美地弥补了这个缺点,你能和代理服务器建立长时间的连接(而不是发完一段数据包就断开了),你和代理服务器的消息能进行双向推送,这不正是我们想要的吗? 这样,我们设计的整个系统,所有数据包(无论是静态网页,还是代理)都是通过HTTPS协议(443端口)流到云服务器的。我们是良民中的良民,因为从头至尾表现的都是一个正常的网站啊! 注:中国移动用户请留意,内部还有一个局域网+防火墙,我没研究过怎么穿越它,请更换联通/电信。 域名与服务器购买 推荐 Google Domain 来购买域名,Google Cloud Platform 来购买服务器,不接受反驳。 理想很丰满,但现实是你一开始如果没有梯子,根本使用不了Google的所有服务,这个时候只能退而求其次,使用 Godaddy + Vultr 这对能用支付宝付款的组合了,但相对来说体验就不是很好。尤其是Vultr机房的整个IP区段(ipv4 + ipv6)全部被 Google Scholar 封杀了,一点办法都没有,这对于做科研的我们并不是一个好消息。 购买了域名以后,你还需要配置域名解析,把它指向到你购买的服务器(公共IP地址),这些网上都有很多教程,不再赘述,实在有问题你可以发邮件给我。 这里再补充一句,Google Cloud 的 us-west(oregon) 节点,其 f1-micro 配置是永久免费的,虽然其配置较低(单核心,614MB内存,10G普通硬盘)但也足够使用,我在上面跑了一个博客+代理+Docker搭建的几个动态应用,也运行良好。这意味着你只需要出流量费就可以了,流向境内的是0....

July 1, 2020 · 1 min · Bruce Yip

从泰坦尼克号谈到浅层神经网络

前言 最近因为课题需要,捡起了很多年前学了但是没怎么应用的机器学习。作为一个经典的算法,逻辑回归目前被作为神经网络诞生前的基础,具有广泛的应用。单层单节点的逻辑回归在“泰坦尼克号生还概率预测”这个问题上表现较为良好(~78%准确度)。本文的在线示例是用大约600条历史上泰坦尼克号上的搭乘者->生还/死亡数据,喂给一个3层的神经网络训练而成。对于以下细节,你或许会感兴趣: 数据集:训练样本627条,测试样本264条 准确度:训练集88%,测试集83%,尚未进行超参数优化(还没来得及做) 模型状况:三层的浅层神经网络,节点数分别为:128(ReLU), 64(ReLU), 1(Sigmoid) 源码:qwezarty/machine-learning-examples 注:在线演示的参数验证并不严格,还请大家手下留情不要用表单工具提交一些奇怪的值(要是把AI玩坏了,她会来找你算账的),另外为了简化输入,我使用下拉选项替代了许多的文本框。好了,现在大家就可以由观看《泰坦尼克号》的经验,有序上船,会不会翻全看造化啦! 在线示例 性别(只接受男/女噢): 男 女 年龄(5-80之间都很合理): 20 21 22 23 24 25 26 27 28 29 30 35 40 45 50 60 70 80 5 10 15 船票等级: 三等舱 一等舱 二等舱 船票费用: 0(偷渡的或工作人员) 5 10 15 30 35 40 50 100 200 船舱位置: 我也不清楚 C G A B D F E 登船港口: 南安普敦, Southampton 瑟堡, Cherbourg 皇后镇,Queenstown 我也不清楚 是否单独一人: 否 是 登船的配偶及亲兄弟/姐妹人数: 1 2 3 4 0 登船的父母/子女人数: 0 1 2 3 5 来吧,决定命运的时候到了! 本AI认为你大约有%的几率生还。 这个模型有什么用? 这是一个典型的“疾病与风险控制”示例,想象一下把数据源替换成“良性/恶性肿瘤与各特征的关系”,例如肿瘤的三维尺寸、发病时长、患者的性别/年龄/遗传疾病史等等,如果你有几百条这样的数据,在良好的调优的前提下,你甚至能达到90%以上的准确度(甚至超过了那些经验丰富的医师,所以有言论说是AI的出现会使医生失业,并不是空穴来风)。...

June 4, 2020 · 1 min · Bruce Yip

Notes of python interface to pythia, the event generator, on macOS

简介 Pythia是基于蒙特卡罗方法制作的Event Generator,最近的一个课题会需要使用到它。它是采用C++编写的,但却通过SWIG支持了Python的调用。其官方的指引写的略微模糊,下面我会以macOS系统+VSCODE编辑器为例,简单讲一下如何实现Python调用。 配置编译选项 我们需要在编译pythia的时候加入额外的选项,以获取python的支持,我们需要获得两个信息: python include path python-config --includes 返回值例如:/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 python path echo $(dirname $(which python))/ 返回值例如:/usr/bin/ 注意,这里最后的"/“是必须的,不然你等等在编译的时候会出错(作者用的直接是+号拼接字符串) compile 最后,在pythia的根目录,我们重新配置再进行编译,将以下命令行里的两个path替换为你上面自己获取到的两个path,最后编译完了记得查看以下log,是否存在error,例如: # 确保你在本地pythia的根目录 ./configure --with-python-include=/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 --with-python-bin=/usr/bin/ # 编译它! make 注:以上方案也适用于Linux,注意下如果你电脑里同时存在了python(2.7)和python3(3.7),例如在macOS里就是这样,则要选择其中一个版本进行编译。但要注意,作者默认是采用了"python"作为调用指令的(而不是python3,所以很不友好),若想使用python3,则要先将python3制作一个软链接为python,然后将–with-python-bin指向到这个软连接的位置。(我更建议大家直接使用python,无论是版本2.7还是版本3.7都是可以的) 智能补全与DEBUG auto-completion 在VSCODE里,我们是通过pylint来进行代码补全的。我们需要在~/.bashrc里增加两个环境变量,使用你喜欢的编辑器打开它,在最后增加以下内容(注:将"your_pythia_lib_path"替换为你的pythia目录下,lib文件夹的绝对路径,例如: $HOME/Code/clang/pythia8244/lib): # added by pythia, the event generator PREFIX_LIB="your_pythia_lib_path" export PYTHONPATH=$PYTHONPATH:$PREFIX_LIB export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$PREFIX_LIB 最后在终端里重新加载~/.bashrc即可(所有开着的终端都需要,或者把所有终端重启),最后使用vscode打开pythia根目录,在example目录下存在文件:main01.py,尝试运行它并且测试以下智能补全是否可用。 source ~/.bashrc 一般而言到这里就可以了,如果依然不能够使用pylint的智能补全,不妨在~/.pylintrc里增加以下内容: [MASTER] init-hook='import sys; sys.path.append("your_pythia_lib_path")' debug 由于本身作者提供的python interface不是原生的python代码,所以给debug和变量监视带来了一些困难。在打好断点,进入debug以后,我们可以在debug console里使用dir(object)方法来查看所有object里提供的attribute和method,例如dir(pythia.event[0])等等。

May 8, 2020 · 1 min · Bruce Yip

浙江大学有线网和树莓派路由器(二)

简介 在设置完无线热点以后,其实在宿舍里wifi信道干扰严重,主要是体现在极高的丢包率(over 50%)和延迟上。如果你的设备刚好支持通过以太网(有线)连接的话,可以和我一样购买usb->rj45的有线网卡给树莓派进行扩展。 注意!这不是一个step-by-step的教程,而是作为系列一的补充启到抛砖引玉的作用,所以请不要copy-paste,也不要做全文件的替换! 网卡选择 我为rpi3选择了rtl8152芯片的usb有线网卡,它能够免驱在rasbian(debian10)下工作,非常方便。这里多说一句,对于rpi4以下机型请选择百兆usb网卡,因为受限于usb2.0的通道速度,千兆网卡根本发挥不出它的性能,并且会存在兼容性问题。如果是rpi4以上机型,通道改为了usb3.0那么可以选择rtl8153芯片(网上说也免驱,我没有测试)。 在购买得到有线网卡以后,接上以后可以通过lsusb来查看rasbian是否成功识别了它,以下是rtl8152芯片的样例输出: Bus 001 Device 006: ID 0bda:8152 Realtek Semiconductor Corp. RTL8152 Fast Ethernet Adapter 组网 方案设想 我购买了两个usb有线网卡,算上树莓派自身的无线和有线,那么总计有:eth0, eth1, eth2, wlan0,四个物理网卡。我组网设想的方案是将eth0作为wan口,然后搭建一个br0的网桥,将eth1, eth2, wlan0桥接在一起,为br0启用dhcp服务。最后,在完成浙大l2tp拨号后,通过iptables为ppp0设置nat(这一步其实在系列一里已经完成了)。 具体实现 首先更新软件包列表后安装依赖:bridge-util,我们要利用brctl建立网桥和管理br0中的interfaces(自己用apt安装即可)。 # 建立br0网桥 brctl addbr br0 有些不太一样的是,之前我们在/etc/dhcpcd.conf中是为wlan0设置了静态ip的,此时我们要将设备名改为br0,即: interface br0 static ip_address=192.168.11.1/24 其次是在/etc/dnsmasq.conf中,设备名也要修改为br0,即: interface=br0 dhcp-range=192.168.11.10,192.168.11.30,255.255.255.0,24h 最后,也要告诉hostapd使用br0网桥,即在/etc/hostapd/hostapd.conf中,要追加一行: bridge=br0 OK,所有配置文件修改完毕,这里我建议大家先重启树莓派以让所有配置生效。重启完毕后,使用ifconfig查看应该会看到多出了一个br0的interface(同时你应该还要看到你的usb网卡,就是eth1)。那么先根据系列一提供的脚本拨号,让pi能够上网(这时会多出一个ppp0的interface),接着我们就要开始进行网络桥接: brctl addif br0 eth1 # 若你没有第二张usb网卡,那么这行不要执行 brctl addif br0 eth2 至此,此时给你的设备接上网线,设备上设置dhcp自动获取ip地址,你就可以通过有线上网啦! 其他技术细节 开机自动组建网桥 每次手动都要组建网桥是非常麻烦的一件事,这部分我还在研究。难点主要是会牵扯网卡启动顺序,所以需要寻找一种合适的方案。此部分留空,后续我会更新。 参考资料 Differences between /etc/dhcpcd.conf and /etc/network/interfaces? Linux Advanced Routing & Traffic Control HOWTO

September 13, 2019 · 1 min · Bruce Yip