C++17 std::variant 简单使用

std::variant应当是用来替代union来使用的,后者从C时代就有了,但是它缺乏类型安全的保障。boost库很早之前就有了实现(其实从C++11开始,感觉boost不知道多少东西被借鉴走了)。std::variant的类长这个样子: template <class… Types> class variant; Usage 使用起来也是比较方便的 std::variant<int, double> v1; v1 = 1; // activate the "int" member assert(v1.index() == 0); assert(std::get<0>(v1) == 1); v1 = 3.14; // activate the "double" member assert(v1.index() == 1); assert(std::get<1>(v1) == 3.14); assert(std::get<double>(v1) == 3.14); assert(std::holds_alternative<int>(v1) == false); assert(std::holds_alternative<double>(v1) == true); assert(std::get_if<int>(&v1) == nullptr); assert(*std::get_if<double>(&v1) == 3.14); 上面代码里的几个好用的constexpr函数:…

C++17 std::generate的使用

std::generate是“遍历——执行”工具函数的一种 #include <algorithm> #include <iostream> #include <string> #include <vector> int main() { std::vector<std::string> v(4); std::generate(v.begin(), v.end(), [i=0]() mutable { return ++i % 2 ? "hello" : "world"; }); for (auto&& s : v) { std::cout << s << " "; } return 0; } 上面的函数输出是hello world hello world . std::generate(beginIt, endIt, generator)函数接受起止的迭代器以及一个生成方法。

C++17 新特性—— if constexpr

看代码说话 template <typename Iterator> auto distance(Iterator begin, Iterator end) { using Traits = std::iterator_traits<Iterator>; if constexpr (std::is_base_of_v<std::random_access_iterator_tag, typename Traits::iterator_category>) { return end – begin; } else { auto result = typename Traits::difference_type(); for (auto it = begin; it != end; ++it) { ++result; } return result; } } 上面的代码用来判断两个iterator之间的距离。主要逻辑是判断iterator是否是可随机访问的iterator然后作分支处理,可以随机访问的迭代器直接把iterator相减。在constexpr if语句中,条件的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。有意思的一点是,被舍弃语句中的…

std::unique_ptr 关于智能指针的种种

这个域名也是突发奇想买到的,作为unique-ptr.com,为了对得起这个名儿,那就说一下智能指针算了。 前传。裸指针和auto_ptr 裸指针就是高度自治(要自己管)的指针,在符合基本法(误)的前提下,使用裸指针的人可以为所欲为。裸指针的一个问题是,它有点不符合现代C++内存管理的指导思想,使用者必须对它的生命周期负责,否则有可能发生: 内存泄漏(memory leak):如果new一大片内存但是由于代码复杂到头来忘了释放,就有可能耗尽内存资源;内存泄漏还有一种情况:如果申请内存做事情做了一半抛出了异常——代码跑到了exception的花括号里之后,原来申请的内存直接没办法拿到并且释放; 悬挂指针(dangling reference):指针已经被删除了,但其内存仍然在使用中;另外还有双重删除(double delete)的问题,当程序的某部分要删除一个已经被删除的指针时,即可出现这种情况;还有一种,指针所指的内存已经失效了(比如出了作用域了),但是后面的代码还用着指针; 应该是为了缓解这类问题,C++ 98标准搞出来了个智能指针:auto_ptr。相当于加了一个“管家”,在对象的内部保存那个裸指针,在管家析构的时候自动释放裸指针的内容,如果赋值的话那就把裸指针的所有权转交给新的管家。但是由于移动(move)语义是C++11才有的,auto_ptr在实现的时候,如果在两个智能指针对象之间作赋值(operator=)或者拷贝构造,它的参数都是非const类型的。为啥呢?因为它是会调用original_auto_ptr.release()方法拿到原始裸指针。这个有点无奈的操作是为了实现指针所指对象的“占有权”管理:两个auto_ptr不允许管理同一个目标裸指针。在“拷贝”的时候,相当于做了“主权移交”。 Copying an auto_ptr copies the pointer and transfers ownership to thedestination: both copy construction and copy assignment of auto_ptrmodify their right hand arguments, and the “copy” is not equal to theoriginal.— https://en.cppreference.com/w/cpp/memory/auto_ptr 这种操作并不符合常人对“拷贝“的理解,更像是”剪切“操作。且正是因为这个问题,切不可把auto_ptr放到stl容器里面,否则会得到各种编译错误。典型案例是这个:https://stackoverflow.com/questions/111478/why-is-it-wrong-to-use-stdauto-ptr-with-standard-containers另外由于析构的时候是用delete,auto_ptr里也不能放指针数组(那种需要用delete[]). unique_ptr 不同于auto_ptr,unique_ptr 的赋值运算符只接受典型地由 std::move 生成的右值。刚才说了,“右值”是C++11移动语义的产物,它比较好地解决了主权移交时的尴尬问题。这怎么说呢,一个智能指针的命运啊,当然要靠自我奋斗,但是也要考虑到历史的行程。回顾看之前说的裸指针的两个问题: 内存泄漏:由于析构函数里会delete管理的裸指针对象,所以在跑出对象作用域的时候,编译器会帮我们解决好问题,不用操心了; 悬挂指针:缓解,但不能完全避免。 一个例子是 #include <iostream> #include <memory>…

