2607d,joka

原文

乔卡

一个D语言nogc实用库.它设计针对C风格管理内存,并作为D标准库的轻量补充.

这里

cpp 复制代码
//数组,打印和串插值.
import joka;
void main() {
    auto numbers = List!int(4, 6, 8);
    scope (exit) numbers.free();
    foreach (i, number; numbers) {
        println(i"[$(i)]: $(number)");
    }
}

为什么是乔卡

1,极简主义:避免太多抽象

2,模块化:最小跨模块依赖

3,速度:极快的编译时间

4,BetterC:-betterC -i完全兼容

5,wasm:与WasmtimeEmscripten配合良好

这里

这里

性能基准测试

以下是在运行Ubuntu,16GB内存的Ryzen32200G(配备16GB内存)上,Jokas``动态数组与其他流行库添加并移除50,000,000整数时的对比.略了.

下面还有一些使用类似工作负载的高级跨语言结果.这些不是直接的基准,仅用来参考:

cpp 复制代码
Testing: ./app_d
real 0.09
user 0.02
sys 0.06
Testing: ./app_rs
real 0.09
user 0.03
sys 0.06
Testing: ./app_zig
real 0.09
user 0.03
sys 0.06
Testing: ./app_odin
real 0.14
user 0.05
sys 0.08

快速入门

这里展示了如何使用DUB安装Joka.创建一个新目录,在以下命令中运行:

这里

这里

这里

cpp 复制代码
dub init -n
dub add joka

就这些.复制粘贴其中一例,确保一切正常.

这里

文献信息

先从示例目录开始,快速了解一下.还可用DDOX来创建包含以下内容的概览:

这里

cpp 复制代码
git clone --depth=1 https://github.com/Kapendev/joka joka_package
cd joka_package
dub run -b ddox

模块

1,joka.configs:配置助手

2,joka.game:游戏开发助手

3,joka.io:输入和输出过程

4,joka.math:数学

5,joka.memory:内存工具和容器

6,joka.microui:带Joka更改的RxisUI

7,joka.ranges:区间工具

8,joka.stdc:C``标准库函数

9,joka.types:常见类型定义美标

10,joka.ui:用户接口

11,joka.warena:wasm圆形

12,joka.wasip1:wasm系统接口预览1

13,joka.wasm4:WASM-4奇幻控制台接口

14,joka.wit:Wasm``接口类型助手

版本

1,JokaCustomMemory:允许声明自定义``分配函数.

2,JokaGcMemory:类似JokaCustomMemory,但默认为使用D垃集器.

3,JokaSmallFootprint:在Joka中为部分静态缓冲使用更少的内存.

4,JokaTypesStubs:移除types.d模块中的string.h依赖,改用内部.在使用wasm非常有用.

5,JokaMemoryStubs:移除memory.d模块中的stdlib.h依赖,改用内部.在使用wasm非常有用.

6,JokaMathStubs:移除math.d模块中的math.h依赖,改用内部.在使用wasm非常有用.

7,JokaMallocOnly:禁止用像jokaMake*此类单一分配函数.

8,WASM4:类似WASI,但针对WASM-4奇幻主机.

类型

这并不是个详尽的列表.所有可用类型见源.

基础

1,IndexedValue:索引+

2,KeyValuePair:键+值

3,GenIndex:生成索引

4,ForeignSlice:非D切片

5,BitSet:静态位集

6,Fault:错误码

7,也许Maybe:可选值+错误码

8,选项Option:可选值(一般偏好"可能")

9,结果Result:成功或错误值(一般更喜欢"也许")

10,联Union:标记联

11,PathSepStyle:路径分隔符

12,StrPair:串对

数组

1,列表List:动态数组

2,BufferList:固定缓冲数组

3,FixedList:固定大小数组

4,SparseList:稀疏数组

5,GenList:生成数组

6,位表BitList:动态位数组

7,栅Grid:二维数组

分配器

1,圆形:固定线性分配器

2,GrowingArena:增长线性分配器

3,内存环境MemoryContext:配置器API

数学

1,Vec2:二维向量

2,Vec3:三维向量

3,Vec4:四维向量

4,RGBA:RGBA``颜色

5,矩形Rect:二维矩形

6,圆Circ:二维圆

7,行Line:二维行

8,圆整Rounding:圆整函数.

9,缓解Easing:缓解功能.

10,TweenMode:补间循环行为

11,Tween:1D补间.

12,Tween2:2D补间

13,平滑切换SmoothToggle:平滑转换态

区间

1,数值区间NumericRange:数字序列

2,ArrayRange:数组视图

3,枚举区间EnumeratedRange:索引序列

4,映射区间MapRange:映射区间

5,FilterRange:过滤区间

6,TakeRange:取区间

7,链区间ChainRange:链区间

8,StrideRange:步区间

