当前位置: 首页 > news >正文

2312d,D语言单元测试等

原文
我最近决定在"系统编程"领域试些小众语言.我已用了Java,DartKotlin高级语言编程多年了(并试了许多其他相同级别或更高级的语言),需要扩大视野,因为对某些类型应用,这些语言并不是最好的工具.

这篇博文中,我想重点介绍D语言这里,经过一些初步实验,它比其他语言更能引起注意.
我还尝试了ZigNim12,但觉得它们不适合我,至少现在是这样.
当然,我已试过Rust,但是Rust虽然在很多方面都是个天才语言,但并没有真正让我对编写代码感到兴奋.相反,一想到周末要花时间与借用检查器作斗争,我就充满了恐惧.

我绝对会在工作环境中使用Rust(且已这样了),因为它的安全保证(不仅是内存安全,还有资源和线程安全)和出色的性能(在低内存消耗原始速度方面),但对业余爱好项目,谢谢.

在我看来,Nim(另一非常有趣语言),在另一端走得太远了,安全不如速度和快乐重要.因此,如果你喜欢它(速度非常快,创建微小二进制文件且使用很少的内存),它可能只是适合你的语言.

Zig有很多承诺,但目前还没有准备好.尽管它专注于简单性,但也非常冗长且难以正确使用.

D似乎是个很好的平衡.它很容易熟悉,同时有些非常有趣的功能.它已存在了足够长的时间,已足够稳定.

本文,我想分享我所学到的东西,重点元编程单元测试.

D简介

2023年,D并不是一门新语言.它自2001年以来一直存在,但从那时起已有了很大的发展,特别是自2010年左右的D2版本稳定以来.

它有3个不同且维护良好的编译器,下载页:

1,DMD是用D自身编写的参考编译器.
2,GDC是D的GCC前端.
3,基于LLVMLDC.

DMD一般用来更快编译(事实上,它可能是生产级语言中最快编译器之一),但其他两个一般更擅长优化运行时速度.

在介绍D的功能方面D语言旅游做得非常出色,而D的Gems部分特别有趣,因为它展示了D有的,而大多数其他语言所没有的东西,如(UFCS)统一函数调用语法,域保护,(CTFE)编译时函数求值,(如@safe,@nogc,@mustuse)属性等等.

另见包括消息传递和线本存储的多线程节,用它们来共同支持使用类似Actor模型来编写并发代码.
讨论更高级功能前,先展示一些D示例.

下面显示了D切片的实际效果:

import std.stdio : writeln;
void main()
{int[] test = [ 3, 9, 11, 7, 2, 76, 90, 6 ];test.writeln;writeln("First element: ", test[0]);writeln("Last element: ", test[$ - 1]);writeln("Exclude the first two elements: ",test[2 .. $]);writeln("Slices are views on the memory:");auto test2 = test;auto subView = test[3 .. $];test[] += 1; //将每个元素递增1test.writeln;test2.writeln;subView.writeln;//创建空切片assert(test[2 .. 2].length == 0);
}

编译并运行它:

  dmd -of=slices slices.d./slices 
[3, 9, 11, 7, 2, 76, 90, 6]
First element: 3
Last element: 6
Exclude the first two elements: [11, 7, 2, 76, 90, 6]
Slices are views on the memory:
[4, 10, 12, 8, 3, 77, 91, 7]
[4, 10, 12, 8, 3, 77, 91, 7]
[8, 3, 77, 91, 7]

还可直接dmd -runfile.drdmd(DMD自带),从源码运行D程序.甚至可按脚本运行:

#!/usr/bin/env rdmd

它显示了许多有趣的特征.
1,test.writelnwriteln(test)相同.这就是UFCS.
2,test[$-1],显示了如何在[]中按数组/切片长度使用$符号.
3,test[2..$],类似同样使用$Go典型切片.
4,test[]+=1,显示了可由编译器优化的向量运算.
5,assert(test[2 .. 2].length == 0);,D断定,稍后用来测试单元.
相当不错.

D元编程

D有许多元编程功能.元编程是针对程序自身编程的编程.
Lisp可能是使用宏元编程的先驱,但并不是元编程的唯一方法.
如,如下例所示,D有允许在编译时检查类型的模板,以特化函数:

@safe:
auto concat(T)(T lhs, T rhs) {static if (is(T: double)) {//T可转换为双精return lhs + rhs;} else {//'~'一般是D中的`连接`符号return lhs ~ rhs;}
}
unittest {assert(2.concat(3) == 5);assert(4.2.concat(0.8) == 5.0);assert("Hello".concat(" D") == "Hello D");
}

运行单元测试:

  dmd -w -main -unittest -run tests.d //1个模块通过单元测试

该示例有点傻,因为D支持重载符号这里,所以只能这样.

如果熟悉Java,concat类似通用静态方法,但与Java不同,D允许编译时检查类型,因此可专门针对某些类型特化函数.

static if是编译时执行的if块,运行时不存在.
注意,模板有两个参数列表:一个包含编译时参数,另一个包含运行时参数.如果D编译器可推导编译时参数,则可省略它.
可用!符号显式提供编译时参数.

如,std.conv标准模块中的to模板,把类型当参数,但因为一般无法推导,因此几乎总是显式传递:

unittest {import std.conv: to;assert(42.to!string == "42");
}

而这只是最基本的D模板.
还可用template关键字来执行更高级操作,如生成多个函数:

template BiDirectionalConverter(T1, T2) {import std.conv: to;T2 convert(T1 t) {return t.to!T2();}T1 convert(T2 t) {return t.to!T1();}
}
unittest {alias StringIntConv = BiDirectionalConverter!(string, int);assert(StringIntConv.convert("20") == 20);assert(StringIntConv.convert(20) == "20");
}

std.conv中的八进制(octal)模板,用来在D中声明编译时的八进制:

void main() {import std.stdio: writeln;import std.conv;writeln(octal!"750");
}

运行:

  dmd -run tests.d
488

强烈建议浏览D模板教程,以了解更多信息.

D中的另一个模板是插件模板.它是一个允许好像在周围域内编写它一样,直接调用点粘贴代码的复制和粘贴模板.

mixin template Abcd(T) {T a, b, c, d;
}
unittest {mixin Abcd!int;a = 10;assert(a == 10);assert(b == 0);assert(c == 0);assert(d == 0);
}

最后,还可用串插件这里生成代码串:

///用T类型的`a,b`和c字段构建`一个结构`.
string abcStruct(T)(string name) {return "struct " ~ name~ " { "~ T.stringof ~ " a; "~ T.stringof ~ " b; "~ T.stringof ~ " c; "~ " }\n";
}
unittest {mixin(abcStruct!string("StringStruct"));mixin(abcStruct!int("IntStruct"));auto abcstr = StringStruct("hey", "ho", "let's go");assert(abcstr.a == "hey");assert(abcstr.b == "ho");assert(abcstr.c == "let's go");auto abcint = IntStruct(42);assert(abcint.a == 42);assert(abcint.b == 0);assert(abcint.c == 0);
}

D可用-mixin标志创建包含编译过程中生成的所有插件文件:

  dmd -w -main -unittest -mixin=mixins.d -run tests.d1个模块通过单元测试

现在,查看mixins.d文件,找到D编译器生成的结构:

//测试.d中扩展.d(67)
struct StringStruct { string a; string b; string c;  }
//测试.d中扩展.d(68)
struct IntStruct { int a; int b; int c;  }

或,用pragma编译指示,以便编译时D仅打印生成代码:

pragma(msg, abcStruct!double("DoubleStruct"));
//dmd -w -main -of=tests tests.d
//结果:
struct DoubleStruct { double a; double b; double c;  }

更多的mixin技巧
官方D文档中的(Parser)代码生成示例,显示了编译时很容易解析来生成常量配置数据.

单元测试

前例中,使用unittest块来演示D的一些功能.我想很明显,编译单元中一般不包含这些块中代码,因此编译器运行测试时,必须传递-unittest选项给编译器(要实际运行测试,或执行生成的二进制文件,加上-run选项).

回顾下,如下单元测试:

unittest {assert(2 + 2 == 4);
}

把上面的4更改为5并运行代码:

  dmd -w -main -of=tests -run tests.d

使用-main选项,以便编译器在没有函数时生成空main函数.
-w标志,按错误对待警告,-of来命名输出文件.用--help查看所有选项.

如果不打印内容,则所有测试都正常.即没有运行测试.
现在用-unittest重试:

  dmd -w -main -unittest -run tests.d`tests.d(18):[unittest]unittest`失败`1/1`模块失败的单元测试

输出非常简单.它只是告诉你有多少模块的测试失败了,及断定失败的文件和行.
快速测试来说不错,但最好告诉失败的真正原因论坛.

如,这是我想出的一个显示失败断定期望结果和实际结果的小模板,来使断定更强大:

auto assertThat(string desc, string op, T)(T lhs, T rhs) {import std.conv: to;const str = "assert(lhs " ~ op ~ " rhs, \"" ~desc ~ ": \" ~ lhs.to!string() ~ \" " ~ op ~ " \" ~ rhs.to!string())";return mixin(str);
}

现在,断定如下:

unittest {assertThat!("adding two and two", "==")(2 + 2, 5);
}

运行它:

  dmd -w -main -unittest -run tests.d`tests.d-mixin-20(20):[unittest]`加二加二`:4==5``1/1`模块失败的单元测试

真酷!
顺便,D单元测试一般来验证函数属性是否符合期望(D编译器一般会推导它们,给每个函数手动注解大量属性非常麻烦).
如,在D中实现时,我试测试:

@safe @nogc nothrow pure unittest {auto tree = Tree([0,0,0,2], [10,11,12,13]);assertThat!("children(2) basic case", "==")(tree.children(2), [3, -1]);
}

仅当按@safe @nogc nothrow pure注解,推导unittest中使用的函数时,才有效(编译器会传递性检查这些函数).
结果如下:

   myd dmd -unittest -run source/app.d 
...一堆错误略...

很有意思!

另一个常见用例是只运行单个测试,编译器不支持,但你可自己做,正如@jfondrenD论坛上所示:

module tester1;
unittest { assert(true); }
unittest { assert(!!true); }
unittest { assert(1 != 1); }
unittest { assert(1 > 0); }
version (unittest) {bool tester() {import std.meta : AliasSeq;import std.stdio : writef, writeln;alias tests = AliasSeq!(__traits(getUnitTests, tester1));static foreach (i; 0 .. tests.length) {writef!"Test %d/%d ..."(i + 1, tests.length);try {tests[i]();writeln("ok");} catch (Throwable t) {writeln("failed");}}return false;}shared static this() {import core.runtime : Runtime;Runtime.moduleUnitTester = &tester;}
}
void main() {assert(false); //这不会运行
}

运行它:

  dmd -w -main -unittest -run tests.d测试1/4...好测试2/4...好测试3/4...失败测试4/4...

非常整洁,但可能不是你想要的.

这使用了如getUnitTests特征等相当高级的东西(D特征是元编程,如果你来自RustScala,概念可能不一样)和UDA(编译时注解)这里.
dub包管理

最后

IDE支持似乎还不错,但与Java,Kotlin,Typescript甚至Rust等主流语言相去甚远.
我首先试使用emacs(你需要获得d模式,然后安装serve-d这里,LSP服务器,也支持VSCode的D支持).
然后注意到DIntelliJ插件非常强大,并且作为Jebrains产品的大用户,很好惊喜(一般,小众语言在IntelliJ中没有很好的支持)!

IntelliJ插件的开发者致敬!它提供了非常好的开箱即用体验,来生成片段漂亮模板,代码浏览(包括进入Dstdlib,非常适合学习),内置文档风格精美,有扫描器,因此在代码中显示警告,通过dfmt自动格式化,内置支持dub.
如果用d-unit作为依赖,甚至可运行测试.

D,还支持CPU和内存分析,及非常好的文档工具ddoc这里,与在Rust中一样,可在编译时执行D文档,确保文档示例总是有效!

我厌倦了像Java这样基于VM的语言,并且不太喜欢编写Rust,D可能会成为我下个最喜欢的语言.

相关文章:

  • 超级计算机与天气预报:精准预测的科技革命
  • x3::forward_ast
  • 七大主流的HttpClient程序比较
  • 国内外网络安全现状与存在的问题。应该如何缓解或解决这些问题
  • 51单片机应用从零开始(十一)·数组函数、指针函数
  • 龙芯loongarch64服务器编译安装gcc-8.3.0
  • 虚拟化之问答
  • html css概念
  • 【极客公园 IF 2024】李彦宏:AI-native时代,需要怎样的产品和开发者
  • 【JavaSE】Java入门九(异常详解)
  • 带你手把手解读rejail沙盒源码(0.9.72版本) (七) fnetfilter
  • 将 OpenLDAP 与 IBM Spectrum LSF 集成
  • 工业数据的特殊性和安全防护体系探索思考
  • 数据结构--图
  • VuePress安装及使用——使用 Markdown 创建你自己的博客网站和电子书
  • (三)从jvm层面了解线程的启动和停止
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 【5+】跨webview多页面 触发事件(二)
  • 03Go 类型总结
  • Android框架之Volley
  • css布局,左右固定中间自适应实现
  • css属性的继承、初识值、计算值、当前值、应用值
  • java8-模拟hadoop
  • Logstash 参考指南(目录)
  • SOFAMosn配置模型
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 从0实现一个tiny react(三)生命周期
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 基于HAProxy的高性能缓存服务器nuster
  • 利用DataURL技术在网页上显示图片
  • 突破自己的技术思维
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 用jquery写贪吃蛇
  • 用简单代码看卷积组块发展
  • 栈实现走出迷宫(C++)
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • (2)STL算法之元素计数
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • .Net接口调试与案例
  • .Net中ListT 泛型转成DataTable、DataSet
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @ConfigurationProperties注解对数据的自动封装
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(白虎组)
  • [14]内置对象
  • [Android] 修改设备访问权限
  • [BZOJ4016][FJOI2014]最短路径树问题
  • [C# WPF] DataGrid选中行或选中单元格的背景和字体颜色修改
  • [cocos creator]EditBox,editing-return事件,清空输入框
  • [daily][archlinux][game] 几个linux下还不错的游戏
  • [EFI]Dell Inspiron 15 5567 电脑 Hackintosh 黑苹果efi引导文件
  • [Go WebSocket] 多房间的聊天室(三)自动清理无人房间
  • [HNOI2008]玩具装箱toy
  • [Linux]history 显示命令的运行时间