搜索

iVocaloid论坛

查看: 2349|回复: 13
打印 上一主题 下一主题

【记录】关于是否去除RUtil2的观察、讨论 [复制链接]

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
跳转到指定楼层
[1L]楼主
Zleepwalking 发表于 2014/10/17 14:26:49 |只看该作者 |倒序浏览
本帖最后由 Zleepwalking 于 2014/10/17 14:27 编辑

从RUtil2-1.0出现以来,开发者中存在一些关于保留或移除RUtil2的争议。应当意识到,RUtil2即带来了一些便利,又造成了一些麻烦。
主要作者Sleepwalking认为要解决争议需要更慎重的考虑,这依赖于长期的观察,于是决定在一段时间内保留RUtil2,此后再决定是否移除。观察期内任何开发者都可以在这里记录下RUtil2的优势或劣势。
需要注意的是,开发者应当明确讨论对象:是名为"RUtil2"的库,还是该库的对象模型"RObject",还是该库的模板实现"RTemplate"、多态实现"RInterface"、字符串类"String"、等等。这里的讨论对象涵盖了上述所有,即"RUtil2"库本身及该库的任意组成部分,故任何评论应当指明所针对的组成部分。而讨论的结果将决定具体移除或改进RUtil2的哪些部分

观察期自今日起,至2015年2月1日止。
若在2015年2月1日所收集的证据不足以下结论,观察期延长至2015年5月1日。


...Sleepwalking(Hua Kanru)
...2014.10.17



知识共享许可协议 除非另有声明,本帖内容采用 署名-非商业-相同方式共享 3.0 许可协议 授权,且需注明出处,所有权利归发帖人。

使用道具 举报

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
[2L]沙发
Zleepwalking 发表于 2014/10/17 14:54:54 |只看该作者
RObject对象模型
优点:解构多个对象时很方便,如
  1. RDelete(& a, & b, & c, ...);
复制代码
在错误处理时优点能较明显地体现:
  1. Type1 a, b;
  2. Type2 c;
  3. Type1_Ctor(& a);
  4. if(! Type1_Func(& a))
  5. {
  6.       RDelete(& a);
  7.         return 0;
  8. }
  9. Type1_Ctor(& b);
  10. if(! Type1_Func(& b))
  11. {
  12.         RDelete(& a, & b);
  13.         return 0;
  14. }
  15. Type2_Ctor(& c);
  16. if(! Type2_Func(& c))
  17. {
  18.         RDelete(& a, & b, & c);
  19.         return 0;
  20. }
  21. RDelete(& a, & b, & c);
复制代码

优点:跨库的多态实现(RInterface):跨库的回调函数也可以根据不同对象类型采取不同行为,而对于对象所在库的依赖关系没有限制。

优点/缺点:提倡位于栈上的对象,如:
  1. Type a;
  2. Type_Ctor(& a);
  3. ...
复制代码
而不是
  1. Type1* a = Create_a();
复制代码
这减少了一次内存分配,但代价是需要多写一行代码。

缺点:编译器实现依赖(需要gcc扩展)

缺点:链接困难(需要考虑初始化函数调用顺序)

RTemplate模板宏
优点:优点即模板功能本身
缺点:作为一种静态模板,会导致臃肿的命名。
缺点:编译器实现依赖(需要gcc/clang的预处理方式)

使用道具 举报

TukuZaZa

来自开源界的一只企鹅

Lv.4-触手吸盘

Rank: 4Rank: 4

0
6
4


UID: 75564
权限: 30
属性: 宇宙人
发帖: 176 (0精)
积分: 312
章鱼: 2
大葱: 12
茄子: 310
注册:2011/1/20
存在感:170

创作者

[3L]板凳
tuxzz 发表于 2014/10/17 17:30:30 |只看该作者
我说最好用c++
顺便加上引用计数的自动垃圾回收

使用道具 举报

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
[4L]地板
Zleepwalking 发表于 2014/10/17 17:50:14 |只看该作者
tuxzz 发表于 2014/10/17 17:30
我说最好用c++
顺便加上引用计数的自动垃圾回收

这里只讨论RUtil2的优劣

使用道具 举报

StarBrilliant

Lv.2-鱼仔酱

Rank: 2Rank: 2

0
0
0