因子 收益率 因子暴露 因子载荷 什么东东?

作为非金融科班的学生,发现有一些术语其实看上去不知所以然,但是理解了之后就会感叹一下: 这tm不就是xxx么~ 记得刚开始搞量化的时候,经常碰到的词汇就是各种因子,以及因子暴露(factor exposure)、因子载荷(factor loading)。然后我就去百度知乎Google搜啊,立马出来一大堆长篇大论告诉你它的由来,各种多因子模型。窃以为对于刚入门的我,最想知道的就是这个东西是什么意思,然后才是它背后的模型。 那么问题来了 因子暴露(aka. 因子载荷)是什么?因子暴露就是因子载荷。假设你的投资组合里有p只股票,并且你有m个因子,那么想象因子暴露是一个p * m的矩阵——每只股票在每个因子上的因子值。打个比方,如果你的因子是32个行业分类,那么这个矩阵的元素非0即1——1代表股票属于这个行业. 如果假定一只股票只能属于一个行业,那么这个矩阵的每一行都只可能有一个1,其余都是0.这个东西一般是归一化或者说标准化过的值,不然会无法做统计分析。 那么,这东西能拿来做什么? 结构化风险模型的假设是,投资组合的收益率是可以由下列公式解释 r = X * b + u 如果依旧假设有p只股票,m个因子,投资窗口时间长是l天其中 r是投资组合收益,p*l矩阵 X是因子暴露,p*m矩阵 b是因子收益率,m*l矩阵 u是特异收益(又叫残差收益),相当于前面X*b不能解释的一部分,p*l矩阵简而言之就是一个投资组合的收益可以被几个因子来解释,解释不了的部分另归另说。 如果假定个股残差收益是互不相关的,那么 比如我假定股票的收益(r)是由不同的行业因子决定的,那么只要有不同行业ETF的收益率(上面的b),我就可以拿来做回归分析得知股票对应的行业(上面的X); 再如假定股票的收益(r)和股票的Barra风格因子值(X)是已知的,那么也可以通过回归分析的方法拿到每个风格因子的收益率(b); 题外话:感觉这个有点像期权定价里的implied volatility和realized volatility。如果是从underlying的价格用Black-Scholes公式算出来的叫隐含波动率,而已实现波动率可以通过观测期权价得到。上面通过ETF收益率算出来的因子暴露有点类似,其实股票的行业归属也可以从基本面的数据里拿到……

记一次gtest EXPECT_DEATH遭遇sigsegv的debug过程

