完美转发不完美

C++的完美转发并不完美,这是因为对于如下例子代码 struct Boulder{ Boulder(){} Boulder(const Boulder&)=default; Boulder(Boulder&&)=default; Boulder& operator=(const Boulder&)=default; Boulder& operator=(Boulder&&)=default; ~Boulder(){} }; void f(Boulder) {} template<class T> void g(T&&t) { f(std::forward<T>(t)); } int main() { f(Boulder{}); g(Boulder{}); } 对于f函数调用,只调用Boulder的构造函数和析构。然而对于由g完美转发后在调用f时候,却不得不调用移动构造,因为f的函数参数是值类型,其参数现在为xvalue而不再是prvalue. 然而有一个技巧可以修复该问题,对于C++安腾ABI 而言,如果将f的参数改为右值引用,godbolt显示修改前后f将产生一样的汇编代码。 godbolt 因此如果想省去g中完美转发的开销,可以借助这一点,将g改写为 template<class T> void g(T&&t) { typedef void(*MyF)(Boulder&&); auto nf=reinterpret_cast<MyF>(&f); (*nf)(std::forward<T>(t)); } 这样*nf的参数是右值引用,省去了移动构造的开销。 上述g中的nf调用虽然理论上是UB,但我们毕竟是对真实的物理机操作,实际中是可以正确执行的。

2024/11/16
articleCard.readMore

闲散颂