UID: 129091
权限: 10
属性: 両性
发帖: 25 (0精)
积分: 24
章鱼: 0
大葱: 1
茄子: 470
注册:2014/2/7
存在感:97
[5L]萝莉
m13253 发表于 2014/10/18 00:58:08 |只看该作者
本帖最后由 m13253 于 2014/10/18 01:17 编辑

没耐心的就直接看最后一句好了。

# RUtil2 作为一个 C 的 OOP 扩展的存在

## 先说 C++

首先,C++ 的发明就是为了解决 OOP 的问题的。
它原本也是「预处理」(传说中的 cfront),后来变成了「编译器原生支持」。

那么 RUtil2 能在保证多态性的同时解决以下几个问题吗?
1、编译期类型推定(CTTI)和执行期类型推定(RTTI),这是多态的重要部分,也需要语言的部分支持
2、效率够高吗?
3、能支持 GCC、Clang、MSVC、ICC 等主流编译器吗?(只跨平台不跨编译器不是真正的跨平台)
4、能搞定 try catch 吗?

## RUtil2 作为 OOP 扩展的优势

1、ABI 兼容性比 C++ 好很多,但是还是没有 C 好。比如依然不能增删虚函数
2、编译快很多倍
3、语法清晰,没有 C++ 那样歧义太多,甚至 C++11 之前 >> 都能引起歧义

## C++ 作为 OOP 扩展的优势

1、贡献者很可能已经会 C++ 了,所以不需要再学习新技术
2、优秀的 C++ 开发者比优秀的 C 开发者多(这是 GCC 作者说的)
3、编译器知道怎么优化,比如 C++11 的构造可以被优化成 memcpy,const 或右值引用时对象复制可以被优化成只复制指向对象的一个指针
4、几乎所有主流编译器支持 C++,只有一小部分编译器支持 RUtil2。
5、C++ 更不容易写出糟糕的代码——申请内存不需要检查返回值,字符串拼接用 a+b 还不需要惦记溢出
6、因为没有奇怪的预处理,所以调试起来没有 RUtil2 让人头晕
7、可以用所有能看懂 C++ 代码的软件来分析代码,包括自动补全,(as long as Qt is not used)

## P.S. 你说的那个 create 的问题,可以用 alloca 来搞定。


# 关于 RTemplate 模板库和 C++11 的 auto

## C++11 的 auto 可以缓解因为模板带来的麻烦,或许解决的问题和 RTemplate 遇到的很类似

比如 C++98 曾经这样写:
  1. template <typename T>
  2. T max(T a, T b) {
  3.     return a > b ? a : b;
  4. }
复制代码
C++11 现在可以简单地写成:
  1. auto max(auto a, auto b) {
  2.     return a > b ? a : b;
  3. }
复制代码
这里函数的返回值和参数全是 auto,会在编译期根据使用情况(int、float、double、std::string 等只要可以比较大小的类型)推断出合适的函数原型。

C++98 曾经这样遍历动态数组:
  1. int a_init[] = {0, 1, 2, 3, 4};
  2. std::vector<int> a(a_init, 5);
  3. for(std::vector<int>::iterator i = a.begin(); i != a.end(); i++) {
  4.     cout << *i << endl;
  5. }
复制代码
C++11 现在改进成:
  1. std::vector<int> a = {0, 1, 2, 3, 4};
  2. for(auto i : a) {
  3.     cout << i << endl;
  4. }
复制代码
这里 for(auto i : a) 在编译期会自动解析成 for(const std::vector<int>::iterator it = a.cbegin(); it != a.cend(), const int &i = *it; it++)

于是,RTemplate 似乎不能实现这些特性,所以才让命名变得奇葩。


# RUtil2 做为一个可插拔式跨平台兼容层

## 从 malloc 说起

例:比如实现了一个 RMalloc,在底层调用 malloc 或者更高效的 jemalloc。哪天发现内存泄漏了,把 RMalloc 改成 dmalloc 后端就可以调试。
类似的还有 pthreads。

## 谈谈 GLib2 和 Apache Portable Runtime

这两货都是为了跨平台而生的,和 RUtil2 功能类似。那么,为什么要造一个 RUtil2 的轮子呢?
(不要说 GLib2 附赠的那个 GObject,完全可以不用它。它的存在主要是为了处理窗口里的对象的,而不是继承和多态。)

GLib2 和 APR 都是编过千万次测试的成熟库,RUtil2 有能力做到嘛?
况且 GLib2 和 APR 支持的平台数目远远多于 RUtil2。