the crash 实习生小哥上周突然发现单元测试在dev机器上跑不起来了,–verbose一看发现是gtest报错Failure death test: 现象其实是所有写了EXPECT_DEATH的单元测试都挂掉了,感觉问题并不简单~ 问了一下边上的几位最近代码有什么变动,一说是用了个新的计算库。 gdb gdb一波,除看代码发现这个测试的原理是起一个子进程,然后把stderr写到一个临时文件里,子进程退出后,从临时文件里读取log信息,和预期值比对。发现第一现象是stderr没抓到,结果在找stderr为什么没抓到的邪路上走了很久,发现根本没产生stderr:/tmp下面写的文件,大小是0字节。 然后继续gdb,由于是要跟踪子进程,要把选项打开: set detach-on-fork off set follow-fork-mode child run了一下,看到了一点端倪:其实在fork之后进程就sigsegv了,而且居然是在动态加载glibc里的chdir函数过程中挂的。查了一下,机器上的glibc已经至少一年没有动过了,因此基本排除是glibc自己有问题。 然后开始各种翻来覆去的看,先看了几个local变量,发现一切都显得那么完美,根本没有任何内存值被搞烂的迹象。接着只能看挂掉的现场了,info registers看到寄存器的值如下: 突然发现红框处的rsp地址有点太“整”了,google了一下,确认这个地址应该是个用户态栈地址的下界。然后查看一下当前的指令,发现是一条push %ebx,也就是说还准备压栈————挂掉的清晰一些了,其实是栈地址空间不够了,也就是传说中的stack overflow。这也是为什么gtest能够看到程序挂掉,但是拿不到预期的错误信息的原因。 the stack overflow 那么,为什么栈空间不够了呢?翻了一下frame 0里的$rsp值,发现在0x7fc0附近,和刚才看到顶上的$rsp=0x7000相差了4032,也就是估计这个程序栈总的大小在4kb。查了一下ulimit -s有32767,诶? 于是接着看代码,发现gtest-death-test.cc里的做法, ExecDeathTestSpawnChild()里写道 mmap一个stack,它的大小是getpagesize() // ==4KB clone一个新的线程来跑测试例,用的这个新创建出来的stack 真相大白了。解决方法是mmap的时候分配多一点的栈空间。至于为什么到了上周才触发,大概是因为.so又多了几个吧~ 后记 当然,其实这个bug早就有fix了,只是我是后来通过搜”ExecDeathTestChildMain stack overflow”这个关键词才找到的。之前搜了半天EXPECT_DEATH没什么结果~相关的PR有: https://github.com/google/googletest/pull/1274 https://github.com/google/googletest/pull/2276 所以说thirdparty code还是要偶尔更新一下的呀~

ASUS RT-AC1900P RT-AC68U 强行刷写Merlin方法

最新的华硕固件(至少是AC68系列)已经开始阻止非官方固件的刷入,具体表现是能够上传固件,但是路由器重启之后其实什么都没发生变化,也并没有看到什么提示。对应这种情况,其实可以利用ASUS Firmware Restoration Tool强行刷写固件,也就是在恢复模式下更新固件。鄙人亲自试验表明,原有的系统配置在用这个工具强刷之后并不会改变(甚至Mesh节点都能继续连接)。 连入救援模式并上传固件的方法,参考官方教程。主要步骤就是: 拔除路由器电源;把路由器LAN口用网线接入到电脑上,电脑上手工配置有线网卡的IP地址为192.168.1.10,子网掩码为255.255.255.0其余留空; 按下RESET键的同时接入路由器电源,保持RESET按下状态直到路由器的电源灯开始有规律闪烁; 打开下面的工具,直接上传Merlin固件; 上传成功后会自动重启的; 固件更新程序下载(需要Windows系统):链接:https://pan.baidu.com/s/1zApE2OiOq7mX6bGgWGpNvA 提取码:3idq

联通8元全国流量王保号套餐变更指南

起因是有了另外一张流量卡, 所以目前的冰激凌套餐也就不需要了,为了更换这个套餐,最终还是去了实体营业厅办理,好在一次成功,遂发个分享。众所周知联通早已取消了所有5元低保套餐,什么小天神之类的早已无法办理,因为她们不属于“在售套餐”了。目前联通最低消费的是这个4G全国流量王8元套餐,内容如下: 全国流量王8元套餐详情: 30分钟语音 全国流量200MB 套餐外 全国流量:执行流量放心用,即按照0.1元/MB累计至10元,不再收费,直至1GB,按照10元/GB计费 全国语音:0.15元/分钟 国内短、彩信:0.1元/条赠送来电显示;全国被叫免费 10010客服——失败 之前看这篇文章有先例,可以不走线下途径办理,所以打电话一试。打10010客服,人工服务后,对方明确表示有这个套餐,但是电话客服给的权限只能申请最低19元的套餐,再往下不行了。好说歹说回复我“帮我这边申请一下,不一定能成功”。然后过了3天,根本没有回访电话,于是我觉得走非投诉途径的话不去实体店是解决不了问题的。 自营售后营业厅——成功 务必前往自营的联通营业厅。当然跟业务员沟通需要技巧:业务员面露难色跟我说她能帮我把其他的阻止我变更套餐的东西退了,但是需要我自己打电话去10018或10010申请。鄙人打10018直接无反应,然后10010人工坐席全忙——这是为啥呢——现在想来可能是业务员把我的高增值业务退订光后,我就挪到在她们电话客服优先级队列的最底部了。然后我一边等人工坐席一边继续跟业务员argue,说之前打人工客服问过,对方明确表示一定要到营业厅办理,不信可以查电话客服记录。无语了1分钟后,业务员终于点击了鼠标完成了套餐变更,之前“没有权限”的说法也默默消失了~事实证明自有营业厅的客服人员是完全可以当场办理这个套餐变更的,只是她不想那么轻易把这个套餐变更的坏KPI挂在自己头上罢了。现在想来,如果当场发送CXXZ查询携转,并且以携号转网要挟,是不是态度会稍微好一点。 建议 结合以往经历,想要成功变更套餐,两条路: 工信部投诉:https://dxss.miit.gov.cn/ 前往自有营业厅,不要怂,直接怼