正如和我同时代的大多数人一样,我是在这样的谚语中长大的:“游手好闲,魔鬼也嫌。”我是个乖孩子,人们说什么,我就信什么。这种道德信条,让我勤奋工作至今。但是,尽管我的道德信条仍控制着我的行动,我的看法却已经历了一场革命。我认为在这个世界上,人们做的工作实在是过多了。工作即美德的观念,对人们造成了极大的损害。现代工业国家需要宣扬一些与过去全然不同的信念。大家都知道这个故事:到那不勒斯的游人看到十二个叫花子躺着晒太阳(事情发生在墨索里尼时代之前),要给其中最懒的一个一里拉,有十一个跳起来说自己最懒,该给自己,他却把钱给了第十二个。这个游人是对的。但是,在不欣赏地中海阳光的国家里,想要闲散就困难多了,得要大规模的宣传来开新风气。我希望,在读了下面的文字后,基督教青年会的领袖们能掀起一场运动,劝导好青年们闲散一些。这样的话,我也算没白活了。 在我为懒惰进行辩解之前,我得先撇清一种我无法接受的说法:一个生活已经富足的人,如果还打算从事日常的职业,比如教书或打字,人们会说他(或她)这种行为是恶劣的,无异于从别人嘴里抢面包。如果这种说法不错,那我们大家就都只须闲呆着,我们的嘴里就能塞满面包了。说这话的人忘了,一个人赚的,通常都会花掉。花费的时候就提供了就业。把收入都花费掉时往别人嘴里塞的面包,同他赚的时候从别人嘴里挖出来的面包,是一样多的。由此看来,真正的坏蛋是存钱不用的人。如果像寓言中的法国农民那样,把省下来的钱都藏在袜子里,显然就不能提供就业。如果把储蓄拿去投资,事情就不那么显而易见了,会出现一些不同的情况。 最常出现的一种情况是把储蓄借给政府。鉴于最文明的政府的大部分公共开支都用来支付过去的战争,或准备未来的战争,借钱给政府无异于莎士比亚剧中那些雇佣杀手的坏蛋。人们节俭习惯的净效应是让借到钱的国家扩张军备。显然,人们倒不如把钱花掉更好些,即便是花在喝酒、赌博上。 不过,会有人对我说,如果把储蓄投资在企业上,情况会完全不同。如果这些企业成功,生产些有用的东西,那倒还说得过去。可是,谁也不能否认,眼下多数企业是失败的。这就是说,大量的人类劳动,本来可以用来生产些可供人享乐的东西,却生产了机器,而且一生产出来就被闲置,没给任何人带来益处。把储蓄投给濒临破产的企业,既害人也害己。如果把钱花在,比如说,搞些派对,朋友们会得到乐趣(我们希望如此),那些赚到钱的卖肉的、烤面包的、走私酒的,也都会高兴。但是,如果把钱花在(比如说)替路面车辆铺设轨道,而那个地方根本没人要这种车子,那就是把大量的劳动耗费在没人得到快乐的道路上了。尽管如此,当他因投资失败而穷困时,他只会被人们视为时运不济的可怜的受害者,或者是一个挥霍浪费的家伙,仅此而已;而花钱做善事的人,却成了人们看不起的傻瓜和轻浮之人。 说了这么多只是开场白。我要极其严肃地说的是:在当今世界上,工作即美德的信念,正在造成大量的祸害,幸福和繁荣之路在于有组织地减少工作。 首先,什么叫工作?工作有两种。第一是改变地面上或地面附近的物品与其他物品的相对位置;第二是让别人这么做。第一种工作是不愉快的,报酬很少;第二种则令人愉快,报酬丰厚。第二种工作可以无限扩张:不仅得有下命令的人,还有那些出谋划策提意见的人。通常由两个有组织的团体同时提出两种相反的意见,这就叫政治。第二种工作所需要的技能,不是对所提意见相关事物的知识,而是说服他人的舌辩弄文之术,例如广告术。 整个欧洲还有第三种人(美洲似乎没有),比前面两种工作者都更受尊重。这些人靠着对土地的占有,能使别人为得到生存和工作的权利而付出代价。这些地主是闲适的,有人也许觉得我会因此而颂扬他们。不幸的是,他们的闲散只是因为别人的劳作才成为可能。事实上,他们对闲适的渴求,是历史上所有工作说教的根源。他们最不希望的事就是别人学他们的样。 从人类文明起源直到工业革命时代,人辛苦工作生产出来的东西,通常仅够维持他一家的生存而略有余。他的妻子得像他一样苦干,孩子们稍有劳动能力,也得参加劳作。除维持生存外仅有的一点剩余,却不属于他们自己,都被战争贩子、祭司们掠去了。碰上饥荒,没有剩余了,战争贩子、祭司们照拿不误,结果是许多做工的都饿死了。这种制度在俄国延续到1917年[注];在东方至今仍无变化;在英国,尽管经历了工业革命,到拿破仑战争期间依然如故,直到一百年前新生的制造商阶级取得政权才告结束;在美国,这种制度在革命后就结束了,除了南方还一直持续到内战之后。一种制度持续了如此长久,直到最近才结束,自然会对人的思想和观念产生深刻影响。我们深以为然的工作乃本性需要的观念,大多来自这一制度,这种观念属于前工业时代,并不适合现代世界。现代技术已经使休闲成为可能,在一定限度内,已不再为少数特权阶级所独享,而是可以由社会大众分享的权利。工作的道德是奴隶的道德,现代世界已不再需要奴隶。 在早期社会中,农夫们的本意显然是不愿把微薄的剩余,拿来供养战争贩子和祭司们的,他们宁愿少生产,或者多消费。起初,全然由于武力强迫,他们才多生产并让人拿走剩余。后来逐渐发现一种办法,就是诱使许多人接受这样的道德观念:努力工作是他们的责任,而且他们得用一部分工作的成果供养那些闲散的人。有了这个办法,强迫手段用得少了,政府的开销也少了。到了今天,如果有人宣称国王的收入不应该比工作的人多,那么在英国百分之九十九挣工资的人都会觉得不可思议。从历史来看,责任观念是掌权者用来诱使别人为主子的利益而不是为自己的利益干活的一种手段。当然,掌权者会把自己伪装起来,设法让人相信他们的利益同人类更大的利益是一致的。有时这倒是实情,例如,雅典的奴隶主们把部分闲暇持久地贡献给了人类的文明,而这在公平的经济制度下,可能是做不到的。休闲是文明的基础。在过去的年代,只有多数人的劳作,才使少数人的休闲成为可能。但是,多数人劳作的价值,并不是因为劳作好,而是因为休闲好。运用现代技术,已有可能公平地分配休闲,同时不损害文明。 现代技术已有可能大大减少每个人获取生活必需品所需要的劳动量。这点在战时尤为明显。军队中所有的男人,以及从事军火生产的男男女女,从事谍报、战争宣传、或与战争有关的政府工作的男男女女,都从生产岗位上撤出来了。尽管如此,协约国这边挣工资的非熟练劳动者的福利水平却比此前还要高。这一事实的意义被财务数据所遮蔽:借贷则使这一意义重新显现,似乎未来正养育着现在。但这显然是不可能的:一个人不能吃一片还不存在的面包。战争证明的结论是,靠科学组织的生产,可以只用现代世界一小部分的劳动能力使全体人民生活得相当舒适。如果战争结束时,将人解放出来以便参与战斗和制造军火的那些科学的生产组织能保存下来,把每天的工作时间减到 4小时,一切都会好好的。可是,相反地,旧时的喧嚣重又回潮,有工作的人还得长时间工作,没工作的人只得失业挨饿。为什么呢?因为工作是责任。人不是按照他的产量多少拿工资,而是按照他的由行业证明的德行好坏拿工资。 这是奴隶制国家的道德,却仍用在与形成这种道德的时代完全不同的现代环境中,产生灾难性的结果也就毫不足奇了。让我们作进一步说明。假如在某一特定时刻,一定数目的人给雇来制造大头针。世界需要多少,他们就造多少,每天(比如说)工作8小时。有人做了一项发明让同样多的人可以生产两倍的大头针,大头针已经很便宜了,即便再压低价格也卖不出更多的大头针。在一个明智的世界里,会让生产大头针有关的人都只工作4小时,而不是8小时,其余一切仍旧可以照常进行。但在现实世界里,这却被视为道德败坏。人们仍得工作8小时,大头针生产得太多了,一些雇主破产了,生产大头针的人有一半失了业。结果呢,就休闲时间总体而言,和另一种做法是一样多的,只是现在有一半人完全闲着,另一半却劳累过度。这样一来,无可避免的闲暇只能造成广泛的苦难,而没能成为普遍的幸福之源。还能想像出比这更愚蠢的事吗? 对穷人也应该休闲的看法,富人总感到惊异。在19世纪初的英国,人们一般每天都要工作15小时,孩子们有时也这样,但通常是12小时。爱管闲事的人提议说这工作时间可能太长了些,有人就会对他们说,工作可以让大人们不酗酒,让孩子们不调皮捣蛋。在我还是个孩子的时候,城市工人们刚获得投票权,就通过法律规定了一些公共假期,惹得上层阶级大发雷霆。我还记得一个老公爵夫人说:“穷人要假期做什么?他们应该工作。”现在的人们说话不那么直白了,但观念依旧,成为经济中许多混乱现象的源头。 且让我们花点时间,不带偏见地、坦诚地考察一下工作道德吧。每个人的一生,出于需要,总得消费一定量的人类劳动产品。就让我们假定劳动总的来说是令人厌烦的,一个人的消费如果比他生产的还多,那是不公正的。当然,他也可以提供服务,如果不是商品的话,就像医生,但他总得提供点什么,以换取他的吃和住,就这一限度而言,必须承认工作的责任,但仅以此为限。 我不必细说,在苏联以外的所有现代国家中,许多人甚至逃避这点起码的工作,这就是那些因继承或婚姻而得到钱财的人。我不认为让这些人闲着所带来害处,能与那些让挣工资的劳动者过度劳动或挨饿的想法所带来的害处相提并论。 如果普通挣工资的人每天工作4小时,那就足够每个人过的,也不会有失业——前提是得做好适当而明智的组织工作。这种想法让有钱人吃惊,因为他们不相信穷人懂得怎样利用这么多的闲暇。在美国,人们通常长时间工作,虽然他们已经够富裕了。美国人对挣工资的人享有闲暇的想法感到愤慨,除非这种闲暇是作为对失业者的严厉惩罚。事实上,他们甚至不喜欢让他们的儿子闲着。很奇怪的是,尽管他们希望儿子们勤奋工作,甚至没时间学文化也在所不惜,对于他们的老婆和女儿不干工作却不在意。在贵族统治时代,对“无用”的势利的赞赏,是没有性别差异的,到了财阀统治时代,却只限于妇女。这显然是不合常理的。 必须承认,明智地运用闲暇,是文明和教育的结果。长时间工作了一辈子的人,突然无所事事,一定闲得难受。但是,没有相当的闲暇,人们将与许多最美好的事物无缘。已没有任何理由要大多数人再忍受这种被剥夺闲暇的痛苦。只有愚蠢的、通常是代人受过的苦行主义,要求我们继续过量工作,尽管己无此必要。 控制着俄国政府的新教条,尽管其中大部分已与西方传统的教义截然不同,仍有一些东西没有变。统治者们的态度,特别是主管宣传教育的那些人,在劳动光荣问题上,几乎同全世界统治阶级对所谓“诚实的穷人”向来说教的如出一辙。勤劳、自制、愿意为遥不可及的利益长时间工作,甚至对当权者俯首贴耳,这一切重又出现;更有甚者,当权者仍然代表着“宇宙主宰”的意志,只是这个“宇宙主宰”有了新名字,叫“辩证唯物主义。” 无产阶级在俄国的胜利,同女权主义者在其他国家的胜利颇有一些相同之处。许多年代以来,男人们承认女性的圣洁,而且为了安抚地位低下的女性们,不断维持着圣洁比权力更可取的说辞。最终,女权主义者们决定她们两者都要,因为她们中的先进分子相信男人关于美德可取的说法,但不相信男人所谓的政治权力一钱不值的说辞。关于体力劳动,同样的事在俄罗斯发生了。许多年代以来,富人和拍他们马屁的人写了许多颂扬“诚实的苦工”的文字,颂扬俭朴的生活,宣扬穷人比富人更可能进天堂的教义,并且试图使体力劳动者们相信,变换物质在空间里的位置的工作,具有特殊的高贵性,正如男人要女人相信她们从性奴役中能得到某种特殊的高贵性一样。在俄罗斯,所有关于体力劳动优越性的说教都是很认真严肃的,其结果是体力劳动者比所有人都更光荣。实质上,劳动信条的复兴者们所要达到的目的,无非就是要招徕突击队员以应付特殊的任务。体力劳动成了青年人的理想,也是一切道德说教的基础。 就眼前来说,这一切也许是无可非议的。国家很大,自然资源很丰富,有待开发,而且只有很少的资金可用于开发,因此,艰苦工作是必要的,并有可能得到大的回报。但是,在每个人无需长时间工作就能舒适生活之后,又会发生什么呢? 在西方,我们有处理这一问题的各种方法。我们不寻求经济上的公平,因此总产品的一大部分到了很小一部分人的手里,这些人大多不工作;由于对生产没有集中的控制,我们生产的许多东西根本没人要;我们让很大一部分劳动人口闲着,因为我们可以让另一部分人工作过度。如果这些方法都不好使,我们还有战争:我们可以让许多人去制造烈性炸药,再让其他人去点燃它们,就像刚发现爆竹的孩子。把这些我们可以控制的手段结合起来,我们总算(虽然有困难)保住了这一观念,即:从事大量繁重的体力劳动是普通人的命运。 在俄国,由于更关注经济的公平和对生产的集中控制,解决问题的方法也不同。合理的解决办法应该是:一旦有条件给大家提供生活必需品、保证基本的舒适,就逐步减少劳动时间,并在每个阶段让人民投票决定是喜欢更多的闲暇还是更多的物品。但是,在说教了多年艰苦工作的崇高美德之后,我们难得看到当权者们会把目标设定为建立多休闲、少工作的天堂。我们更可能看到的是,他们会不断制定新的计划,为了未来的生产率,牺牲今天的休闲。我最近读到一份俄国工程师提出的独出心裁的计划,说是要筑一条坝穿越喀拉海,使白海和西伯利亚北部沿海变暖。真是一个了不起的项目!但当苦工的高贵性展现于北冰洋的冰天雪地之时,无产阶级的舒适生活很可能要给推迟一个世代了。这种事真要发生的话,那只能是把艰苦工作的美德视为目的本身、而非走向不再需要艰苦工作的境地的手段的结果。 虽然有一些移动物品的活动为我们的生存所必需,但它绝非人生的目的之一。否则,我们就得说随便一个不熟练工人都比莎士比亚高明。在这个问题上,我们受到两个动机的误导。其一是让穷人保持满意的必要性,这使富人数千年来不断鼓吹劳动的高贵,并时时注意让自己在这方面处于“不高贵”的地位;其二是机械装置所带来的新乐趣,这使我们对自己能在地球表面制造出令人惊奇而聪明的变化感到高兴。这两个动机对实际工作的人都没有多大吸引力。如果你问他一生中最美好的东西是什么,他大概不会说:“我爱好体力劳动,因为那使我感到我在完成人类最崇高的任务,因为我喜欢思考人能在多大程度上改变地球。确实,我的身体需要一段时间的休息,我得尽力打发这段时间,但只有当清晨来临,我能重新开始辛苦劳作的时候,我才感到莫大的幸福,因为我的满足感来自辛苦劳作。”我从没听到工人们说过这样的话。他们只是认为,也应该这么认为,工作只是谋生的必要手段,如同他们认为从休闲中他们能得到幸福的享受一样。 虽然多少有点闲暇是令人愉快的,但有人会说如果24小时只工作4小时,人们就不知道该怎么打发日子了。即便说在现代世界这是确实的,那也是对我们文明的谴责,在早期是不会这样的。先前人们还是能够轻松轻松,玩一玩的,虽然由于崇拜效率,多少也有些限制。现代人认为任何事都应当是为别的什么事做的,而绝不能是以本身为目的。例如思想严肃的人总是谴责看电影的习惯,对我们说那会诱使青年人犯罪。但制作电影的一切工作都是可尊敬的,因为那是工作,因为它带来利润。认为一切值得做的事都应该带来利润的观念,把所有事都搞颠倒了。屠夫供你肉,面包师傅供你面包,都是值得称道的,因为他们赚了钱,可是,在你享用他们提供的食物时,你却是无可称道的,除非你是为了有力气工作而吃。总的来说,人们认为赚钱好,花钱坏。鉴于它们乃是同一交易的两面,这种观念是荒谬的。人们也满可以说钥匙好、锁眼坏。生产物品的好处,完全是从消费物品的利益中衍生出来的。在我们的社会里,个人为利润工作,但他的工作的社会目的在于消费他所生产的东西。正是生产的个人目的和社会目的的分离,使人在这个以赢利刺激工业发展的世界里很难头脑清楚地思考。我们对生产考虑得太多,对消费则太少。结果是我们太不重视享受和单纯的幸福,我们也不是根据生产给予消费者的快乐来判断其价值。 在我建议把工作时间减少到4小时时,我不是说剩下的时间都只能纯粹无聊地打发掉。我的意思是一天工作4小时应能使一个人得到生活的必需品和基本闲适,剩下的时间应属于他自己,他觉得怎么合适就怎么用。这种社会制度的一个必要组成部分是教育应比现在更为深入,而且教育的部分目的应是建立人们的兴趣爱好,使他们能明智地利用闲暇。我所考虑的倒主要不是那些所谓“高级趣味”的东西。除了边远的农村地区,已经看不见农民跳舞了,但他们的本性中仍然会有提高自身素养的冲动。城里人的娱乐活动大多变得被动:看电影,看足球赛,听收音机,等等。原因是他们的活力都被工作耗尽了。如果闹暇增多,他们是会重新主动参与、享受娱乐活动的。 过去是有闲阶级数量小,工人阶级数量大。有闲阶级享受的便利缺乏社会公平的基础,这就必然使它难以让人忍受、得不到多少同情,并且需要为其特权炮制辩护的理论。这就大大减少了休闲的优越性。但尽管有这些不足,它还是对几乎整个我们所说的文明作出了贡献。休闲培植了艺术,发现了科学,产生了各种著作,发明了哲学,并改进了社会关系。甚至被压迫者的解放也常常是由上面发动。没有有闲阶级,人类决不能走出野蛮状态。 不负责任的有闲阶级的生活方式常常是异常浪费的。这个阶级的成员没有一个受到过必须勤奋工作的教育,而且就整体而言,这个阶级也不比他人更聪明。这个阶级可能产生过一个达尔文,但其代价是成千上万的乡绅除了猎狐和惩罚偷猎者之外,决想不到还有别的更聪明的事干。现在的大学正在系统地提供过去有闲阶级偶尔作为副产品提供的的东西,这是很大的进步,但仍有某些不足。大学生活和世界上的生活是如此不同,生活在学术环境中的人,容易忽视普通公众所关注的事情和问题,而且他们表达意见的方式常使他们无法对公众产生应有的影响;另一个不利因素是,大学里的研究是有组织的,那些想要做独创性研究的人可能受到打击。因此,学术机构尽管有用,在一个人人走出自家院墙、忙于追求非功利性目标的世界上,并不是文明利益合适的守护神。 在一个无人被迫每天工作4小时以上的世界,每一个怀有科学好奇心的人都可以全身心地投入;每一个画家都可以专心绘画而不致挨饿(不管他的画是好是坏);年轻的作家们不必被迫以耸人听闻的粗制滥造作品来吸引注意力,以使自己有经济能力来写出不朽的作品,而时机一旦来临,他们或许已经丧失了情趣和能力;那些在业务工作中,滋生了经济学或政府工作某个方面兴趣的人,能够发展他们的思想,而不必受学术机构的羁绊,这种羁绊往往使大学里的经济学家的工作缺乏现实性;医务工作者们能有时间学习医学上的进展;教师们也不必再恼火地勉强用老掉牙的方法教授他们年轻时学过的东西,而这些东西时过境迁,可能早已不符合实际了。 最重要的是,生活中将会有幸福和欢乐,而不是神经紧张、身体疲倦和消化不良。工作量将确保维持在足以使休闲令人愉悦、而又不会使人疲累的程度。因为人在空余时间是不累的,他们不会只要求那些被动的、乏味的娱乐活动。至少百分之一的人可能会把业余时间奉献出来,从事具有公共意义的事务,因为他们无需以此谋生,他们的独创性就不会受到妨碍,也不必刻意符合旧权威所规定的标准。但是,休闲的好处并不仅仅显现在这些个别的情形中。普通男男女女,如果有机会过幸福生活,会变得更加和蔼,不那么难为人,不那么用怀疑的眼光看人。对战争的兴趣也会消失,部分是因为这个原因,部分还因为在战争中要付出长期和艰苦的劳作。在各种德性中,善良的性情是世界最需要的。性情善良是悠闲、安全的结果,而不是艰苦奋斗一生的结果。现代生产方法已给了我们大家悠闲、安全的可能,我们却还是选择让一些人工作过度,另一些人挨饿。迄今为止,我们始终都像机器出现以前那样忙忙碌碌,我们这样做是愚蠢的,但没有理由永远愚蠢下去。