所以说,我建议上现有的库。只要许可证没有问题。

## 定制性和依赖

或许 GLib2 和 APR 太大了?(APR 已经够小了,要知道 Qt 的兼容库更大……)
那么去找一个可以 ./configure 定制的,且许可证兼容的库吧。


# 关于造轮子

## 为什么反对造轮子

年长的贡献者不愿意学习新技术了。他们认为自己会的东西已经够用了。
所以使用流行——即很多人都会——的技术有利于给项目吸引更多贡献者。

## 如果真的要造轮子

例:你知道一帮开发者为了能在 Linux 上使用熟悉的 C# 开发程序,而造了个 Mono 吧?
Mono 的存在不仅仅是为了模拟 Windows 程序,更是很多 Linux 原生程序(Banshee 等)的基础。

所以说,宁可搞个 Mono 去使用熟悉的 API 也不愿意学习新的语言,是比我们长一辈的人的思维。

因此,即使造个轮子,要让 API 长得很像现有的轮子。
如果真的做不到,请让学会旧轮子的人学新轮子好入门。

## 不要和现有标准冲突

先举符号名一个例子:

比如符号名 _[_A-Z] 在 C99 和 C++11 中是禁止的,
_Z _$ etext _etext __etext edata _edata __edata _try __try _catch __catch _except __except _finally __finally 也是看似没有问题,实际上不能安全使用的符号名。

再举文件类型:

C 语言的合法扩展名是 c(小写 c,大写表示 C++)。使用 rc 做扩展名很有可能会让某些 build script 或者 Makefile generator 自作聪明调用 windres 去处理 rc 文件。(当然 CMake 这种相比 QMake 或 automake 来说自动化程度如此之低的构建系统来说恰巧没有遇到这个问题)

实际上 RUtil2 现有的代码和标准冲突的地方太多。这可能会在一个环境下凑巧编译运行成功,在另一个环境下莫名其妙挂。
上次 RUCE 爆音在 build=x86_64-linux-gnu host=i686-w64-mingw32 下会挂,在 build=host=i386-msys-mingw32 下却没有挂的原因,我猜测和使用了某些非标准的东西导致降低可移植性有关。

## 有则改之无则加勉

我上面说的不一定和实际一致。我对 RUtil2 的了解仅限于看过少量代码。
所以,有则改之无则加勉。


# 我的总结

1、RUtil2 可以去除,我鼓励去除
2、作为面向对象功能,RUtil2 的预处理做得很 awkward。面向对象可以让 C++11 去做(如果你不喜欢 C++ 而且有时间和精力可以自己造个轮子,写一个像 cfront 或者 valac 那样的到 C 语言的翻译器吧)
3、作为跨平台兼容库,RUtil2 只帮了倒忙。不如改用 Apache Portable Runtime 或其它许可证兼容的库

使用道具 举报

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
Zleepwalking 发表于 2014/10/18 12:24:51 |只看该作者
m13253 发表于 2014/10/18 00:58
没耐心的就直接看最后一句好了。

# RUtil2 作为一个 C 的 OOP 扩展的存在

谢谢反馈。

使用道具 举报

TukuZaZa

来自开源界的一只企鹅

Lv.4-触手吸盘

Rank: 4Rank: 4

0
6
4


UID: 75564
权限: 30
属性: 宇宙人
发帖: 176 (0精)
积分: 312
章鱼: 2
大葱: 12
茄子: 310
注册:2011/1/20
存在感:170

创作者

[7L]大姐姐
tuxzz 发表于 2014/10/18 13:24:20 |只看该作者
本帖最后由 tuxzz 于 2014/10/18 15:03 编辑

首先我赞同5L的观点
然后我在他的基础上补充几条:

# RUtil2的优势的补充
1. 语法较CXX相对简单
2. 由于不会自动构造/折构,开发者可以在任何时间构造/折构一个在stack上的类。提高可控性。

# RUtil2的劣势的补充
1. 因为使用大量的宏,在编译时错误输出有时候会变得非常混乱
2. 如果一个RTemplate类中的代码出现一处错误,这一处错误会被报告1遍以上,造成输出混乱
3. 无法重载运算符是很不方便的:
    例如:1. 链接两个String需要String_Join(dest, sorc),没有直接a += b直观方便。
                2. 2个同等长度的vector相加,直接重载operator+和operator+=在使用时较其他方式更方便。
                3. 如果有运算符重载,我们可以为array写两个operator[],一个会检查是否下标越界,方便调试,另一个不检查,提高性能。但是RUtil2不能。
