- 主题:异常这玩意儿当初是哪个脑残发明出来的?
这个问题也有另外一个答案。
其实我一直主张值语义。因为原始类型除了 flaot/double 没有 null 这语义,没道理给复杂对象类型都弄个这个语义。
有没有 null,应该由对象自己决定。比如 string 类型,内部指针是空指针的时候就是 null,和 0 长度的字符串区别开。
或者像 socket 类型,没连接 fd 无效的时候就是空 socket.
如果是这样的话,那构造函数抛异常也不需要了。
【 在 zhongjianguo 的大作中提到: 】
: 这应该是C++的难言之隐,在当初把语言设计为 构造函数不能返回错误时,就决定了C++离不开异常。
--
FROM 120.36.160.*
所以是不是我学会了新版 c++ 就等于学会了 rust?
【 在 milksea 的大作中提到: 】
: 提案估计在路上。不过实话说这个语法糖的重要性真不如标准库以此为基准大改造,后者更难。
--
FROM 110.84.123.*
这事我思考了一段时间,为啥现在对象的所有权会变得这么复杂呢。一个很重要的原因就是有大量回调函数的存在,让对象的生命周期跳出函数的生命周期。
原本 RAII 或者简单引用计数就能够做好资源管理,现在因为有大量的异步回调,变得不够用了。所以新兴的 rust 这些语言才会搞出这么复杂的资源管理模型。
看人家 golang 就不会那么麻烦。c# 也有值类型,也不会那么麻烦。
依我看,等 c++ 也普及了协程编程之后,rust 就会变得一文不值。
【 在 milksea 的大作中提到: 】
: 还是有点差别。
: safe c++的提案(safecpp dot org)倒是特别高仿复刻rust,关键的borrow checker,默认mut,pattern match什么的都有。
: 我觉得rust对c++熟手不难学,它安全性上强调的几个点,比如所有权和生存期,在传统面向对象领域其实是交给程序员自己思考的;在语法表达上的变化,熟悉现代c++和一些函数式风格的话也没啥。
: ...................
--
FROM 110.84.123.*
如果有协程,很多并发访问就能被避免。
实际 golang 和 c# 的协程我觉得也不够优秀。原因是 golang, c# 和 java 都选择了让协程在线程自由调度,因为他们有庞大的兼容性包袱需要继承。
下一门语言我希望是单线程多协程 + 进程隔离式的架构。那么既然是单线程,所谓的并发性安全问题就几乎不再存在了。搭配 cow 技术即可解决掉现在各种编程语言的困境。
现在编程语言把操作系统的线程 API 以及 CPU 核并行计算这种概念直接暴露给程序员,我觉得相当不妥的。而且可扩展性也不够强。应该换个思考方式:
1. 程序员获得一个单一的 CPU 核心、一块独立的无限的内存空间。
2. 程序员编写模块跑在上面那个最简环境内。
3. 可使用协程做到并发。编程语言使用 FP 范式的 COW 数据结构、immutable 以及纯函数等概念附加到协程里面实现模型内编程的可靠性。
4. 模块之间使用消息通信。通信机制由操作系统+编程语言提供。
5. 通信机制跨 CPU 核、NUMA 内存、CPU/GPU 甚至是由不同架构的网络机器,这些复杂的事情不应该由程序员来考虑。
【 在 milksea 的大作中提到: 】
: gc语言没什么可比性。go和c#当然不用考虑对象生存期的问题,但并发访问的安全问题不会被gc解决,所有权分析仍然是有力的工具。实际上,所有权分析的学术讨论远早于rust,很多论文都是基于java这种面向对象语言的。(例如,ownership type的重要综述是2013年的Ownership Types
: c++自由,rust限制多,其他方面差不了那么多。rust的卖点是安全又不是方便。大型企业大型项目就是需要更多语言约束,这个需求是很自然的。
--
修改:hgoldfish FROM 110.84.123.*
FROM 110.84.123.*
c++ 当然可以啊。所以我自己一直以上面写的那些思路在写程序的。
我写的程序很少出现什么内存泄露和因为指针引起的崩溃。就简单使用 RAII 规则搭配有栈协程。
不过效率上,Qt 的 COW 数据结构不够高效。甚至协程的程序,应该尽可能使用申请在栈上面的 string:
class string
{
private:
int len;
char buf[len];
};
大量使用协程的程序,更需要的是值类型。
总之,我上面提到的思路是一个全方面的方案。值得创新个新的语言出来。
【 在 milksea 的大作中提到: 】
: c++现在就行吧,executor用单线程的。rust的调度器也是可定制的,看用什么库了。
: c++和rust这样的语言在定位上就是要满足各种不同需求的,任务调度在不同项目里要求不一样。你这些想法都是库开发的问题。
--
FROM 110.84.123.*
就需要个协程。
我把这个思维方式应用在我们一些服务端业务逻辑开发上。比如我手头有个 OLAP 的小型数据库。有了协程以后,我整个程序结构很简单,每个数据集开个线程,相互独立。每个数据集都有很多请求,每个请求过来是一个协程。
thread1 (workset1)
process --> thread2 (workset2)
thread3 (workset3) ---> fiber1 (request)
---> fiber2 (request)
数据集我弄了个 shared_ptr<WorkingSet>,每个用于处理协程的请求都保持它的一份引用。更新数据集时,是替换这个 shared_ptr<> 而不是直接更新它——当然,我这个技术是适配我这种多读少写的情况。如果是多读多写,那得用 rust 的 btree map 那种支持 cow 的数据结构才对。但思路都一样。
上面几个 thread 虽然相互是独立的,但我用了线程间 RPC blocking queue,类似于 golang 的 chan,修改另外一个 workset 时,不需要搞序列化与反序列化,非常快。
每个请求一个协程,当这个请求结束时,各种资源跟着这个协程的销毁一起销毁了。我没感觉到管理资源有啥难度。
【 在 milksea 的大作中提到: 】
: 你这个思路需要什么语言机制么?看着像底层库开发
--
修改:hgoldfish FROM 110.84.123.*
FROM 110.84.123.*
上面那个帖子讲了一个例子。
我手头还有几个 PC 端的程序,用也是协程,但是因为混入 GUI 的事件循环,看起来就复杂了一些。但是基本情况也差不多。就是有事件过来,如果有 IO,我就启动协程。事件处理完成,协程也被销毁了,使用到的(内存)资源也跟着销毁。
GUI 应用比较简单,因为不会搞多线程。我几年干下来,非常非常少碰到因为内存导致的崩溃。我写的 PC 端软件,有很多用户会连续开一个多月都不关的。
【 在 milksea 的大作中提到: 】
: 你这个思路需要什么语言机制么?看着像底层库开发
--
修改:hgoldfish FROM 110.84.123.*
FROM 110.84.123.*
这些机制都是我自己弄出来的。在 c++ 语言社区还不普及。而且像我前面说的,c++ 标准库里面没有 cow 的 btree map.
如果最终能够形成一个新语言来完美实现这一整套的思路会更好一些。如果没有,我现在也用得挺舒服的,并不需要用 rust 来解决那些对我不存在的问题。
这就是我瞄了 rust 几眼得出的结论。不喜欢 rust 的,又觉得 c++ 有很多问题的程序员可以考虑考虑我这个方向。
我应该是全世界极少数拿 QtCore 的值语义 + 协程来做服务器后端的程序员哈哈。
【 在 milksea 的大作中提到: 】
: 我是是你这些不需要c++语言为你做什么改变啊
--
FROM 110.84.123.*
我前面有介绍了两种实现 cow 的方式啊。一种是类似于 Qt 的 QString, QByteArray 这些数据结构使用的,它其实就是个 shared_ptr<> d,读这个数据结构之前,拿住引用。那么写数据结构之前 memcpy() 复制一份内容,修改完修改这个 shared_ptr<> d 不会影响读。
另外一种是使用 b-tree,这种数据结构能够做到 cow 修改。所以普通地被各种数据库使用。rust 使用 b-tree 来实现 treemap,而 c++ 则是使用 rb tree,python 使用 hash table.
上次有人测过,说 btree map 的数据局部性更好,对 cpu cache 更友好,运行效率更快。不知道是不是真的。
【 在 ylh1969 的大作中提到: 】
: cow是个难题,我也没想好怎么弄COW。
--
修改:hgoldfish FROM 110.84.123.*
FROM 110.84.123.*
这肯定不算 COW 啊!
btree 数据结构你可以再研究研究一下,这个数据结构可以做到真正的 COW,是有个宝藏数据结构。
【 在 ylh1969 的大作中提到: 】
: 没研究那么深。用的btree,修改时锁死,独享,不让别人动。不知道能否达到COW的效果。
--
FROM 110.84.123.*