9,累加区间AccumulateRange:累加区间

10,ChunksRange:Chunks区间

11,SplitterRange:分割区间

12,ArgTokenRange:命令行参数

别名

cpp 复制代码
Sz: size_t
Pd: ptrdiff_t
Str: char[]
IStr: const(char)[]
Strz: char*
IStrz: const(char)*

游戏

1,翻转Flip:翻转方向

2,磁帖Tile:游戏磁帖

3,TileMap:游戏地图

4,SpriteAnimation:动画数据

5,精灵Sprite:动画精灵

6,GTimer:常见计时器

7,故事Story:对话系统

8,方框Box:小矩形

9,BoxWorld:Arcady``物理引擎

wasm

Joka通过-betterC -i标志支持wasm,并与EmscriptenWasmtime整合.也可通过允许"块"其他环境中使用一些Joka模块.

下面是一个WASI你好示例:

这里

cpp 复制代码
import joka.wasip1;
import joka.types;
extern(C)
void _start() {
    auto value = 40 + 29;
    auto text = "Value is: {}\n".fmt(value).toCiovec();
    fdWrite(stdout, &text, 1, null);
}

编译并运行``Wasmtime:

cpp 复制代码
ldc2 -betterC -i --mtriple=wasm32 --checkaction=halt app.d
wasmtime app.wasm

此外,可用wasm32-wasi目标来默认启用某些版本,如默认JokaMemoryStubs,并取得WASI特定的实现:

cpp 复制代码
import joka.io;
extern(C)
void _start() {
    println("Value is: ", 40 + 29);
}

ldc2 -betterC -i --mtriple=wasm32-wasi app.d
wasmtime app.wasm

脚本文件夹中还提供了基础的WASM-4模板.开始时,在你的项目目录中复制该目录的内容.WASM-4项应遵守以下结构:

cpp 复制代码
my_wasm4_project/
├── app.d
├── joka/
└── run

内存跟踪

Joka包含一个轻量内存跟踪系统,可检测调试构建中的泄漏或无效释放.默认,助手函数``memoryTrackingInfo``输出如下略.

上述泄漏摘要可过滤,只显示包含过滤串的路径的泄漏.如,memoryTrackingInfo("app.d")只显示路径中带"app.d"的泄漏.可用ignoreLeak忽略具体分配,比如这样:

cpp 复制代码
// struct Game { int hp; int mp; }
// Game* game;
game = jokaMake!Game().ignoreLeak();

也可分组分配,这样更好理解在ScopedAllocationGroup中每个分配的用法,如下:

cpp 复制代码
//也可用`"bestartAllocationGroup"`和`"endAllocationGroup"``函数实现`.
with (ScopedAllocationGroup("World")) {
    allocateMonsters();
    allocateActors();
    with (ScopedAllocationGroup("Contents")) {
        allocateItems();
        allocateEvents();
    }
}
allocateText();
//不属于任何团体.

可用静态(isTrackingMemory)内存检测跟踪是否激活,如果激活了,可用_memoryTrackingState检查当前跟踪状态.

_memoryTrackingState是线本的,因此每线程都有自己的独立跟踪状态.

独立的内存.d

完全可不完全依赖Joka,直接使用``分配内存模块.为此,复制memory.dtypes.d到一个项目中.

独立数学.d

也可不完全依赖Joka,直接用数学模块.复制math.dtypes.d到项目里.

常见问题解答

Joka有分配器API

是的.看看memory.d里的MemoryContext.Joka``默认设计成类C标准库,但许多数据结构确实接受可选的分配器.

乔卡有像Jai那样的全局环境吗

是,而且它故意给它取了一个难听的名字(__memoryContext),目的是让人们不敢使用.原因在于,全局环境往往使得低层API脆弱.在乔卡中,鼓励仅在特例下使用.

相比Jai,Jokas版本只是管理内存.以下是一些相关信息:

cpp 复制代码
struct MemoryContext {
    void* allocatorState;
    AllocatorReallocFunc reallocFunc;
    void* malloc(Sz alignment, Sz size, IStr file, Sz line);
    void* realloc(Sz alignment, void* oldPtr, Sz oldSize, Sz newSize, IStr file, Sz line);
    void  free(Sz alignment, void* oldPtr, Sz oldSize, IStr file, Sz line);
}
alias AllocatorReallocFunc = void* function(void* allocatorState, Sz alignment, void* oldPtr, Sz oldSize, Sz newSize, IStr file, Sz line);
struct ScopedMemoryContext {
    MemoryContext _previousMemoryContext;
    this(MemoryContext newContext);
    this(ref Arena arena);
    this(ref GrowingArena arena);
}
ScopedMemoryContext ScopedDefaultMemoryContext();
void jokaRestoreDefaultAllocatorSetup(ref MemoryContext context);
void jokaRestoreNullAllocatorSetup(ref MemoryContext context);
void jokaEnsureCapture(ref MemoryContext capture);
MemoryContext __memoryContext;