4. 无法实现自动构造以及自动折构,这会导致:
    1. 很难实现String的cow及其他一些功能
    2. 增大了开发者的输入量。
    3. 容易出现忘记构造/折构的问题。
6. 文档少,新人很难上手。

# 我的意见
1. 将RUtil2写成一个CXX的跨平台工具集。






8899999999889988888899888888999998888998888998888888999998888899999999889999999988888888888889988888888888888899888888
8899888888889988888899888889988899888998889988888889988899888899888888889988888888888888888998899888888888889988998888
8899888888889988888899888899888889988998899888888899888889988899888888889988888888889999899888888998899998998888889988

使用道具 举报

StarBrilliant

Lv.2-鱼仔酱

Rank: 2Rank: 2

0
0
0


UID: 129091
权限: 10
属性: 両性
发帖: 25 (0精)
积分: 24
章鱼: 0
大葱: 1
茄子: 470
注册:2014/2/7
存在感:97
[8L]实妹
m13253 发表于 2014/10/18 15:57:50 |只看该作者
tuxzz 发表于 2014/10/18 13:24
首先我赞同5L的观点
然后我在他的基础上补充几条:

有一点我要纠正,C++ 在编译时的错误输出也很混乱(,但是只要看第一条错误信息就可以了,况且 RUtil2 的错误输出更乱)。

另外,请为「APR」和「造轮子」那两节再补充一点吧。

使用道具 举报

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
Zleepwalking 发表于 2014/10/19 19:45:34 |只看该作者
tuxzz 发表于 2014/10/18 13:24
首先我赞同5L的观点
然后我在他的基础上补充几条:

重载operator[]检查下标越界的想法很好,用这个来实现CDSP2_InfWave会方便很多。如果用C++重写,我希望最大限度利用标准库的功能,不知能不能直接跳过RUtil2。

使用道具 举报

TukuZaZa

来自开源界的一只企鹅

Lv.4-触手吸盘

Rank: 4Rank: 4

0
6
4


UID: 75564
权限: 30
属性: 宇宙人
发帖: 176 (0精)
积分: 312
章鱼: 2
大葱: 12
茄子: 310
注册:2011/1/20
存在感:170

创作者

[10L]幼驯染
tuxzz 发表于 2014/10/20 22:00:10 |只看该作者
Zleepwalking 发表于 2014/10/19 19:45
重载operator[]检查下标越界的想法很好,用这个来实现CDSP2_InfWave会方便很多。如果用C++重写,我希望最 ...

能用标准库的地方当然要用,但是如果标准库功能或者性能达不到要求我们还是有必要在RUtil2里加相应功能的

使用道具 举报

TukuZaZa

来自开源界的一只企鹅

Lv.4-触手吸盘

Rank: 4Rank: 4

0
6
4


UID: 75564
权限: 30
属性: 宇宙人
发帖: 176 (0精)
积分: 312
章鱼: 2
大葱: 12
茄子: 310
注册:2011/1/20
存在感:170

创作者

[11L]怪蜀黍
tuxzz 发表于 2014/10/21 19:04:26 |只看该作者
本帖最后由 tuxzz 于 2014/10/21 19:05 编辑

8F提出了要求对APR和造轮子两节补充一下,在此补充:

对于APR,我持中立。
  • 它可以节省我们造轮子的时间。
  • 开发组部分成员需要学习这个新的库需要时间。
  • 它提供的功能相对简单,写一个也花不了太多时间(比写一个RUtil2时间少的多)。
  • 它可能会使我们项目的代码风格不统一。
  • 我不知道它对C++是否友好。

对于造轮子,依然中立:
  • 会花费我们更多的时间
  • 可以统一代码风格
  • 可以全面了解接口及实现
  • 可以更适合我们的项目
  • 可能引入不必要的bug


各有利弊吧。
我个人意见是写一个RUtil++,作为跨平台兼容层。
8899999999889988888899888888999998888998888998888888999998888899999999889999999988888888888889988888888888888899888888
8899888888889988888899888889988899888998889988888889988899888899888888889988888888888888888998899888888888889988998888
8899888888889988888899888899888889988998899888888899888889988899888888889988888888889999899888888998899998998888889988