2024/11/16
articleCard.readMore

adapting c++20 ranges algorithms for most metaprogramming

daisy在cppnow 2023的演讲中展示了可极大简化模板元算法的技巧。 以排序为例 基本思路是 将type_list中的每个Type映射到他们的index, 然后对index数组做sort,结果为sorted_indices, 最终type_list中各个元素按照在sorted_indices新位置重新安放,返回结果type_list. #include <type_traits> #include <ranges> #include <array> #include <algorithm> template <class...> struct type_list {}; template <class List, auto Key> struct Sort {}; template<size_t N, class ... Ts> using at_t = std::tuple_element_t<N, std::tuple<Ts...>>; template<class ... Ts, auto Key> struct Sort<type_list<Ts...>, Key> : std::type_identity<decltype([]<size_t... Idx>(std::index_sequence<Idx...>) { constexpr auto sorted_indices = [] { std::array idxs = {Idx...}; std::ranges::sort(idxs, [](size_t i, size_t j) { using variant_t = std::variant<std::type_identity<Ts>...>; std::array vars = {variant_t{std::in_place_index_t<Idx>{}}... }; return std::visit(Key, vars[i]) < std::visit(Key, vars[j]); }); return idxs; }(); return type_list<at_t<sorted_indices[Idx], Ts...>...>{}; }(std::index_sequence_for<Ts...>{}) ) > {}; int main(){ using type= Sort<type_list<double,int,char>, []<class T>(std::type_identity<T>){ return sizeof(T);}>::type ; static_assert(std::is_same_v<type, type_list<char,int,double> >); } 这里的排序算法的comparator非常巧妙, [](size_t i, size_t j) { using variant_t = std::variant<std::type_identity<Ts>...>; std::array vars = {variant_t{std::in_place_index_t<Idx>{}}... }; return std::visit(Key, vars[i]) < std::visit(Key, vars[j]); } 我们需要取到type_list中第i个元素和第j个元素对应的信息,做法是构造一个variant的数组,variant的每项依次构造为std::type_identity<Ts>…,然后使用std::visit访问数组中的第i项和第j项,因为该lambda是constexpr的,所以可以用于编译时调用。 Key是以std::type_indentity为参数的仿函数 []<class T>(std::type_identity<T>){ return sizeof(T);} 这里使用type_identity<T>而不是T做参数是因为,T可能是void或者数组或者不完全类型等不能作为参数的类型,而使用type_indentity<T>做参数可解决该问题。 main函数展示了如何使用该方法依据类型的size对typelist中的type排序。

