CovScript 4来啦!从3.4版本开始,ECS将随运行时一同发行

经过了近两年的Preview后,CovScript 4(Extended Covariant Script, ECS)终于逐渐成体系了。目前Cov4主要是由三部分组成,分别为编译器(ecs)、语法扩展(*.ecsx)和支持库(ecs, ecs_parser, ecs_generator, ecs_bootstrap)。事实上,整个CovScript 4的生态都构建在CovScript 3的基础上。

由于CovScript 4并不是刚需,因此为了降低运行时安装包的体积,我们采用了在运行时中附带一个简单的ECS启动器,这个启动器将扫描当前环境中是否存在ECS Bootstrap环境,只有通过CSPKG安装了这个环境才会启动真正的ECS编译器。

安装ECS Bootstrap环境的方法也很简单,只需要在命令行中运行:

> cspkg install ecs_bootstrap --yes

通过CSPKG部署ECS的好处有很多。除了上面提到的原因外,由于ECS目前依旧处于Preview状态,通过CSPKG部署还能大大降低用户更新的复杂度,当我们发布补丁时,用户只需要在命令行中运行下面命令即可更新本地的支持库:

> cspkg upgrade --yes

希望大家喜欢~

关于Modern C++中Universal Reference的一些理解和谬误

Universal Reference只是一种技巧,并不是一种C++中内建的语法糖。实际上所谓Universal Reference本身只是模板推导、引用折叠以及类型退化共同构成的一种编程范式。

通用引用是否就是形如T&&的引用?

不是。通用引用成立的非常重要的条件之一就是模板类型推导。T必须是一个独立的、构成推导条件的模板类型参数。比如:

通用引用正确示例

template<typename T>
void uniref(T &&dat) {
// TODO
}

通用引用错误示例

template<typename T>
struct test {
void uniref(T &&dat) {
// TODO
}
};

通用引用的推导规则是怎样的?

首先需要介绍的概念是引用折叠。引用折叠本身也是存在于模板类型推导的过程中

引用折叠仅存在于使用通用引用承载左值时。大家都知道左值引用的形式为T&,若套用在通用引用的形式中,就会变成(T&)&&,这时外侧的&&会被自动忽略掉。这个行为被称为引用折叠。

其次是类型退化。退化的具体规则比较复杂,我们在这里仅介绍在通用引用中的类型退化。事实上之所以推荐在传递参数时使用通用引用,就是因为通用引用中发生的类型退化几乎是所有传参方式中最小的。

当通用引用承载右值时,注意这里是右值而不是右值引用,T&&会退化为平凡参数,或者说是退化为传值(Pass by value)。

最后是最简单的类型匹配。对于本身就是右值引用的参数,这时发生的是最简单的类型匹配。如传入int&&,通用引用的T&&将构成对这个类型的全匹配,因此不会发生任何多余的修饰。

也就是说,通用引用的推导规则可总结为下表:

参数左值左值引用右值右值引用
修饰折叠折叠退化全匹配
T&&T&T&TT&&

常见谬误

通用引用能保留参数的原有类型

并不能,具体可见上表中的推导规则。通用引用常用于实现完美转发,而完美转发依赖std::forward对类型进行还原。

若不进行还原,对于传入的右值很容易被判定为左值。这是传值带来的附加效果。事实上这里在函数返回之前的确是一个左值。

右值引用就是通用引用

长得像罢了

更多问题,可以在评论中回复。

智锐科创首次参展Web全栈大会,创始人兼会长李登淳在会上发布 Covariant Script 3.3

2019年11月16日,四川大学计算机学院分团委旗下的智锐科创计算机协会首次参加在成都市天府新区菁蓉汇举办的Web全栈大会,创始人兼会长李登淳在会上发布了智锐科创核心产品Covariant Script 3.3编程语言。

这是智锐科创计算机协会自2018年成立以来首次以正式身份参展此类行业会议。来自西南交通大学的曾韬同学作为智锐科创计算机协会的校外学术顾问、Covariant Script编程语言的核心开发者与李登淳共同代表智锐科创参加此次会议。

图为李登淳和曾韬在展位上的合影
图为李登淳和曾韬为咨询者进行讲解和展示

展览期间,李登淳代表协会与多家企业代表进行了深入的交流,与麦麦养老、图灵文化等公司达成了初步合作意向,进一步扩大了智锐科创计算机协会以及Covariant Script编程语言的知名度和在相关社区中的影响力。