使用道具 举报

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
12#
Zleepwalking 发表于 2014/10/25 15:58:33 |只看该作者
tuxzz 发表于 2014/10/21 19:04
8F提出了要求对APR和造轮子两节补充一下,在此补充:

对于APR,我持中立。

其实我现在有点意向把命名风格改成小写:
csvp_hnmframe_tocontour(csvp_hnmframe* ths, csvp_hnmcontour* dst...);

关于RUtil的延续,我们现在一个重要的问题是库间的耦合性太高了:离开了CVESVP,RUCE不能用;离开了CVEDSP,CVESVP不能用;离开了RFNL,CVEDSP不能用;离开了RUtil2,全都不能用……

RUCE -> CVESVP -> CVEDSP2这条依赖是没办法的,开源界甚至都没有什么能替代的库(libsms处在各种bug+多年无人维护状态;Essentia只能分析不能合成,而且windows兼容性很差;praat太畸形+缺很多功能)。

我希望降低CVEDSP2到RFNL的耦合,变为可选依赖(或者使RFNL成为可代替依赖)。若可行甚至连RFNL, CVEDSP2, CVESVP到RUtil的依赖也扔掉。为了尽量通用,RFNL应该用C写,或者至少有个C的API;API完全使用标准库的数据类型。

另外APR看上去并不那么简单,RUtil2只花两个月就基本完成了(而且不具备线程控制的函数)。

使用道具 举报

StarBrilliant

Lv.2-鱼仔酱

Rank: 2Rank: 2

0
0
0


UID: 129091
权限: 10
属性: 両性
发帖: 25 (0精)
积分: 24
章鱼: 0
大葱: 1
茄子: 470
注册:2014/2/7
存在感:97
13#
m13253 发表于 2014/10/26 12:24:05 |只看该作者
本帖最后由 m13253 于 2014/10/26 12:26 编辑
Zleepwalking 发表于 2014/10/25 15:58
其实我现在有点意向把命名风格改成小写:
csvp_hnmframe_tocontour(csvp_hnmframe* ths, csvp_hnmcontour ...

我建议 API 尽量都是 C 风格的(extern "C"),即使用 C++ 或其它语言实现它。
因为 C++ 只要改类里的一个成员的定义就可能导致旧版 DLL 和新版 DLL 二进制 ABI 不兼容。
而且不同 C++ 编译器(甚至同一编译器的不同版本)编译出来的 DLL 不能混用,C 却可以。

但是用 C 接口就意味着字符串很头疼,复杂的结构在传递的时候不能利用 C++11 的 move 特性来减少内存复制。所以还需酌情考虑实现方案,比如工具类库可以用 C++ 接口(因为只有 C++ 的使用者),算法类库可以用 C 接口(考虑到 ABI 问题)。

APR 确实不简单,因为一个 HTTP 服务器都能够在 APR 的支持下工作。如果学习成本高于造轮子的成本,不如试着造个轮子吧。
我现在正在造一个 LibRocaPort 的轮子,计划实现进程、线程、动态库管理、字符串管理等功能。接口部分还没有定,求建议和意见。

P.S. 我才学 C++ 不到一个月,在这一个月里,我似乎明白了 C++ 作者为什么要创造 C++ 了。不仅仅是有「对象」那么简单。

使用道具 举报

Sleepwalking

我不是技术宅!

Lv.5-章鱼须

Rank: 5Rank: 5Rank: 5

0
9
0


UID: 111156
权限: 40
属性: 宇宙人
发帖: 201 (1精)
积分: 540
章鱼: 3
大葱: 14
茄子: 2688
注册:2012/8/18
存在感:476
14#
Zleepwalking 发表于 2014/10/26 17:32:06 |只看该作者
m13253 发表于 2014/10/26 12:24
我建议 API 尽量都是 C 风格的(extern "C"),即使用 C++ 或其它语言实现它。
因为 C++ 只要改类里的一个 ...

感谢建议。
关于字符串。看应用场合——Rocaloid重心还是放在信号处理和机器学习上的,故我认为API使用C-style字符串足够了。
关于造轮子和C++。建议在足够了解前先别急着造轮子。失败案例:SPKit(万恶之源;RUtil的由来;2013年6月)。

使用道具 举报

您需要登录后才可以回帖 登录 | 注册/sign up

申请友链|Archiver|iVocaloid - 自由,开放,合作,共享    | 版权持有者点击这里进行举报

GMT+8, 2025/6/7 17:55

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部