2024/1/22
articleCard.readMore

Reproducible github Developer Environments

我們以 github上 jiayuehua/CMakeVcpkgManifest 代码库 爲例,说明如何在github上建立可重复的开发环境。 1. 增加devcontainer 配置文件 在visual stuido code中 C-S-P,选择CodeSpaces:add devcontainer configure files… 生成devcontainer配置文件和Dockerfile。 修改这两文件为和CMakeVcpkgManifest的内容完全一致,因为CMakeVcpkgManifest正确的配置了sshd和安装了X11 forwarding所需要的程序。将改动push到github。 2 . 添加codespace, 在github上添加codespace。 3 .本地使用ssh 连接codespace 在本地使用ssh 连接codespace,使用ssh -X 连接,可以使用X11 forwarding。 ~> gh codespace list 14m 1s NAME DISPLAY NAME REPOSITORY BRANCH STATE CREATED AT opulent-doodle-7p7xpx9wqw2j5 opulent doodle jiayuehua/CMakeVcpkgManifest master Available 29m ~> gh codespace ssh --config > ~/.ssh/codespaces ~> cd .ssh 5s ~/.ssh> ln id_rsa codespaces.auto ~/.ssh> ln id_rsa.pub codespaces.auto.pub ~/.ssh> ls authorized_keys codespaces codespaces.auto codespaces.auto.pub config id_rsa id_rsa.pub known_hosts known_hosts.old ~/.ssh> cat config Match all Include ~/.ssh/codespaces ~> ssh -X cs.opulent-doodle-7p7xpx9wqw2j5.master Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-1042-azure x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage This system has been minimized by removing packages and content that are not required on a system that users do not log into. To restore this content, you can run the 'unminimize' command. Last login: Tue Aug 15 07:25:11 2023 from 127.0.0.1 @jiayuehua ➜ /workspaces/CMakeVcpkgManifest (master) $ xeyes 可以看到xeyes在本地显示。这里假定你使用wslg安装了ubuntu,ubuntu里X11 server。这时我们便可以在本地使用visual studio code 对带图形界面的代码库进行调试了。

