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内有提供)
-
打开 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 端的胶水代码则就容易多了. 参看几个示例即可…