hashlink

2016年12月22日 06:55GMT+8

简介

hashlink 是一个以 c 语言作为底层, 而上层则是 haxe 语言的系统应用平台,你可以把它简单看成是类似于 Java 或 c# 东西。

不同的是 hashlink 不仅可以编译成”字节码”通过其虚拟机来运行, 还可以编译为 c 源码然后再用 c 语言编译器来编译。

编译成”字节码”可以用于快速开发:

# 编译
haxe -hl output.hl -main MyApp

# 使用 hashllink虚拟机运行字节码
hl output.hl

最新:

@:packed : 用于某个字段, 字段必须是 @:struct 标记过的, 表示字段将以 struct 的形式加入, 而不是指针

hl.CArray : 一片连续的大块内存 (TODO: 那么和普通的定长数组的区别是什么了?)

只有 DX12Driver 中用了这个类,来处理了 @:struct VertexBufferView 之类的数据

CArray is a compact array where all objects are memory aligned and stored as a single GC block

这个注释说明 CArray 是单独的 GC 块, 而普通数组则是在普通的 GC 块上划分内存

而在发布(release)的时候可以编译成 c 代码以获得更好的性能(我倒是没感觉到有任何性能的提升…)

# 生成 c 代码
haxe -hl output.c -main MyApp

# 使用 gcc 编译
gcc -o myapp output.c hlc_main.c -Lhl

使用 vs2017 1) 从已存在文件新建项目,选择 console 程序。2) 将 hl/src, 和编译出的 c 文件的目录添加到 include.

3) 设置好 libhl.lib, 例: #pragma comment(lib, "libhl.lib"), 4) 将编译出的主 out.c 添加到”源码”

hashlink 一些其它的特点这里简单描述下:

  • GC 采用的是 mark-and-not-sweep(标记但不清除) 的方式

  • 在实用性方面,目前仅被作为 haxe 的 heaps 游戏框架的底层

编译虚拟机

以下使用了 vs2017 社区版, 需要自已下载 sdl, openal 等放至到 include 目录 (参看各 readme 文件),

  • directx 丢失了 d3dcompiler_47.dll, 通过链接更新 KB4019990 补丁即可.

  • sdl download the Development Libraries

  • openal Download the Win32 binaries

    需要 Openal32.dll(可能需要另外单独下载) 以及 soft_oal.dll(openal-soft-1.17.2-bin.zip内有提供)

  • libuv 中文手册

  • 打开 hl.sln,在 “解决方案配置” 的下拉选项中选择 “release2013”(有”release2013/release/debug”), 最后获取 release2013 目录下的 hl.exe 和所有 .hdll 和 libhl.dll

如果不想自已编译可在 github 上的 release 页面中找到。