2023/8/15
articleCard.readMore

使用mpmcpipeline和jthread实现软流水

多个Jthread线程,每个线程完成mpmcpipeline中一个stage的处理,实现软件流水。第一个stage收到sentinal value时,所有work线程都退出。 #include <gtest/gtest.h> #include <folly/MPMCPipeline.h> #include <vector> #include <thread> #include <boost/mp11.hpp> #include <fmt/format.h> #include <folly/Function.h> #include <type_traits> #include <boost/hana.hpp> namespace mp11 = boost::mp11; namespace hana = boost::hana; template <class T> struct getNumber { constexpr int operator()(int n) const noexcept { return n; } }; template <class T> constexpr auto getnumber = getNumber<T>{}; //n queuesize, T...: sizeof...(T) ie (N+1) queues' type template <int n, typename... T> requires(sizeof...(T) >= 2) class SoftPipeline { //N+1 queues' sentinal boost::hana::tuple<T...> sentinals_; //N stage piepeline folly::MPMCPipeline<T...> pipeline_; //N stage,so N threads std::vector<std::jthread> threads_; //every stage work function template <int N> struct StageCall { SoftPipeline* softpipeline_; using Input = mp11::mp_at_c<mp11::mp_list<T...>, N>; using Output = mp11::mp_at_c<mp11::mp_list<T...>, N + 1>; using Func = folly::Function<Output(const Input&)>; Func func_; //if receive sentinal, break void operator()() { for (;;) { Input val; auto ticket = softpipeline_->pipeline_.template blockingReadStage<N>(val); softpipeline_->pipeline_.template blockingWriteStage<N>(ticket, func_(val)); if (val == hana::at(softpipeline_->sentinals_, hana::size_t<N>{}) ) break; } } }; public: using HeadType= mp11::mp_front<mp11::mp_list<T...>>; using TailType= mp11::mp_back<mp11::mp_list<T...>>; template <class... U> requires((sizeof...(U) == sizeof...(T) - 1) && !std::is_same_v<std::remove_cv_t< mp11::mp_at_c<mp11::mp_list<U...>,0> >, SoftPipeline>) SoftPipeline(const HeadType& sentinalvalue, U&&... arg) : pipeline_(getnumber<T>(n)...) { //make sentinals tuple auto val = hana::make_tuple(sentinalvalue); auto f = []<class Seq, class Func>(Seq s, Func func) { return hana::append(s, func(hana::back(s)) ); }; auto funcs = hana::make_tuple(arg...); sentinals_ = hana::fold(funcs, val, f); //initional N threads [this] <std::size_t... I, class... F>(std::index_sequence<I...>, F&&... f) { (..., this->threads_.emplace_back(std::jthread(StageCall<I>{this, std::forward<F>(f)}))); }(std::index_sequence_for<U...>{}, std::forward<U>(arg)...); } void blockingWrite(const HeadType &h) { pipeline_.blockingWrite(h); } TailType blockingRead() { TailType val; pipeline_.blockingRead(val); return val; } }; TEST(mpmcpipeline, basic) { SoftPipeline<2,int,int,int> pipeline(3,[](int n) { return n + 1; },[](int n) { return n + 2; }); pipeline.blockingWrite(1); auto n =pipeline.blockingRead(); fmt::print("n:{}\n",n); pipeline.blockingWrite(3); n =pipeline.blockingRead(); fmt::print("n:{}\n",n); } value category 和noexcept规范作为练习留给读者。