除常规参展外,11月17日,智锐科创计算机协会利用会间茶歇,以Lightning Talk(闪电演讲)的形式举办了最新的Covariant Script 3.3产品发布会,向数百名专业开发者讲解了Covariant Script编程语言的理念以及核心竞争力,重点发布了全新的CSbuild编译套件以及Covariant Script在线开发环境(http://dev.covariant.cn)。

图为李登淳在发布会上讲解Covariant Script编程语言的理念
图为李登淳在发布会上讲解Covariant Script编程语言的优势
图为李登淳在发布会上展示业界领先的CSBuild构建系统
图为李登淳在发布会上展示新上线的在线开发环境

Covariant Script 3.3(代号:穿山甲,Manis Pentadactyla)是智锐科创计算机协会启动新产品研发后发布的第一个稳定版本。自2019年9月以来,智锐科创开发团队在进行新产品研发的过程中发现了现有版本的一些缺陷,因此团队决定暂缓新产品研发,并用近两个月的时间对现有版本进行了一次颇为彻底的重构。此次重构给Covariant Script带来了大量新特性,包括:支持捕捉操作系统中断、更完善的函数式编程支持、基于OSAL(操作系统抽象层)实现的大量系统API支持以及修复包括CSVE-2019-10-01和CSVE-2019-11-01在内的部分缺陷。

得益于Covariant Script 3.3更加完善的系统API,同时发布的还有迭代开发半年之久的全新CSBuild编译套件。这是一个通用化的C/C++编译套件,用户只需使用Covariant Script标准语法编写一份配置文件,CSBuild编译套件即可通过检测系统环境实现软件的快速构建。

此外,发布会上还特别介绍了全场的压轴戏——Covariant Script在线开发环境。智锐科创为此专门开发了一个Covariant Script沙箱,将危险的系统调用全部屏蔽。最重要的是,作为智锐科创计算机协会对前端开发的第一次尝试,这款开发环境更是引来了在场开发者的注意,更有开发者愿意帮助智锐科创开发出更完善的前端页面。

图为发布会后李登淳与曾韬在会场合影

此次参展并召开发布会是智锐科创计算机协会的首次尝试,为以后组织和参与更大规模的公共活动打下了坚实的基础。

此次活动由四川大学计算机学院提供资金和平台支持、成都freeCodeCamp和西南交通大学提供帮助。

怎样学习计算机程序设计

最近很多同学在协会跟着我们学习C++编程语言,遇到了一些问题

首先我们需要确定的几个事实:

  1. C++难不难?难!不然也不会有这么多人转向Java等语言,但这并不代表大家学不会C++。以本文的大多数读者四川大学学生为例,能考上985重点本科院校的同学,本身智商和数理基础是绝对没问题的
  2. 为什么学校的C++这么简单?因为学校教的内容不仅严重落后于工业界,其内容的深度和广度都非常欠缺
  3. C++是不是已经开始被淘汰?不是!C++只是随着计算机科学的发展逐渐收缩至其更专精的范围内
  4. C++更新标准(17/20)有没有必要?没必要!最新的标准至今还没有出正式提案,C++17则依旧属于Modern C++的范畴
  5. 以后工作中C++能不能用的到?当然能!目前C++就业主要是走系统软件和服务器后端,本科打好基础非常重要

这里必须强调的是,我们的课程难度是高于计算机专业课程的,不是普及计算机知识的科普课程。但这并不意味着你学不会,除非你连初中生都不如

那么问题出在哪?

学习方式有问题。

我这里就不想老调重弹应试教育害死人之类的,但我这里必须再次强调的是,无论你以前多牛逼,无论是算法打了多少奖,或是高考考了多少分,只要你写不出来我们要求的程序,你就是弟弟!扔掉以前的坏习惯,走出舒适区!

首先的要求就是要把Modern C++当作一门新的语言来学习,忘记你之前的所有好习惯坏习惯,千万不要看目录某个概念很熟悉就跳!过!去!了!

其次就是养成自己解决问题的习惯。有问题怎么办?扒搜索引擎!泡论坛!问同学!别动不动就找老李私聊,老李也有自己的生活,老李很忙!

再就是大多数同学都会遇到的问题,就是觉得没有头绪,觉得特别难,针对这个问题,我需要特别强调:

  1. 不要打破沙锅问到底!计算机这玩意本身就是抽象的,在你的思维成长到能够理解所有层面抽象之前,死记住就行了,千万别揪着一个问题不放
  2. 如果你遇到了一个困难,应当庆幸!因为如果一个人一直做觉得很简单的事情,那是不可能进步的。遇到困难,说明突破困难就能进步了
  3. 再巨的大佬也是从小白成长起来的,心态远比能力更重要

如果对一个问题实在是没有头绪,怎么办?

  1. 找一个列表,列出来自己需要实现的功能有哪些特点?尝试归纳总结
  2. 不要一口吃个大胖子,尝试先实现这个问题中最简单的部分
  3. 看看别人怎么做的?GitHub是个非常有用的示例程序库

最重要的,千万不要惧怕犯错。希望大家能仔细品味为什么是Learn by doing,这里面这个doing到底是在做什么。

我是怎么到今天的?一个词,头铁。实际上我做的事情在很多人看来是“不够聪明“的,我在刚开始学程序的时候就是头铁,啥都不会直接啃C++。很多人就说我傻,为什么不学Python,不学Visual Basic,不是更简单吗?但如今这个傻子自己写出了一门和Python能比一比的语言,而那群曾经嘲笑我的人可能还只是会写点Python小程序吧。

对比我初学程序时的环境,请大家不要再抱怨,自己多花点精力,挑战一下自己的极限。实在有问题想不清楚,还有我们在背后支持,你们已经比我们初学时要幸福一百倍了,千万不要身在福中不知福。

好耶!是小对象优化

论静态优化在C++程序设计中的应用

四川大学计算机学院 李登淳

说到小对象优化,今年开始跟着我上课的朋友可能有一点印象,这是因为我在课程计划中提到了如何实现小对象优化,但后来由于课程进度等原因反正就是没讲,今天就说一下什么是小对象优化

自计算机诞生以来静态和动态就是非常矛盾的一对存在,静态的东西速度快,动态的东西功能强,因此静态化就是程序优化中非常重要的一个方面。今天我就打算利用任意容器(any)来讨论一下怎样给动态的东西进行静态优化

任意容器的原理和实现

任意容器在C++17标准中被纳入STL的范畴,这在一定程度上也凸显了任意容器这一原理简单且实用的工具的重要性

任意容器的原理说起来也非常简单,即类型擦除(Type Erasing),利用面向对象编程中的抽象方法将具体的类型信息全部“擦除”,具体来说就是将所有的类型全部抽象为一个Object

但这个时候就引出一个问题:为了达到这种效果,派生类对象往往会在堆上分配

在堆上分配就带来了两个显著的问题:

  • 首先是频繁分配带来的内存碎片问题
  • 其次是堆内存分配本身就是低性能的

其实这里内存碎片带来的主要问题其实还是性能,也就是说我们现在还是最关心一个问题就是性能

堆内存分配的性能优化

一般来说常用的堆内存分配性能优化手段有以下几种:

  1. 内存池,在大型项目中比较常见,也就是说自己维护内存的分配
  2. 缓冲池,预缓存一部分内存提供给IO热点,在CovScript 3的Any中得到了应用
  3. 缓式求值(Lazy Evaluation),利用写时复制(Copy On Write)等手段减少不必要的内存分配

这几种优化手段的优点显而易见,但也都有其缺点:

  1. 内存池往往对分配算法要求较高,且一次性会占用过多系统内存
  2. IO热点难以判断,可能会在IO不频繁的场景下反过来拖累性能
  3. 缓式求值传统上使用引用计数器和句柄实现,引用计数器会造成额外的性能开销,而句柄实现的间接内存访问也会破坏编译器的内联优化,在Modern C++中移动语义在一定程度上替代了缓式求值的地位

所以,最好的性能优化方案,其实是避免堆内存分配。

老李又开始说废话了,如果能用栈内存,谁想用堆内存啊!!!

曲线救国之小对象优化:引论

既然堆内存不好玩,为什么不搞点栈内存玩呢?

问题:能不能在栈内存上搞动态内存分配?

在这里请读者回顾一下C++中Placement New的相关语法

也就是说,C++可以在一块特定的内存上构造任意对象,这块内存既然可以是堆内存,当然也可以是栈内存,那么我们只需要拿到一块堆内存就可以了

但说到这里很多朋友就要开始挠头了,获取栈内存可以用malloc,堆内存用什么?

答案是数组

用数组的原因显而易见,单个变量所占用的堆内存往往大小有限

在这里我们常使用unsigned char作为数组的基本类型单位,因为unsigned char是C++中占用内存最小的内建类型,也就是八个字节,因此我们可以用unsigned char*合法的表示任何C++中的内存地址。

现在我们拿到了栈内存,也知道怎么往栈内存里面塞东西,那么接下来呢?

曲线救国之小对象优化:时间与空间的权衡

栈内存看起来好哇!那为什么大家不把整个程序的数据都存储在栈内存里面呢?

首先,栈内存一旦分配,大小是不能改变的(此处不考虑VLA变长数组)

其次,很多情况下内存的拷贝性能是比较差的,因此要考虑栈内存对对象大小的影响

再说深一点,过大的对象会导致Cache Miss,这样反而会得不偿失

什么是Cache Miss?

现代CPU往往通过多级缓存的方式提高内存访问的性能,然而缓存的大小是十分有限的,因此过大的对象可能导致CPU无法通过缓存加速其访问,因此会一定程度上影响性能

到这里我们就需要考虑一个恒久的话题了:怎样权衡时间和空间

较大的对象我们只能扔到堆里,从而避免浪费宝贵的栈空间

而较小的对象,我们就可以将其存储在栈里

但这个阈值怎么样来确定呢?是不是一股脑把所有的数据全部存储在栈上就能达到最佳性能?

过小的阈值是不是没有实际意义?毕竟如果装不了什么东西,到最后还是要扔到堆上

过大的阈值会不会浪费内存空间?毕竟大多数情况下大家都会选择将较大的内存块扔到堆里面

根据大量的测试,我发现3*sizeof(void*)是最合适的大小,巧合的是STL也选择了这个阈值

这个结果是综合了内存对齐和平均情况下内存的占用情况而定的,在实际使用中读者可以尝试自己调整一下阈值看看效果

曲线救国之小对象优化:实现

传统情况下any只需存储一个Object基类指针即可,引入小对象优化就带来了很多问题

  1. 存储小对象的Buffer应该放在哪里?
  2. 数据回收时如何处理不同的回收语义?
  3. 小对象复制时能不能直接按位复制?
  4. 怎样处理移动语义?

这里让我们一一进行讨论

平静中的不平凡:记2019年夏UC Berkeley游学之行

硅谷,在我们这群计科学生心中,可以说是布达拉宫之于藏传佛教教徒的存在。其他人我不敢说,至少我个人是及其期待趁早去硅谷“朝圣”的。但期待归期待,19岁这趟美国之行的确是完全出乎意料——本以为最好情况是大学毕业时能去自己玩一趟。不过话说回来,自从进入大学之后,学校通过“双特生“特殊培养计划给予了我一大堆特殊待遇,我也借此走上了一条奇幻的不归路,似乎这样想也没啥特别出乎意料的。Anyway,稀里糊涂的通过了申请面试后,我终于加入了由四十位各路神仙组成的硅谷之行小分队。

首先横在我们面前的第一座大山就是美国签证。因为父母工作原因,我之前根本没出过国,连护照都是2019年春节时才刚办的,俗称“白本“。看了一些博客后吓了一身冷汗,我们申请签证的那段时间中美贸易战打得正酣,再加上我是个白本,因此对于签证这件事我个人非常紧张。爸妈倒是不以为然,觉得我们是去带动美国GDP,难道美国人还拦着不成?最后当然是出乎意料的顺利,由一位据说很小的时候就在美国自己穷游过的学长带着,我们全程都没看见面签官,只说了一句”Goodbye“就顺利全体通过了签证,唯一比较可惜的是只拿到了一年签,大概下次去美国的时候还需要再办一次。

时光飞逝。办完签证后,转眼间我们度过了熬夜爆肝的期末以及烈日炎炎的军训,终于盼来了暑假。但就像新婚的小媳妇不敢进洞房一样,面对即将到来的完全陌生的美利坚之行,我脑中更多的是焦虑而不是欣喜。收拾好行李,躺在宿舍的床上,半夜未眠,满脑子都是之前看的各种攻略和注意事项。

出发前,江安的夜空

凌晨,起床简单吃了些零食,立刻马不停蹄的前往机场、办理乘机手续、安检、出关、候机、登机。在飞机上渡过无聊的11个小时后,顺利抵达了San Francisco国际机场。一切都非常顺利,就连自己之前一直担心的工地英语也发挥了巨大的作用。

顺利抵达美利坚

在项目方安排的宾馆安顿下后,除了倒时差的不适感外,更多的是扑面而来的新鲜感。美国的一切都是完全陌生的、完全没有经历过的,这与在国内到一个很远的城市旅游不同,甚至与在亚洲国家旅行不同,这是一个文化、地域和人种都截然不同的地方。

来到美国的第一餐是经典的麦当劳,一群中国学生站在点餐台前不知所措,原本信誓旦旦地说自己英语很好的同学这时也哑然无声,不知道的还以为中国人占领了这家UC Berkeley旁的麦当劳店。最后还是胆子大的同学尝试与店员交流,点了第一份餐,同学们纷纷跟随他点了一堆相同的餐食——这在后来的14天里面十分常见。店员往往摆出一副“Fine”的表情,手忙脚乱地处理一大堆完全相同的订单——我们去的很多店似乎从来没有一次性接待这么多点同样食物的客人。这大概也是中国特色了吧。

在美国的第一餐充分反映出理论和实践的差距:哪怕你在中国能多么流利地使用英语,真正落地在纯正的英语环境中后,都免不了变成工地英语。

我们到达那天是旧金山当地周五上午,后来还有一波同学是当天晚上到达的。由于紧接着就是周末,因此我们的课会在之后的周一正式开始。第二天,也就是周六,我们来到了此次美国之行的第一站:Silicon Valley

硅谷是一个神奇的地方。这里并不是一个像深圳一样遍布摩天大楼的地方,而是铺满了其貌不扬的普通小楼,最高大概也不过三四层,穿插在一片片绿色的树林中。

硅谷一角,被晒得无精打采的狗趴在车窗边上

我们在硅谷游览了Stanford University和Google Plex,也算是了结了多年来的期待,完成了“朝圣”的愿望,或者说不通过VPN物理直连某404网站的愿望。唯一的不足是,我们在山景城Shoreline Lake划船的时候不小心把船搁浅了,还弄湿了下半身。

把船开到岸上的俩乐观哥们合影留念
在Stanford University前的草坪上假装写代码
物理直连某不存在网站,顺带朝圣

第三天,我们来到了此次美国之行的第二站:加州海岸。我们先是驱车两小时从旧金山沿着一号公路来到了卡梅尔小镇,然后又一起去了著名的十七里湾(17 Miles),一路感受着加州瑰丽的自然风光和崎岖的山路,直呼后悔没带单反。好在手机的拍照功能足够强大,帮我记录下了这一路美丽的自然风光。

卡梅尔小镇保留了较为原汁原味的美国生活,富足而安逸
在十七里湾的”The Bird Rock”处请朋友拍下的照片

经过两天的旅行,是时候安顿下来学些知识了。第四天,我们来到了此次美国之行的第三站,也是最重要的一站,我们将在这里学习十天的课程。因为宾馆就在UC Berkeley的旁边,我们一行人便步行前往。UC Berkeley的校园修建在山丘上,爬起来颇为费力,配合加州盛情的阳光,待我们到达教室所在的Faculty Club时,全身已经浸透了汗水。

UC Berkeley标志性的钟楼

学习的过程并不总是枯燥无味的。美国的教学方法可以说与国内截然不同,但巧合的却与我在高中社团时推行的教学方式类似,我不仅没有不适应,反而乐在其中。学习之余,我们乘坐地铁游览了旧金山市中心,参观了旧金山市政厅。除此之外,宾馆的老板娘——华裔,武汉人——考虑到我们在异乡吃得不舒服,帮我们准备了一次集体火锅,这大概是我在异国他乡感受过的最温暖的一件事了。

放学后,我们乘地铁到旧金山市中心游览
异国他乡的火锅,以及一群可爱的人

一周的学习之后,我们再次踏上了旅行的路程。我们去了著名的九曲花街、渔人码头、金门大桥和Sausalito小镇,虽说整体有点俗套,但来都来了,为什么不去看看呢?

九曲花街,实际上只有五个弯,整条街的花还是看起来很震撼的
渔人码头,因为之前没看攻略,没看到最经典的地方,有些可惜
金门大桥,第一次如此接近在各种电影中频繁出现的经典场景
Sausalito小镇,没排队吃上有名的冰淇淋,隔着旧金山湾能隐约看到旧金山市区

再往后,就是紧张的结业仪式了。我们每个小组必须完成教授布置的任务,并进行集体展示。我们大多数人都熬到了半夜两三点钟,当然最终的结果也是令人欣喜的。

Faculty Club, UC Berkeley, San Francisco
当我第一次坐在回家的航班上,看见脚下逐渐远去的美洲大陆时,我不禁感慨我辈之幸运。我的父辈都没有踏出过国门,而我却能在20岁之前来到美利坚。这也宣告着此次UC Berkeley游学之行,顺利结束了

十四天的美国之行短暂而充满了意义,我将其称之为“平静中的不平凡”。为什么这么说呢?我们没学到什么实实在在的知识,也没看遍整个美国西海岸瑰丽的自然风景,或许与美国较慢的生活节奏有关,整个旅途都是十分平静的,令人心无旁骛。但这平静的旅程又是及其不平凡的,我看到了我们国家与世界第一强国的差距,也看到了差距在不断缩小,更看到了我们国家新时代的年轻人为了祖国未来的发展而不懈奋斗、奋进、学习。这其中由观察、体会与反思引发的思考,以及由思考带来的思维方式的转变,才是此次美国之行带给我们最宝贵的财富。

使用CNI组成宏编写CovScript扩展

概念介绍

CNI是什么?

CNI,全称为C++ Native Interface,是Covariant Script与C++之间进行交互的抽象接口,几乎支持所有能被调用的东西

CNI组成宏是什么?

CNI组成宏是CNI标准扩展的一部分,旨在用简单易用的宏定义代替繁杂的 CNI 声明

CNI标准扩展又是什么?

C/C++ Native Interface Standard Extension,即CNI标准扩展,是Covariant Script解释器扩展的一部分,旨在降低中低复杂度的Covariant Script语言扩展的编写难度

让我们开始吧

必需头文件

#include <covscript/dll.hpp>

Covariant Script扩展头文件

#include <covscript/cni.hpp>

CNI标准扩展头文件

CNI根名称空间(CNI Root Namespace)

在CNI标准扩展中,我们首先需要接触到的第一个概念就是CNI根名称空间

Covariant Script在引入扩展时,会将扩展中的内容映射为Covariant Script语言中的名称空间,因此CNI根名称空间就是这种映射在C++中的表现

事实上,由于需要建立C++与Covariant Script扩展系统之间的联系,CNI根名称空间会自动完成一些必需工作。因此,我们几乎所有的操作,都必须在CNI根名称空间中进行

声明CNI根名称空间

CNI_ROOT_NAMESPACE {
    // C++代码
}

类似于C++的名称空间声明,在关键的CNI_ROOT_NAMESPACE宏后只需紧跟花括号组成的语句块即可

CNI根名称空间对应着C++中的名称空间名cni_root_namespace

CNI函数(CNI Function)

CNI函数名字叫做函数,实际上是一种C++函数到Covariant Script函数的映射

CNI函数是CNI组成宏的一部分,可以说是整个CNI标准扩展中唯一一个真正和CNI产生联系的部分,其他部分只能说是在配合CNI的工作

CNI系统是Covariant Script扩展系统的多个子系统之一,其功能为构建CNI抽象层以完成Covariant Script函数到C++函数调用的转换工作,具体原理我们这里就不再深究

声明CNI函数

// 声明C++函数
CNI(函数名)

我们只需要先在当前名称空间(必须是CNI名称空间)中声明C++函数,然后再在其后追加一句CNI组成宏,即可完成CNI函数的声明

Hello,world!

到这里我们已经介绍了最基础的CNI组成宏用法,下面就是喜闻乐见的实践环节了

示例代码

// CovScript头文件
#include <covscript/dll.hpp>
#include <covscript/cni.hpp>
// STL输入输出库
#include <iostream>
// CNI根名称空间
CNI_ROOT_NAMESPACE {
    // C++函数声明
    void test() {
        std::cout << "Hello,world!" << std::endl;
    }
    // CNI函数声明
    CNI(test)
}

结合我们之前介绍的知识,能不能看懂这段代码呢?

编译为Covariant Script扩展

由于开发环境的多样,我们这里不再阐述Covariant Script SDK的安装方法

本教程基于基于Debian Linux的发行版,使用Windows的朋友推荐先使用Windows Subsystem Linux进行开发

首先是安装SDK,请在root环境中运行以下代码或使用sudo执行

# wget http://mirrors.covariant.cn/covscript/covscript-amd64.deb
# dpkg -i ./covscript-amd64.deb

然后,切换到工作目录,即你的代码所在目录,执行以下指令

# g++ -std=c++14 --shared -fPIC -O3 源代码.cpp -o 扩展名.cse -lcovscript

执行完毕后,会在当前目录生成一个文件名为”扩展名.cse”的Covariant Script扩展,接下来执行Covariant Script REPL验证

# cs_repl
Covariant Script Programming Language Interpreter REPL
Version: 3.2.0 Psephurus gladius(Stable) Build 8
Copyright (C) 2019 Michael Lee. All rights reserved.
Please visit http://covscript.org/ for more information.
> import test
> test.test()

程序应在终端输出“Hello,world!”,如下图

这便代表我们的扩展编写正确,Covariant Script调用了扩展中的C++函数

模板元编程之类型萃取(Type Traits)中匹配规则的推导方法

现我们希望在编译期判定

  1. 一个类型是否为常量类型
  2. 一个类型是否为引用类型
  3. 一个类型是否为模板类型

那么如何去做呢?

首先是写出泛匹配,泛匹配的结果一定为失败(false)。在本例中的三个需求都是针对单一类型的萃取,所以泛匹配只需要写一个参数即可:

template<typename> struct is_xxxx {
static constexpr bool result = false;
};

在这里,模板参数的参数名被省略,只需把xxxx改成对应的易读名即可,如is_constant_type、is_reference_type、is_template_class。所有的单一类型萃取都可以使用这个泛匹配,只需根据需要更改result的形式即可。比如在这里我们只需要判断是不是某种类型,所以我们使用bool,相应的特化匹配也只需要写一个即可。

接下来就要开始写特化匹配了。特化匹配一般比较难写,我们在这里分成四步:

  1. 写出最终要匹配的类型的一阶推导式
  2. 补齐对应的参数名和相关语法
  3. 写出对应的模板参数列表
  4. 完成特化匹配

一:写出最终要匹配的类型的一阶推导式

推导式的形式一般是:

元素 -> 语法描述

那么推导式的作用就是描述使用“语法描述”分解“元素”的方式,我们将这种方式成为文法。编译器会根据推导式描述的文法对程序进行解析,这种解析的过程称为匹配。

一阶推导式仅涉及到一次匹配,一般比较好写,但以后为了解决更复杂的问题我们可能要写高阶推导式甚至递归推导式,其中递归推导式涉及到消除无限递归的问题,我们以后会讲。

在这里,对应三个例子,有三个一阶推导式:

  1. ConstT -> const T
  2. RefT -> T& | T&&
  3. TemplateClass -> T<…>

注意,“…”指的是任意参数。后两个一阶推导式理论上是递归推导式,但我们不关心接下来的递归结果,所以也可以看作一阶推导。

问题:请列出下面几个类型符合的推导式以及推导结果

  1. const int&
  2. const std::map<std::string, std::size_t>

答案:

  1. 符合ConstT和RefT两个推导式,推导结果分别是T=int&、T=const int
  2. 符合ConstT和TemplateClass两个推导式,推导结果分别是T=std::map<std::string, std::size_t>、T=const std::map

二:补齐对应的参数名和相关语法

一般来说,需要补齐的有以下几个情况:

  1. 任意参数,需要用模板参数包表示出来
  2. 嵌套匹配,需要加上传递给嵌套匹配的参数

在这里需要补齐的只有第三个,补齐后的形式为:

TemplateClass  ->  T<X...>

三:写出对应的模板参数列表

这一步就比较简单,只需按照模板参数的语法规则对照写就可以了:

  1. template<typename T>
  2. template<typename T>
  3. template<template<typename…ArgsT>class T, typename…X>

其中,第三个模板参数列表还可以简化:

template<template<typename...>class T, typename...X>

为什么可以简化?因为这里ArgsT并没有实际意义,仅为了标记模板类参数的模板参数列表。

四:完成特化匹配

到这里我们只需要将我们完成的部分填入下面的模板:

模板参数列表 struct is_xxxx<推导式语法描述> {
static constexpr bool result = true;
};

在这里,既然是特化匹配,匹配结果必然是成功(true);除了填入完成的部分,还要把xxxx改成与泛匹配对应的易读名;像第二个判断类型是否为引用的特化匹配因为存在两个情况所以需要写两个特化匹配。

所以我们最终完成的代码如下:

// 一个类型是否为常量类型
template <typename T>
struct is_constant_type
{
static constexpr bool result = false;
};
template <typename T>
struct is_constant_type<const T>
{
static constexpr bool result = true;
};
// 一个类型是否为引用类型
template <typename T>
struct is_reference_type
{
static constexpr bool result = false;
};
template <typename T>
struct is_reference_type<T &>
{
static constexpr bool result = true;
};
template <typename T>
struct is_reference_type<T &&>
{
static constexpr bool result = true;
};
// 一个类型是否为模板类型
template <typename T>
struct is_template_class
{
static constexpr bool result = false;
};
template <template <typename... ArgsT> class T, typename... X>
struct is_template_class<T<X...>>
{
static constexpr bool result = true;
};

此代码符合C++11标准

浅析中国崛起之必然

四川大学计算机学院 李登淳

中国是一个什么样的国家?对于我们来说,中国是我们的祖国、我们的家园,是代表着我们文化的符号,是我们思想深处灵魂的依靠。对于亚洲各国来说,中国是又爱又恨的邻居,是无法忽略的重要区域大国;对于西方各国来说,中国是拥有五千年历史的神秘国度,他地大物博、人口众多,他的人民勤劳勇敢、吃苦耐劳。西方人的是身边总是围绕着他们又爱又恨无孔不入的中国制造。

一、优秀文化铸就了中国的精神脊梁

中国是四大文明古国中唯一一个存活至今且完整保留了自己优秀文化的国家。就像世间万物一样,漫长的历史对于中国来说也是具有两面性的,在一定程度上造成了近代的闭关锁国。

傲慢是一个文明最危险的符号,纵观世界,历史上的帝国覆灭最终均可以总结为傲慢。在我们的国家,正是因为傲慢使得清朝的皇帝选择了自高自傲、闭关锁国,最终造成了中国近代与世界主流发展的脱节。但我认为,中国近代的丧权辱国给每一个中国人留下了深刻的痛,从而在一定程度上推动了中国近代彻底的革新。为什么这么说?我们可以看一下儒家文化圈中的日本和我们熟悉的邻居印度。先说日本,日本与中国的文化十分相似,受益于明治维新完成了工业革命,但之后由于资源匮乏演变为军国主义转而输出武力,在一战二战中均为重要的组成部分。二战之后日本实力被大大削弱,虽然受益于经济全球化发展出比较完整的工业体系,但由于体量太小难以维持,最终在20世纪末爆发经济危机,至今一蹶不振。

再说印度,受益于英国殖民的历史,印度在1949年时拥有比我们好得多的工业基础,同为拥有悠久历史的文明古国,应当发展的比中国更好,至少与中国类似才对。但事实则是中国成为了世界工厂、工业总产值位列世界第一、是当今联合国安理会五大常任理事国,在各项指标上远超印度,除了人口。

印度为什么没能崛起呢?没有经历过现代工业国家必须的土地改革;没有行之有效的中央集权政治制度;没有消除阶级限制难以解放大量的劳动力;没有高效的教育制度以提供现代工业需要的高素质工人;诸如此类。

中国在近代完成了很多非常伟大的改革,长达几十年的革命、动荡和战争几乎完全清除了中国的阶级旧势力。土地改革解放了大量农村劳动力,激发了广大劳动者的劳动热情;文化大革命和文青上山下乡完成了世俗化和教育的普及;义务教育培养了大量高素质的工业人口;强有力的中央集权保证了政令畅通和地区之间的亲密高效协作;苏联在建国初期的援助使得中国快速完成了工业化。正是由于这些伟大的改革给我们国家打下了坚实的基础,才能在40年前改革开放时引爆了发展的引擎,最终成就了近代经济奇迹。

那么上述的各项改革,为什么在中国能够完成呢?正是基于我们国家悠久的历史留下的政治智慧以及近代丧权辱国引发的中国人的觉醒。我们独有的儒家文化决定了我们对教育的重视,自古以来的家国情怀,以及“王侯将相宁有种乎”和“水可载舟,亦可覆舟”的政治觉醒。这正是漫长历史两面性的正面部分,不可否认的促进了当代中国的复兴。

但为什么和中国文化类似的日本没能崛起呢?这就不得不提到日本的先天缺陷:国土太小、资源匮乏,表现出来就是对于一些社会问题难以内部消化,只能选择转移内部矛盾,即战争,或者崩溃,如经济危机。但中国作为一个国土辽阔的大国,能够在内部消化整个产业链,任何问题在中国都有很大的缓冲空间。西方国家认为,中国是一个“试图伪装成国家的文明”,这也在侧面反映出中国本身的多样性。

综上所述,在亚洲各国中,中国无论在过去还是将来都是不容置疑的顶尖力量。

二、中国特色社会主义是中国崛起的必然

在全球化的今天,西方国家由于在工业革命中占据了先机从而称霸世界长达百年之久,中国的崛起无法避开西方国家的遏制和打击。上一个能够挑战美国霸权的国家–苏联,已逐渐消失在历史的视线中。那么中国为何能够避免冷战和热战,最终和平崛起呢?我们不得不提起当今世界唯一的超级大国:美国。

美国是当前世界的霸主,以民主和自由著称,被戏称为“灯塔国”。美国的崛起,一方面是得益于美国得天独厚的地理优势:两面环海,无陆地入侵威胁,这使得两次世界大战期间美国本土几乎没有被战火波及;水资源丰富,大面积国土为可耕种的平原,有利于规模化耕种,最终使得美国的农业成本极低。美国在第二次世界大战期间大赚战争财,战后又通过马歇尔计划牢牢掌控了欧洲的经济命脉,最终冷战打败苏联后成为全球唯一的霸主。

美国看起来强大到独孤求败,但实际上其内部也存在着诸多问题。

首先最大的问题便是产业空心化:美国的第三产业占比高达60%,工业产值不及中国的三分之二。无论第三产业是多么美好,第二产业永远都是现代工业国家的立国之本。中国之所以成为世界工厂正是因为中国拥有完整的工业体系、成熟的供应链体系和发达的物流系统。

第二大问题是腐败分裂的政治体系:美国的政治体系被视为民主和自由的典范,其三权分立的政治制度有效的防止了权力的滥用。但近代以来美国政治格局被资本集团挟持,导致国家的利益被资本高度捆绑。虽然美国表面上是民主制度,但资本家会通过操控舆论的方式来蒙蔽民众的眼睛,从而达到操控选情的目的,最终牢牢掌控美国政府,使其成为实质上的傀儡政府。在此基础上各个资本集团为了达成自己的目的从而扶持不同的政党,因此表现出来的便是美国两党纷争的政治局势。

资本是逐利的,在一定程度上来说资本家远不及政治家的目光长远。不同利益集团缺乏强有力的中央政府统一调控,最终造成了美国政府执行力效率低下,除非存在强有力的外部压力,很难像中国一样“集中力量办大事”,或是为了更长远的利益牺牲眼前的利益。得益于中国的一党专政和中央集权,中国的各种政策持续性强,执行效率和力度都要更高,当然也能很好的协调各方面的资源,如中国的各种资源调动工程和经济特区等。

不得不承认,任何一种制度都有其两面性,美国的制度减少了政府犯错的几率,却牺牲了执行效率;中国的制度提升了执行效率,却忽略了政府也会犯错的事实。但是我们的政府近年来在不断改进监察制度,从而极大程度地规范了政府的行为,这也在另外一个方面证实了当前中国政治体系的优越性。

说完了内部,不得不提到中美两国外交政策的差异性。美国的外交政策充分体现出西方文化的排他性,西方文化自古以来都是小城邦之间的外交,他们缺乏大一统的文化认知,所以欧洲至今还是无数个小国在欧盟框架中打打闹闹,美国各个州至今分裂严重。这表现在美国全球治理的政策上就是美国倾向于把每一个团结的地区拆分为一个一个的小国,然后再插手地区事务使得这群小国之间引发矛盾,然后爆发地区冲突,美国就可以坐享其成,卖军火赚的盆满钵满的同时还能保证这些国家无法形成气候,威胁美国的既有利益。

中国的外交政策则融合了中华文化中包容的理念,无论是“和平共处五项原则”,还是“求同存异、合作共赢、共建人类命运共同体”,都无不体现出中国五千年的政治智慧。虽然无论是何种政策,均反映出国家之间的利益交换或者说体现出国家根本利益的最大化。如果说中国的政策像是“聪明”的商人,精打细算;那么美国的政策则更像中世纪的“欧洲海盗”,豪夺强取。

综上所述,美国依旧强大,但其已经无法阻挡中国崛起的步伐。

我们的先辈力挽狂澜,帮助我们国家度过了最困难的时期。但当今世界依然充满了各种挑战:日本和印度依旧是不可忽视的地区大国,美国再疲弱也在短期内不会失去其霸主地位。所以,中国的崛起之路远比我们想象的要困难。但另外一方面,我们的国家无论是在政治体系上,还是在综合实力上,都已经展现出崛起的不可阻挡之势。

总结来说,中国的崛起是必然的。当代中国,必将大有可为。

2018.12.18,于江安河畔,有感而发

逆流而上:从政府大院一步步走向四川大学

四川大学计算机学院 李登淳

我们这一代人被称为“千禧宝宝”,一出生就处于一个十分优越的环境中:我们的父母大多数都接受了良好的教育,国家经济正享受着改革开放的红利,我们中的很多人都是独生子女。

在这样一个时代背景下,我们在享受着父辈未曾享受过的资源同时,也面临着比父辈多得多的诱惑和陷阱。

我出生在地处城乡交界处的一个政府大院中,父母是很普通的国家公务员,都接受过高等教育。由于国家政策的限制,我父母只能养育我一个孩子,所以我是从小集宠爱于一身的独生子女。大院里的孩子情况多数与我相似。虽然我们和城里的孩子都属于独生子女,但包括我在内的大院里的孩子与他们不同:我们从小接触不到千奇百怪的社会环境。

我们的世界就是大院,我们在能够独立出行之前从未自己踏出过大院的大门,当然这也让我们这群孩子和所属的家庭免遭非典的伤害。因为那个时候母亲在另外一个不远的政府单位工作,所以非典将我和母亲隔离开了好一段时间,那时候不知道啥是非典,只知道不能见到母亲了。这也导致了我在具体查询了非典相关资料以前并不知道我小时候爆发过这样一场灾难。

后来我大了一些,父母开始带着我去院外的农田里玩,摘麦穗,挖野菜,捉虫子。记得那个时候的天还是蓝的,晚上还能坐成一圈围在大院操场上数星星。我们一群孩子在整个大院里玩捉迷藏,玩飙自行车游戏。家长们都互相认识,在那个夜不闭户的小院里从来不担心我们的安全。夏天热了我们就去买雪糕的奶奶家买几只小布丁,大概只要五毛钱。

就这样我们在大院的庇护下小学毕业了。

父亲中专时学的是计算机专业,所以他对于我偶然间造访他办公室时对电脑展现出的兴趣进行了一些引导,送给我他曾经的教材。我便使用那几本被父亲翻的泛黄但依旧整洁的旧书进行了计算机相关的启蒙,从此便一发不可收拾。

初中时我到了隔壁教育更发达的城市读书,住在爷爷家,父母每天晚上乘坐单位的班车到爷爷家照顾我的生活。后来爷爷奶奶为了方便我读书搬到了叔叔家,从此爷爷家成了实质上我们的新家。我正是从初中开始比较系统的学习计算机相关知识,买了很多这方面的书籍,包括硬件组成和维护以及比较简单的CMD脚本等。父亲的好友是单位的信息管理员,得知我对这方面感兴趣便放我到单位的废旧硬件仓库里玩,我硬是在里面拆拆装装淘出来三四台电脑,这也为我后来学习编程打下了坚实的基础。

初二春节的时候在书店里偶然看见了一本Accelerated C++,当时刚参加过学校的科技展,其中就介绍了C++,于是我毫不犹豫地买了下来,开始自学C++。经过近一年的摸索我在初三时取得了一些进展,开始写一些简单的小项目,现在看来都是玩具级别的东西。当时因为父母管教比较严格并没有智能手机,又因为小时候不知道网吧这种东西所以一直都是有什么问题自己烧脑解决,然后回家之后在简陋的小平板上一点一点打进去调试练习。这也让我养成了有问题先自己想方设法解决的习惯以及不依赖IDE进行编程的习惯。

初三时同时发生的还有另外一件事:我们的班主任兼数学老师发掘了我的潜力,鼓励我自学数学知识并尝试自己解决困难的数学问题,然后在讲台上给同学们讲解。可以说我的这个班主任彻底的改变了我的性格,让我更加自信,培养了我独立探究问题的能力和教授知识的能力。

同时我也得以逆转不断下降的成绩,成功考入市重点高中。

进入高中,首先是毛遂自荐第一次当上了班长,在高一班主任的培养下逐渐熟悉了团队管理的一些技巧和注意事项,同时也磨练了我桀骜不驯的性格。但真正再一次改变了我的性格的是不久后的一次车祸:我为了躲避行人不小心使自行车前轮刹车抱死,头部受到严重冲击摔在路边不省人事,被同学看见帮忙打了120送到医院抢救。完全恢复意识时大概已经是一两天之后了,上身插满了心肺监护的探头。

还好我命大活了过来,只是头骨骨裂,颅内出血量又恰巧躲过了头部手术指标,除了口腔里因为碎裂的牙齿划伤缝了几针之外没有其他外伤。被捆在病床上一个月后腿部肌肉萎缩到几乎不能走路了,我又在父亲的帮助下重新站起来学会走路。在病床上的一个月里,除去前半个月几乎都在睡觉(恢复神经),剩下半个月就是在病床上思考人生。父母因为这场事故濒临崩溃,痊愈后再也不对我的学习加以期望,用父亲的话说,孩子能好好的活着就满足了。

真的感慨父母永远是背后最坚实的依靠。

因为这个事我在做事风格上要收敛了不少,开始做一些实事。出院后第一件事就是在各方的支持下向校方提出申请建立了计算机社团,这中间又经历了好多波折,但年级主任最终扛不住我的软磨硬泡同意了。然后就是热火朝天的宣传,招新,闹得整个学校沸沸扬扬,我最高纪录大概是给上百人同时讲课。社团在我高二时达到顶峰,学校划给我们一个废旧机房,我组织学生打扫了一遍卫生然后扯上了网,在一堆旧电脑里凑出来几个用,在这里我得以大量的编写代码,我这边编写出来的程序立刻就有几十个学生帮忙测试,形成一个十分良性的循环,在这种需求驱动的开发模式推动下我接连完成了CovBasic的开发和CovScript开发,后者成为我中学时期编写的规模最大复杂度最高的程序。

高三时我渐渐退出社团日常管理,但依旧掌握大局,偶尔还是去机房写一些程序。在此期间我带的一个学生获得了我们社团成立以来第一个省级奖项:NOIP二等奖。由此为契机我推动了学校官方竞赛班的创立,但由于有了竞赛班加上高三学习越来越紧张我分身乏力,社团总体开始走下坡路,到我毕业时已经名存实亡了。

同时另外一边,我母亲注意到了四川大学等一些985高校有不需要各大竞赛奖项而且最高能够降一本线录取的优惠资格,开始为我筹备自主招生事宜。我母亲的上级领导的女儿前一年刚刚通过自主招生考上了北京师范大学,所以他便将相关资料和经验交给我母亲,我母亲在其帮助下顺利帮助我完成了相关准备工作并顺利通过了四川大学的自主招生复试资格。我根据我自身情况高考前一个月向父母和班主任提出回家自学,在充分交流之后他们倒也是十分开明的同意了我的请求。最终我十分顺利的在高考中超常发挥,取得了理想的成绩。

为什么说是超常发挥?因为我考前焦虑症虽然没体现在精神上,却异常导致了阑尾炎的发作,我考试两天都是在疼痛中渡过。考完之后几乎立刻病情就得到了缓解,高考之后立刻就赶到成都参加四川大学自主招生复试,我竟然吃了好几天麻辣火锅都没有任何问题,后来一直阑尾都没有任何复发的迹象。

当然了,复试的时候由于我拥有十分丰富的演讲和教学经验,应付面试毫无压力,顺利拿到了直降一本线的录取资格。

回看18年的成长历程,总体是非常顺利且特殊的。我一直在说,我用我自己的经历证明了,任何努力终究不会白费,但付出努力的同时必须要抓住机遇。有人可能会说,我只是幸运罢了,其实并没有这么简单。的确很多事情我非常幸运,但事实上幸运在不断的降临在每个人身上,只是他没有发现并加以利用罢了。我觉得我的经历给我的最大启发便是千万不要过于功利的为了一个短期的目标而奔跑,成功的关键在于向着一个足够远大且真实的目标付出持续而坚定的努力。我的理想是建立完整的中国自主研发软件生态,所以我在这条漫漫长路上不断地积累,不断地努力,而能够进入四川大学就读只是一个阶段性成果罢了。

那为什么说我是“逆流而上“呢?

很明显,在大家都疯了一般在学校里参加各种模拟测试时,我回家自学;在大家都去网吧,打游戏,谈恋爱,追电视剧时,我在写程序;大家都在参加各种比赛拿证的时候,我在社团泡着写程序;大家都在使用成熟框架写程序时,我不厌烦的去尝试自己实现基础功能。我看起来是最傻,最Low的那个臭屌丝,整天沉迷于好像没什么用的程序里,最后却比大多数嘲笑我的人跳的更高。

那为什么逆流的人很少能攀的更高呢?

逆流是一个非常危险的行为,如果把控不好方向很容易走歪。都听说过“博观而约取,厚积而薄发“,却很少有人懂得其本质。现在的人都太浮躁,被大量的毒鸡汤洗脑,希望能一夜成名。尤其是我们的同龄人和后辈,被现在一些不良之风带坏,梦想通过当网红打电竞一夜暴富,过上衣食无忧的生活;或者希望几年,几个月,甚至几周就精通一门专业技术,快速实现自己不切实际的梦想。无论是什么事,没有厚积,哪来的薄发!如果怀揣梦想,一定要脚踏实地的一步一个脚印去实现它,戒骄戒躁,才能有机会去实现它。

最后,任何一个人的成长道路都不是完全相同的,一定要结合自己的实际情况,选择适合自己的道路,并坚定的走下去。我相信任何人都能够逆流而上,活出自己的精彩。

谨以此文,纪念我18年的成长历程,送给我的学生,我的学弟学妹,我的后辈以及我自己,希望能够给更多的人启发而走出自己的道路。