Tips

  • 如何在 C 中嵌入虚拟机可以参考 src/main.c 文件

    例如 @:hlNative("fmt","png_decode"), 表示位于 fmt.hdll 库中的 png_decode 方法, 需要自已参考 libs/fmt/fmt.c 的统一格式

    在 hl 包下的 haxe 代码, 有时候你可以看到 $new 这种以美元字符开头的代码, 可参考 genhl.ml

  • libs 目录下有一些 haxelib, 例如: hxsdl, hldx 你需要将它们添加到 haxelib 以使用 heaps 框架

  • 对于 hl.UI, 实际上只有 dialog 还算比较实用, WinLog 只是用来测试的(参看 sdl 中的某一函数), 因为它并没有和 MSG 相关东西, 所以应该使用 sdl 来创建窗口, 因为 hl.UI 仅适用于 windows 平台(未来会实现多平台)

  • 字符串底层使用 ucs2 编码(JS, Java, c# 也一样)

    注意: 字符串是 ucs2 的,但通过 haxe.io.Bytes.ofString 转换 Bytes 时, 将以 utf8 的形式写入

  • hl.Bytes: 没有检察越界,请小心使用。它与 c 语言的 void* 一样.

   

misc

类型

基础类型

  • void: 空, 因此没有对应的真实值
  • u8: unsigned 8, (0-255)
  • ui16: unsigned 16(0-65535)
  • i32: signed 32(-2147483648-2147483647)
  • bool: (true or false)
  • f32: 32位单精度浮点数
  • f64: 64位双精度浮点数

接下来的将表现为指针的形式, (由 gc 管理内存申请的各种数据结构)

  • bytes: 连续字节(类似于 c 语言的 char*), 可自由读写(没有附加边界检测)。

    Tips: 如果 c 端的参数是 hl.bytes 类型, 可直接传 haxe.io.bytes, 编译器会自已转换

  • dyn: 在运行时可以将任何值传递给 dynamic 类型(装箱操作),它的运行时真实类型可通过 gettype 获得.

  • ret fun(args): 在 hl 中为严格的函数类型, 可以是 closure 或一个函数。

  • array: 数组, 非严格类型, 无边界检测, 有固定长度

  • object: 固定了结构, 相当于 haxe 中的 class

  • dynobj: 动态结构,性能不如 object, 但可以添加或移除某一字段。 对应 haxe 中的匿名结构

    底层由一个已排好序(通过计算字段名的 hash 值)的数组所实现, 因此搜索某一字段的时间复杂度为 O(log n)

  • virtual(fields...): 一种灵活的结构, 建议看原文档

  • enum(name): 一个 enum 值, 可以有不同的构造器以及可选的参数

  • ref(T): 引用类型, 它应该是某个函数的参数类型, 这个类型能自动创建, 有些 c 端的函数的参数类型是指针类型, 因此也可以用它

     static function main() {
         var i = 0;
         inc(i);   // 参数必须是变量, 由于变量可以随意加 Meta, 因此可写成 inc(@OUT i) 以让代码更可读
         trace(i); // 1
     }
     static function inc(i: hl.Ref<Int>) { i.set(i.get() + 1); }
    
  • null(T): 一个可以为 null 的基础类型(T的类型必须是基础类型)

  • type:

  • abstract(name): 抽象类型, 由 c 端定义才可以在 haxe 这边访问

    /* src/std/socket.c */
    #define _SOCK	_ABSTRACT(hl_socket)
    

个人补充:

  • vclosure*: haxe function 在 c 端的表现形式

 

haxe 与 HL 类型:

haxe c
Void void
Int i32
Bool bool
String string
Float f64
Single f32
Dynamic dyn
char* hl.bytes

haxe 的 anonymous structure or interface instances 以 c 端表现为 virtual(...)

 

Boxing

当基础类型以及(bytes, type, ref, abstract, enum) 转换到 dyn(通过操作码 todyn) 类型时将会有 Boxing(装箱操作).

C API Documentation

需要写”胶水代码”将现有的 c 库 API 提供给 haxe 来使用. 文档原文

首先在 c 语言端的胶水代码中插入一行 #include <hl.h>, 即可获得各种可用的 API, 以及一些用来写胶水代码非常方便实用的宏。你可以查看一些示例 标准库扩展库

在写c端的胶水代码时,需要注意的是内存的分配, 即如何从 GC 中申请内存

  • hl_gc_alloc_noptr: 用于基础类型, 表示被分配的块不会包含有某些相关联的 “指针”, 例如: 字符串, 固定数组 或其它二进制的数据。

  • hl_gc_alloc_raw: 分配的块可能会包含有 hashlink 指针。

  • hl_gc_alloc_finalizer: 类似于 noptr ,但是结构的第一个字段必须指向 (void(*)(void *) 以便 gc 在清除时调用, 参考 file.c 的 hl_file_open

  • hl_gc_alloc(type, size): 分配指定的 haxe 数据类型, 由于 haxe 类型通常在 haxe 端分配,因此大多数情况下你不会使用这个。 ```

之后 haxe 端的胶水代码则就容易多了. 参看几个示例即可…