2023/5/22
articleCard.readMore

在其它线程周期回调函数

不同于FunctionScheduler ,ThreadedRepeatingFunctionRunner是在另一线程周期回调函数,例子 #include <folly/experimental/ThreadedRepeatingFunctionRunner.h> #include <iostream> #include <gtest/gtest.h> #include <chrono> #include <thread> #include <functional> using namespace folly; using namespace std; using std::literals::chrono_literals::operator""s; using std::literals::chrono_literals::operator""ms; //running in another thread struct MyClass final { MyClass() : count_(0) {} ~MyClass() { threads_.stop(); // Stop threads BEFORE destroying any state they use. } void init() { threads_.add("hello", [this]()noexcept { return this->incrementCount(); }); } std::chrono::milliseconds incrementCount() noexcept { cout<<count_<<endl; ++count_; return 1000ms; } private: std::atomic<int> count_; // CAUTION: Declare last since the threads access other members of `this`. ThreadedRepeatingFunctionRunner threads_; }; TEST(threadedrepeat,runner) { MyClass repeat; repeat.init(); std::this_thread::sleep_for(5s); } ThreadedRepeatingFunctionRunner类型的threads_的add函数第一个参数是名字,第二个参数是要周期回调的函数,周期回调的函数返回的参数是duration,单位ms,用于指回调的周期,比如lambda调用的incrementCount返回1000ms,退出前需要调用threads_的stop函数,停止周期回调和退出work线程。