Windows 10 HpyerV OpenWRT 软路由安装 + OpenClash 安装 全教程

本教程详细说明如何在Windows10下经由Hyper-V技术安装OpenWRT软路由。物理机器配置如下: Intel Celeron J1900 4GB DDR3 4口千兆网卡淘宝上这种配置的机器遍地都是,J1900对付软路由绰绰有余了,其实跑Win10浏览网页也不在话下~ 施工结果如下图: I. Hyper-V虚拟交换机配置 安装前请确保你的 CPU 支持硬件虚拟化(Hyper-V)技术并且已通过BIOS启用 启用Windows的Hyper-V功能 进入“控制面板”,然后依次点击 程序 – 程序和功能 点击「启用或关闭 Windows 功能」,打开 Windows 功能管理窗口,并勾选「Hyper-V」,点击“确定” 配置虚拟交换机 打开开始菜单,搜索或找到“Hyper-V管理器”,点击右侧栏里的“虚拟交换机管理器”链接。本人的物理机器有4个网口,配置的目标是网口1连入互联网,网口2、网口3、网口4连入下属设备。我们首先创建用于外部网络的虚拟交换机,这个交换机相当于你路由器的 WAN 接口,用于将路由器连接到外部网络: 点击“新建虚拟网络交换机”; 右侧给他起个名字,我是叫它ToExternal,或者随便叫什么“外部网络”也都行; 下面的“连接类型”选中“外部网络”,然后选择你的物理网口1对应的设备名称。注意这里需要勾选“允许管理操作系统共享此网络适配器”; 点击“应用”而后需要增加一个内部网络适配器,是用于给虚拟化宿主机提供来自软路由的网络的。 接下来给余下的准备做路由器LAN口的网口2、网口3、网口4创建虚拟交换机。这边千万注意,选择“外部网络”并选定对应的物理网口后,不要勾选“允许管理操作系统共享此网络适配器”。这些网口,可以命名为LAN1, LAN2, LAN3之类。 创建OpenWRT虚拟机 虚拟机配置 首先需要找一个x86_64的OpenWRT固件,这个网上多半可以搜索到。如果实在有点懒,可以找这里或者这里或者这里。请注意,部分固件是需要转换成Hyper-V能够识别的格式的,自行搜索下载“StarWind V2V Image Converter”这个软件转换成VHDX格式即可。 下载完成后 打开 Hyper-V 管理器,点击「新建」-「虚拟机」,将会打开一个新的窗口。虚拟机的名称可以随意设置。 第二步会让选择虚拟机的代数,如果你下载的是uefi固件(一般名称里有uefi或gpt字样),可以选择第二代;否则谨慎起见选择第一代; 分配内存,个人分配了512MB固定内存,并开启动态内存允许分配到1GB。一般认为512M差不多了,甚至有说256都行了; 为虚拟机分配第一个网络适配器,选择之前创建的「内部网络」;这一步其实是向虚拟机增加了第一个网口,openwrt底下它会被默认分配为eth0; 为虚拟机创建硬盘,需要选择之前下载或者转换好的 OpenWRT 硬盘映像; 完成初始化后,先不开机,点选右侧栏里的“设置”继续配置这个虚拟机: 点选“添加硬件”——网络适配器,将之前添加的所有外部网口依次加入,这边规定一下顺序,首先是那个通往外部互联网的网口1对应的,其次是剩下三个打算做LAN口的;注意这边添加网口的顺序影响到OpenWRT里eth的序号,按照这样设计,eth1对应的是我们接入互联网的WAN口,eth2-4对应的是未来的LAN口; 如上图,点击所有添加的网口签名的“+”号——高级功能:勾选“启用MAC地址欺骗”。不开启此功能将导致之后软路由下的设备无法上网。 随后扩展硬盘 点击「IDE…