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可能会成为我下个最喜欢的语言.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/236004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 的效率提升主要表现在哪些方面?

Vue3带来了许多性能优化和效率提升的特性。本文将重点讨论Vue3在静态提升、预字符串化、缓存事件处理函数、Block Tree和PatchFlag方面的改进。我们将通过对比Vue2和Vue3的编译结果来说明这些方面的效率提升。 静态提升 在Vue2中,每次渲染时都会重新创建VNode节点…

linux | 软连接与硬链接 | 实测

很多知识,我们都是通过学习其他博主的博客,可是学习的过程 常常需要辩证的去辨别真伪。这很耗精力,最好的方法就是实践去检测。 软链接 和 应链接 在 linux 上操作非常重要 常见场景:最重要的就是,做备份,尤…

Talk | 约翰霍普金斯大学博士生魏晨: De-Diffusion-文本是不同模态的沟通桥梁

本期为TechBeat人工智能社区第557期线上Talk。 北京时间12月20日(周三)20:00,约翰霍普金斯大学博士生—魏晨的Talk已准时在TechBeat人工智能社区开播! 她与大家分享的主题是: “De-Diffusion-文本是不同模态的沟通桥梁题”,介绍了她的团队在如…

面向对象程序设计(异常,RTTI,泛型,动态加载)

异常 首先,我们知道Java中的异常分为两大类:编译时异常和运行时异常。编译时异常通常是一些在编写代码时就能发现的错误,比如文件找不到之类的;而运行时异常则是在程序运行过程中出现的,比如除零错误。 我们可以使用…

Linux---进程状态

目录 一、系统进程状态介绍 1.运行状态 2.阻塞状态 3.挂起状态 二、Linux中的进程状态 1.R (running) 2.S (sleeping) 3.D(disk sleep) 4.T(stopped) 5.t(tracing stop) 6.X(dead&am…

liunx安装git

安装 Git 更新包列表: 首先,更新您的包管理器的包列表。这确保您可以安装最新版本的 Git。在基于 Debian 的系统(如 Ubuntu)上,使用以下命令:sudo apt-get update如果您使用的是基于 RPM 的系统(如 CentOS&…

OpenHarmony应用开发环境搭建指南

OpenHarmony的应用开发主要是基于Deveco Studio(目前只支持Windows及Mac平台)搭配相应的SDK进行,现对开发环境的搭建进行说明。 1:Deveco下载安装 下载对应平台的安装包即可。接下来以Windows平台为例,进行开发环境的搭建。 下载…

楼宇对讲、可视门铃案例分析

语音通话芯片:D34018,D34118,D5020,D31101; D34018 单片电话机通话电路,合并了必 需的放大器、衰减器和几种控制 功能,包括发送和接收衰减器、 背景噪声电平检测系统和一个衰 减器控制系统,对发送和接收电 平好于背景噪声做出反…

PyCharm添加自动函数文档注释

目录 1、背景2、开启PyCharm自动函数文档注释 1、背景 规范的函数文档注释有助于他人理解代码,便于团队协作、提高效率。但如果我们自己手写函数文档注释将非常耗时耗力。PyCharm安装后默认没有开启自动化函数文档注释,需要我们开启 2、开启PyCharm自动…

反序列化 [SWPUCTF 2021 新生赛]ez_unserialize

打开题目 查看源代码 得到提示&#xff0c;那我们用御剑扫描一下看看 我们知道有个robots.txt&#xff0c;访问一下得到 那我们便访问一下 cl45s.php看看 得到网站源代码 <?phperror_reporting(0); show_source("cl45s.php");class wllm{public $admin;public …

55.0/CSS 的应用(详细版)

目录 55.1.1 设计边框样式 55.1.2 调整边框的粗细 55.1.3 边框颜色 55.1.4 复合设置边框 55.2 模块的边距 55.3 模块的内边距 55.4 层的应用 55.4.1 层的建立 55.4.2 浮动——float 55.4.3 清除浮动 55.4.4 层的定位 55.4.5 设置层的溢出——overflow 55.4.6 设置鼠…

Linux开发工具——vim篇

vim开发工具的使用 文章目录 vim开发工具的使用认识vimvim常用三种模式vim正常模式命令集模式切换移动光标删除文字赋值替换撤销上一次操作更改跳到指定的行 vim末行模式命令集列出行号跳到文件中的某一行&#xff1a;保存文件离开vim查找字符&#xff1a; 总结题外话&#xff…

spring使用@Autowired @Lazy 注解 解决循环依赖

今天在启动项目时报错&#xff1a;org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘colorController’: Unsatisfied dependency expressed through field ‘projectService’; nested exception is org.springframework.…

Solidity-2-合约结构

在 Solidity 语言中&#xff0c;合约类似于其他面向对象编程语言中的类。 每个合约中可以包含 状态变量、 函数、 函数 , 事件 Event, 错误(Errors), 结构体 和 枚举类型 的声明&#xff0c;且合约可以从其他合约继承。 还有一些特殊的合约&#xff0c;如&#xff1a; 库 和 …

使用Dropzone提升开发效率的全方位指南

开发过程中&#xff0c;文件上传、图像处理、脚本执行等任务常常是不可或缺的环节。为了提升开发效率&#xff0c;开发者需要一个强大且灵活的工具。在这方面&#xff0c;Dropzone 是一个非常优秀的选择。本文将介绍如何在各种使用场景下&#xff0c;利用 Dropzone 工具来提高开…

C++复合数据类型:字符数组|读取键盘输入|简单读写文件

文章目录 字符数组&#xff08;C风格字符串&#xff09;读取键盘输入使用输入操作符读取单词读取一行信息getline使用get读取一个字符 读写文件 字符数组&#xff08;C风格字符串&#xff09; 字符串就是一串字符的集合&#xff0c;本质上其实是一个“字符的数组”。 在C中为了…

【数据结构和算法】子数组最大平均数 I

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 滑动窗口含义 2.2 滑动窗口一般解法 2.3 方法一&#xff1a;滑动窗口 三、代码 3.1 方法一&#…

异步编程Promise

文章目录 前言一、关于 Promise 的理解与使用1.相关知识补充区别实例对象和函数对象同步回调异步回调Js中的错误&#xff08;error&#xff09;和错误处理 2.promise是什么 二、Promise 原理三、Promise 封装 Ajax四、async 与 await总结 前言 在项目中&#xff0c;promise的使…

Easyrecovery2024手机版文件数据恢复软件

Easyrecovery是著名数据恢复厂商Kroll Ontrack推出文件的恢复软件&#xff0c;因病毒、误删、U盘故障等问题导致的word/excel/ppt/照片等文件丢失的情况&#xff0c;Easyrecovery能做到较高的恢复成功率&#xff01;Easyrecovery是全球著名数据恢复厂商Kroll Ontrack推出的文件…

.Net Attribute 什么是特性、预定义特性使用(一)

什么是特性&#xff1f; 特性&#xff08;Attribute&#xff09;是用于在运行时传递程序中各种元素&#xff08;比如类、方法、结构、枚举、组件等&#xff09;的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面…