2023/5/21
articleCard.readMore

在当前线程周期回调函数

FunctionScheduler 可在当前线程周期回调函数 #include <folly/experimental/FunctionScheduler.h> #include <iostream> #include <gtest/gtest.h> using namespace folly; using namespace std; TEST(repeatfunc,repeatfunc) { FunctionScheduler fs; fs.addFunction([]() { cout << "Hello, world!" << endl; }, 1s); fs.start(); this_thread::sleep_for(10s); fs.cancelAllFunctions(); } 只需要addFunction时指明要回调的函数和时间间隔。调用start启动,当不再需要回调时调用cancelAllFunctions. 本例中,每隔1s打印一次”Hello, world!”,共打印10次。 可以看到,比使用folly eventbase的asynctimer简单很多。

2023/5/21
articleCard.readMore

for_each

C++11引入了range for loop,可是range for loop只能拿到向前range的元素,而没法取得循环的当前下标。 folly提供了for_each算法,相比标准库的for_each,folly的for_each回调的函数对象或函数可接受两个参数,这时第一个指当前元素,第二个值循环的下标。同时folly的for_each还可对异构的tuple操作。此外folly for_each还可支持提前结束操作,类似for循环的break。 例子: #include <gtest/gtest.h> #include <folly/container/Foreach.h> #include <type_traits> #include <tuple> #include <vector> #include <fmt/format.h> TEST(for_each, foreach) { auto v = std::vector {1,2,3}; auto t = std::make_tuple (1,2,3); auto tb = std::make_tuple (1,2,3.2); auto func = [](auto elem,auto index){ fmt::print("elem:{}, index: {}\n", elem,index); if (elem == 2) { return folly::loop_break; } return folly::loop_continue; }; auto funcb = [](auto elem){ fmt::print("elem:{}\n", elem); if (elem == 2) { return folly::loop_break; } return folly::loop_continue; }; folly::for_each (v,func); fmt::print("++++++\n"); folly::for_each (t,func); fmt::print("++++++\n"); folly::for_each (tb,func); fmt::print("++++++\n"); folly::for_each (tb,funcb); fmt::print("++++++\n"); }

