原文
标准C
定期改进,现在为C23
.但是还是没有解决一些怪东西
.Dlang
社区在D语言编译器
中嵌入了一个C编译器
,这样可编译C
.
该C编译器
(又名ImportC
)是从头开始构建的
.它提供了使用现代编译器技术
来修复这些缺点
的机会.为什么标准C
不修复它们?
1,计算常式
2,编译时单元测试
3,前向引用声明
4,导入声明
计算常式
请考虑以下C代码
:
int sum(int a, int b) { return a + b; }
enum E { A = 3, B = 4, C = sum(5, 6) };
用gcc
编译时:
gcc c test.c
test.c:3:20: error: enumerator value for C is not an integer constant
enum E { A = 3, B, C = sum(5, 6) };
^
即,虽然C
可在编译时
通过常量折叠计算简单式
,但它不能在编译时执行函数
.但ImportC
可以!
C语法
中出现C常式
的地方,只要这些函数无I/O
,访问可变全局变量
,系统调用
等操作,编译器就应该可在编译时执行函数
.
编译时单元测试
一旦C编译器
可(CTFE
)编译时求值函数,突然间,其他事情就变成可能
.
如,在C代码中很少看到单元测试
,很明显,在构建系统
中单元测试
需要一个单独的目标
,且必须按单独的可执行文件
构建和运行.
有点麻烦
表明它不可能!
int sum(int a, int b) { return a + b; }
_Static_assert(sum(3, 4) == 7, "test #1");
>gcc c test.c
test.c:3:16: error: expression in static assertion is not constant
_Static_assert(sum(3, 4) == 7, "test #1");
^
ImportC
可编译它.
这样不需要单独的构建
,就可以单元测试编译时
运行的函数.不需要额外的工作
.每次编译代码
时,都会运行单元测试
.我在ImportC
的测试包
中广泛使用它
.
前向声明引用
更多代码:
int floo(int a, char *s) { return dex(s, a); }
char dex(char *s, int i) { return s[i]; }
gcc c test.c
test.c:4:6: error: conflicting types for dex
char dex(char *s, int i) { return s[i]; }
^
test.c:2:35
:注意:前dex
的隐式声明在此:
int floo(int a, char *s) { return dex(s, a); }
如果floo
和dex
的顺序逆转,则编译正常
.即编译器只知道它在词法
上之前的内容.禁止前向引用
.
这岂是石器时代的编译器设计
?现代语言无此问题
,为什么在C
和C++
中持续存在?ImportC
不是现代语言
,但它是现代编译器
,接受任意顺序
的全局声明
.
为什么这很重要
?一般表明每个前向定义
都需要一个额外的声明
:
char dex(char *s, int i); //声明
int floo(int a, char *s) { return dex(s, a); }
char dex(char *s, int i) { return s[i]; } //定义
这样做,只是毫无目的
的折腾.这不仅烦人
,还会促使倒过来布局声明
.叶函数
排在最前面
,全局接口函数
排在最后
.就像自下而上
读报纸文章一样.毫无意义
.
ImportC
可按任意顺序
编译声明.
导入声明
给定三个floo.d,dex.h,dex.c
文件:
//floo.c
#include "dex.h"
int floo(int a, char *s) { return dex(s, a); }
//dex.h
char dex(char *s, int i);
//dex.c
#include "dex.h"
char dex(char *s, int i) { return s[i]; }
必须为每个外部模块
制作一个.h文件
是一项粗活
,更差,如果.h
文件与.c
文件不完全匹配
,你要花费大量时间
找出问题所在.
答案是什么:导入dex.c
!
//floo.c
__import dex;
int floo(int a, char *s) { return dexx(s, a); }
//dex.c
char dexx(char *s, int i) { return s[i]; }
甚至不必编写.h
文件.当然,这也适合ImportC
.
参考
导入C
D语言
:如果你在翻译单元
中工作,那会大大简化
,但这样可在不重复大量代码
时做更多的事情
.我想知道作者是如何解决它的
?
你是正确
的,因为正在求值的函数
的源码
必须可供编译器使用
.这可通过#include
来完成.我在D中
就是这样,并导入
了包含要求代码的模块
.
:如果可按宏表示测试
,这是可能的,如果添加第一点,则这就很简单.
当你想要测试函数
时,按宏表示测试
不管用.我给出的示例
很简单,很容易理解
.实际使用
可能更复杂.
:性能
D的编译速度
比C编译器
快,主要是因为
:
1
.C
预处理器是一头需要多次传递
的无可救药的猪
.我知道,我从头开始实现了多次
.C预处理器
在发明
时是个很好的设计选择
.
今天,它已变成化石
.我仍对为什么C++
从未来得及弃用它
感到敬畏.
2
.D
使用导入
而不是#include
.这样更快,因为不需要一遍遍地编译.h
文件.
D的策略
是分开解析与分析语义
.我想它确实慢了一点
,但它也不必重新编译重复的声明
并折叠成一个
.
当然,执行编译时函数
,可能是个瓶颈,但这(当然)根据它的使用程度
.喜欢轻触使用它
,性能很好
.如果使用它实现编译器
,它可能会很慢.
我并不是建议删除C
的#include
.导入
的东西将是额外的
.
:对这些问题有什么想法吗?
如果你正在使用黑客
,来在C语言
中用模板,则你已超越了这门语言
,需要一个更强大的语言
.D
拥有顶级的元编程
-如常
,其他模板语言
也在追随D的路径
.
:不能使用预编译头文件
?
你提到它很有趣.我在90
年代为SymantecC
和C++
实现了它们.
我再也不想这样做了!
它们很脆,是维护的噩梦.不过,它们确实加快了编译速度,但没有提供语义优势
.
使用D
时,我非常关注快速编译
,以至于预编译的头文件
无法提供足够的加速,因此值得付出痛苦
.
:我个人不喜欢前向引用
,因为它会使代码
更难读.你不能再依赖图的拓扑顺序
.
正如文章所写的,这会强制在顶部有私有叶函数
,而公共接口
在文件尾.可以说,正常方式
是在顶部放置公共接口
,而实现
则在"首屏下方"
.
作者是D的创建者
,所以他可能对D
没问题.D大概是25
岁.而Zig
只是一个蹒跚学步的孩子
.
是的.D
是其他语言
中许多最新功能
的源头
.
我开始研究D
的原因是C
和C++
非常不愿意向前发展
.
但更重要的是,Zig
故意没有实现D一大堆事情
.
有时需要吝啬
.zig
基本上是c--+
,其中+
是常式
的东西.
它会不断添加D功能
,比如常式
.
没人使用C++
模块,因为它们使用起来很笨拙
.D很容易
.注意:你可以自由复制D的模块设计
.这是最好
的.
:C++
的常式
经过17
年的发展,仍落后
于D
.在D中,垃集器
使得很容易分配内存
.此外,只有通过函数
取的路径
需要与CTFE
兼容,其他则不需要.
C语言:
#include <stdio.h>
void main()
{
int i;
for (i = 0; i < 10; ++i);
printf("%d\n", i);
}
//99%的C程序员找不到八哥!
而等价的D语言
:
import core.stdc.stdio;
void main()
{
int i;
for (i = 0; i < 10; ++i);
printf("%d\n", i);
}
给你:
test.d(5): Error: use `{ }` for an empty statement, not `;`
赶紧的,标准C
,修复它!