有些类型,比如列表,会跟踪他们使用的分配器.拥有分配器的成员一般叫抓成员.建议在使用抓前先调用jokaEnsureCapture.

也可用jokaSystem*函数忽略环境.如,GrowingArena类型使用了jokaSystemMallocjokaSystemFree.

拦截第三方代码

该系统的一个理由是可"拦截第三方代码".别.环境系统是一个需要考虑的全局变量.它类似带笔的颜色PICO-8API,但用来管理内存和区间神奇.

为什么有些函数不用@nogc

因为D垃集器可用JokaGcMemory版本来分配内存.@nogc属性只是给编译器一个提示,告诉它检查,调用函数是否也带该提示.它有帮助,但对写无GC代码来说不必.

如,考虑该函数:

cpp 复制代码
char[] temporaryString() {
    static char[64][32] buffers = void;
    static currentBuffer = 0;
    currentBuffer = (currentBuffer + 1) % buffers.length;
    return buffers[currentBuffer][];
}

该函数使用静态缓冲运行时创建临时串.它从不用GC分配,因此在实际操作中是nogc函数,但不是@nogc函数.

如果你试从@nogc``调用函数它,编译器会因为缺少属性而拒绝它.即D中的属性并不是管理内存工具.

就我个人而言,除非写库,否则我一般避免在自己的项中使用属性.你也可用-vgc标志来跟踪GC的用例.

你为什么支持D型垃集器

它也是一个管理内存工具.Joka一般在调试构建中使用跟踪分配器来帮助识别错误,但JokaGcMemory版本则是为偏爱考虑安全的人设计的.该方法与FilC所带的类似.

Joka和其他D库混在一起难吗?

不.Joka不对代码施加任意限制,因此与d标准库或其他库运行顺畅.有些库选择仅用@安全,@nogc或无抛,但这些是他们的绑定,不是Jokas.

我完全避免用"面向属性"的项目结构方式.

为什么没有jokaFreeSlice函数

因为切片本来就应该像数组而不是指针一样使用.它们在D代码中无处不在,即很容易误解错误的.使用jokaFree(slice.ptr)可避免.

这样可暴露不安全的部分,帮助避免错误.另一个好处更容易推导.Joka只有一个释放内存的函数.

Odin语言有三个释放内存的函数:

cpp 复制代码
free
free_all
delete

如果你是奥丁新手,光凭名字可能很难判断它们的作用.用切片释放内存的方法是删除.使用时如下:

cpp 复制代码
main :: proc() {
    buffer: [256]u8
//在栈上创建一个缓冲.
    slice := buffer[:]
//从缓冲取一个切片
//...
    delete(slice)
//试`释放内存`.
}

总之,Joka简单且安全地处理它.

常见的-betterC错误有哪些

使用-betterC按全局@nogc属性.该标志不仅是移除垃集器,还增加了有时可能太限制的额外检查.如果编写无GC代码很重要,且确实需要编译器的帮助,则在每个文件顶部添加@nogc:

或不带i标志使用-betterC.-betterC -i组合一般是可行的.

还有类型信息错误.在源中搜索"新"并删除它.

该目标位使用了struct[N].使用此类类型时需要D运行时某些部分(_memsetn等),但因为-betterC工作原理,可能会缺少这些部分.

静态数组的方案是实现缺失的函数,或使用自定义``静态数组类型(joka.types中的StaticArray).

一个基本错误是串错误.一般想在编译时用函数创建串,但因为-betterC标志带来的额外检查,这更难.下面是"正常"生成串的函数,随后是一个与该标志相符的替代方法:

cpp 复制代码
//`"betterC"`不行.
//参数可来自`运行时`或`编译时`.
string createString(string value) {
    return value ~ "_OwO";
}
//同`"betterC"`工作.
//必须在`编译时`已知参数.
string createString(string value)() {
    return value ~ "_UwU";
}

Joka的用途

它主要用在游戏引擎``Parin.

为什么要不带GC用D

因为手动管理内存很有趣!

相关推荐
fqbqrr1 个月前
2606d,用d语言构建游戏引擎
游戏引擎·d
fqbqrr2 个月前
2605d,d可以没有GC
d
fqbqrr4 个月前
2603,d去年9月会议
d
fqbqrr9 个月前
2510d,C++虚混杂
c++·d
fqbqrr2 年前
2501d,d作者,炮打C语言!
c语言·d
fqbqrr2 年前
2501d,d的优势之一与C互操作
d
fqbqrr2 年前
2412d,d的6月会议
d
fqbqrr2 年前
2411d,右值与移动
d
fqbqrr2 年前
2407d,D2024三月会议
d