2023/5/20
articleCard.readMore

cpo和tag_invoke

customization point object(cpo)和tag_invoke由eric niebler提出,在ranges和executor中有很多应用。 cpo可用于对库中的一些仿函数进行定制,而tag_invoke用于实现cpo,用于用户的类区分对于不同库的定制。 #include <gtest/gtest.h> #include <folly/Utility.h> #include <folly/lang/CustomizationPoint.h> #include <folly/functional/Invoke.h> template<class T> struct Tag { }; template<class T> constexpr auto tag_c = Tag<T>{}; namespace A { struct FooCpo { template<typename T> auto operator()(Tag<T> t) const noexcept(folly::is_nothrow_tag_invocable_v<FooCpo, Tag<T> >) -> folly::tag_invoke_result_t<FooCpo, Tag<T>> { return folly::tag_invoke(*this, t); } }; struct BarCpo { template<typename A> auto operator()(A&&a ) const noexcept(folly::is_nothrow_tag_invocable_v<BarCpo, A>) -> folly::tag_invoke_result_t<BarCpo, A> { return folly::tag_invoke(*this, (A&&)a); } }; FOLLY_DEFINE_CPO(BarCpo, doathing) FOLLY_DEFINE_CPO(FooCpo, dosomething) } namespace B { struct FooCpo { template<typename A> auto operator()(Tag<A> t) const noexcept(folly::is_nothrow_tag_invocable_v<FooCpo, Tag<A> >) -> folly::tag_invoke_result_t<FooCpo, Tag<A>> { return folly::tag_invoke(*this, t); } }; FOLLY_DEFINE_CPO(FooCpo, dosomething) } namespace My { class SomeClass { friend void tag_invoke(folly::cpo_t<A::dosomething>, Tag<SomeClass>) { std::cout << "A someclass\n"; } friend void tag_invoke(folly::cpo_t<A::doathing>, const SomeClass& some) { std::cout << "A doathing someclass\n"; } friend void tag_invoke(folly::cpo_t<B::dosomething>, Tag<SomeClass>) { std::cout << "B someclass\n"; } }; } TEST(cpo, basic) { My::SomeClass c; A::doathing(c); } TEST(cpo, advance) { A::dosomething(tag_c<My::SomeClass>); B::dosomething(tag_c<My::SomeClass>); } 上文的 BarCpo 示范了如何定义一个cpo, BarCpo有一个模板成员operator(),内部调用tag_invoke, FOLLY_DEFINE_CPO(BarCpo, doathing) 帮我们定义了BarCpo类型的doathing cpo. 类My::SomeClass friend void tag_invoke(folly::cpo_t<A::doathing>, const SomeClass& some)实现了对A::doathing的定制。TEST(cpo,basic)中的A:::doathing(c)调用了我们实现的定制。 My::SomeClass 的友元void tag_invoke(folly::cpo_t<A::dosomething>, Tag)和 friend void tag_invoke(folly::cpo_t<B::dosomething>, Tag) 示范了对于不同名字空间A和B的dosomthing cpo,tag_invoke可方便的分别定制.

2023/5/18
articleCard.readMore

transparent,为关联容器增加查找成员

folly::transparent 模板类可以用比较器实例化,从而支持异型查找: #include <set> struct S { template<class T, class U> bool operator()(T& a, U& b) const { return a < b; } }; struct Man { int id_; auto operator<=>(const Man&) const = default; }; struct ManComp { bool operator()(const Man &l,const int& r)const { return l.id_ < r; } bool operator()(const int& l, const Man& r)const { return l < r.id_; } bool operator()(const Man& l, const Man& r)const { return l.id_ < r.id_; } }; void f() { std::set<Man,ManComp> m; m.find(11);//将报错 } void g() { std::set<Man,folly::transparent<ManComp>> m; m.find(11); } 上述f()中的m.find(11)将报错,因为find的参数只能接受Man类型,而int类型并不能隐式转化为Man,而g()将成功,因为比较器为folly::transparent一个实例,m.find()的查找重载成员将增加一成员模板函数,理论上模板函数的参数只要能和Key也就是Man比较就可。故而使用folly::transparent后成功.

2023/5/18
articleCard.readMore