hxcpp

2014年05月16日 07:16GMT+8

hxcpp-guide 最好的文档

  • issues: 如何给c++编译器传递 defines? 即将 -D 传递给 c++ 编译器而非 haxe 编译器

编译

编译器配置,找到并修改 user/.hxcpp_config.xml, 比如 win7 中这个文件通常在 C:\Users\MYNAME\ 下, 通常只在使用 andorid 或一些指定平台时才要配置这个.

如果这个文件缺失, hxcpp 将会从 toolchain 目录下自动复制 example.hxcpp_config

The easiest way to start would be to just change the "toolchain" files in hxcpp.
These contain lines like:

<flag value="-O2" tag="release" />

You can just change the "O2" and see if you get the desired result.

If you want to take it a bit further, you could do:
 <flag value="${MY_OPTIM_FLAG}" tag="release" />

and then set MY_OPTIM_FLAG via something like "haxe -main .... -D MY_OPTIM_FLAG=-O1"

new Foo

一个简单的示例:

class Main {
	static function main() {
		var foo = new Foo();
	}

}
class Foo{
	public function new() {}
}

添加禁用优化参数 -D no-analyzer 在编译成 cpp 后,我们可以在 include 中找到 Foo.h 头文件类似于:

#include <hxcpp.h>
#define Foo Foo_obj

class Foo_obj : public hx::Object
{
    public:
        typedef hx::Object super;
        typedef Foo_obj OBJ_;
        Foo_obj();
    public:
        void __construct();
        inline void *operator new(size_t inSize, bool inContainer=false,const char *inName="Foo")
            { return hx::Object::operator new(inSize,inContainer,inName); }
        inline void *operator new(size_t inSize, int extra)
            { return hx::Object::operator new(inSize+extra,false,"Foo"); }
        static hx::ObjectPtr< Foo_obj > __new();
        static Dynamic __CreateEmpty();
        static Dynamic __Create(hx::DynamicArray inArgs);
        //~Foo_obj();
        HX_DO_RTTI_ALL;
        static void __register();
        ::String __ToString() const { return HX_HCSTRING("Foo","\xe6","\x7e","\x35","\x00"); }
};

Main.cpp 中相关的代码类似于:

Foo foo = Foo_obj::__new();

// Foo.hx 中 new 的方法体对应为:
void Foo_obj::__construct(){}
  1. 可以看到 haxe 中的 Foo 编译后变成了 Foo_obj, 其次, new 变成了 __new 方法

事实上,只有当你在写 extern class 时, 才需要关心这些名字的改变,或者永远都不在 extern class 中添加 new 字段。

在 extern class 中由于 Foo 会成变 Foo_obj,因此如果是 static 属性方法,应该使用 @:native() 来重写字段名

  1. 继承了 hx::Object, 用于实现”反射(Reflect)”, 不过可使用 @:unreflective 关闭这一项

如果将 @:nativeGen 添加到类或接口上,则代码看上去非常干净,但是这样将无法从 haxe 这边使用 new 关键字,因为 new 是和 GC 一起的

  • Tips : 尽量减少使用 Dynamic 类型的使用,一些泛形应该使用 @:generic 以明确类型构建

hxcpp-debugger

https://github.com/HaxeFoundation/hxcpp-debugger/wiki/Getting-started

直接嵌入 c++ 代码

  • __cpp__ 直接在 haxe 代码中嵌入 cpp 源码. https://github.com/HaxeFoundation/haxe/pull/2875

    // __cpp__ 后边的 字符串不支持 字符串连接变量, 只是直接常量字符串
    untyped __cpp__('printf("hello world!")');
    
    // 使用下边方法传递变量到 __cpp__
    inline private static function setInt(bytes:haxe.io.BytesData, addr:Int, x:Int){
    	untyped __cpp__("*((int *) ({0}->GetBase() + {1})) = {2}",bytes,addr,x);
    }
    
  • __global__ 直接调用cpp的全局函数等等, 参考 cpp.Lib

Metas

重先先介绍几个重要的:

  • @:nativeGen: 将以纯 c++ 的形式构建类, 主要用于你是用 haxe 来写 cpp 代码供外部使用

    在 haxe 使用它需要和 @:structAccess 一起使用,因为在 haxe 中初始化它时将会再栈上分配内存

    由于是在栈上分配,所以不能把它的值传递出去, 由于不能传递, 因此没什么用

  • @:native 和其它平台的差异,特别是写 extern class 时. 在 cpp 中如果用在静态字段上,将不带类名。例:

    extern class Foo {
        @:native("cpp")
        static function hi():Void;
    }
    
    class Main{
        static function main() {
            Foo.hi(); // 这行实际上会直接编译成全局的函数 cpp(), 不带类名
        }
    }
    

In order to improve support for interacting with CPP.

注意: 如果参数是文件路径的话, 则当前文件路径 ./ 不能省略. 比如上层目录要写成 ./../

# TODO
@:abi                   # Function ABI/calling convention

# 嵌入 xml 到 build.xml, 文件格式参看下边 buildXml
@:buildXml              # Specify xml data to be injected into Build.xml

# 附加于cpp文件上. 代码注入于 namespace 的前边
@:cppFileCode           # Inject code into top of cpp file

# 附加于 类 上.. 在 .cpp 文件中头部添加 头文件引用, 注意: 只能添加一个,只有第一个会有效
# 示例: @:cppInclude("stdlib.h") => #include "stdlib.h"
@:cppInclude            # File to be included in generated cpp file

# 注入代码到 .cpp 文件的顶部,namespace 的内部(haxe 中对应的 package 名称)
@:cppNamespaceCode      #

@:decl                  #

# 注入代码到 函数开始处,还不如直接使用 __cpp__ 来注入代码
@:functionCode          # Inject code into top of function - eg, whole implementation.

# 注入代码到 函数结尾处, TODO: 现在似乎不会理会 return 的位置,不如直接使用 __cpp__
@:functionTailCode      # Inject code into end of function

# 注入 cpp 代码到 头文件(.h) Class 的内部 - 用于声明 成员变量或成员方法 例如: @:headerClassCode("int a,b")
@:headerClassCode       # Code to be injected into the generated class, in the header

# 注入 cpp 代码到 头文件(.h) Class 的外部 - 用于 添加 头文件引用或 宏定义 例如: @:headerCode("#include <stdio.h>")
@:headerCode()          # Code to be injected into the generated header file

# 附加在 class 上, 在 头文件(.h) 中 注入 例如: @headerInclude("stdio.h") - 只能添加一个头文件
@:headerInclude(String)	# File to be included in generated header file

@:headerNamespaceCode   # 注入 cpp 代码到 头文件(.h) Class 的外部,命名空间的内部

# @:include("path/file.h") - 用于 extern 类, 指示 这个类应该导入哪个 头文件 和 @:sourceFile 一起使用
@:include               # Generate "#include ..." to .h/.cpp where the class is being imported.

# ??? 用在 class 上, TODO: 但加不加似乎并没什么区别
@:nativeProperty        # Use native properties which will execute even with dynamic usage

# ??? 原生“静态扩展”,即被标记方法的第一个参数的类型它原来就有的方法
@:nativeStaticExtension # Converts static function syntax into member call

# 编译的cpp代码中不包含有类似于 HX_STACK_LINE...这些调试用的代码, 只用于字段或方法(但是好像无法作用于构造函数),
@:noStack               #

@:nonVirtual            # Declares function to be non-virtual in cpp (cpp only)

# @:sourceFile("path/file.cpp") - 用于 external class,路径以当前 hx 文件所在为当前目录,参考 hxcpp/test/native 目录
@:sourceFile            # Source code filename for external class

# 重要: 用于 extern 类,
@:structAccess          # Marks an extern class as using struct access('.') not pointer('->')

# 如果你不需要使用 Reflect 来动态查找类上的成员, 则可以加上这个标记
@:unreflective          # not generate __Field and __SetField bodies by default

@:void                  # Use Cpp native 'void' return type (cpp only)

# 文件依赖, 也就是当依赖的文件发生过改动,将重将编译.
# 附加在 extern 类上, 哪个类调用了这个 extern 类, 就会在 build.xml 中相关 cpp 新增一条 <depend name="file.h" />
@:depend                # Add a dependency in the build.xml file.

混合编程

这个一个尝试使用 extern class 来处理 c++ 库的项目: https://github.com/snowkit/linc_empty

CFFI

新的写法非常简单,称之为 Prime 并且可以除了 cpp 还可以作用于 neko 平台. 先将外部的 C/C++ 库编译成 lib.ndll 用于在 haxe 代码中调用. hxcpp包源码的 test 中有一个 cffi 的示例.

extern class

另一个调用外部 sdl 源码的示例: https://gist.github.com/nadako/c8aec20c2a7751348f91

参看 hxcpp\test\native, 重要的是:

  • 无法以 new 的方式来创建外部 cpp 类, 因为它返回的是一个指针, 只能用 静态方法创建

    @:native("new RGB")
    public static function create(r:Int, g:Int, b:Int):Pointer<RGB>;
    
  • 外部类由于并非从 GC 申请, 因此需要自已管理内存

    如果你像上边返回的是 Pointer 对象, 那么可以调用 Pointer::destroy 与 destroyArray, 分别为 delete ptr 和 delete [] ptr

  • 在 extern class 上加上 @:structAccess,

  • 如果 extern class 源码没有定义命名空间, 那么 这个 hx 类 也不应该有包名

    • (推荐)用 @:native("ClassName") 省略掉包名就行了.

    • 或可以使用 -cp 将 hx 类添加进编译路径

TODO: 感觉 Pointer<T>Star<T> 经常弄混,好像是由于编译器优化导致的就像和 js 的 {}.some 错误一样.

  • 当把一个 c++ 的指针对像传递给 haxe 任意类的 new 方法上时,必须用 Pointer 才能通过编译

变量转换

一些 haxe 变量转换到 cpp 之后, 可以参考 hxcpp/include/hxcpp.h 下的一些头文件查看一些定义:

HAXE    |    CPP
--------+-----------
Int         int
Float       float    #ifdef HXCPP_FLOAT32
            double
Bool        bool

####         namespace cpp
cpp.Int8       signed char
cpp.UInt8      unsigned char
cpp.Char       char
cpp.Int16      signed short
cpp.UInt16     unsigned short
cpp.Int32      signed int
cpp.UInt32     unsigned int

cpp.Float32    float
cpp.Float64    double

只有上边几种类型是不需要转换的, 但上边只有数据基本类型, 其它 Array, String 都需要做一些转换.

  • cpp.NativeArray C 语言中所描述的数组, 即同类型连续排列的内存引用
    • hxarray.(__unsafe_get, __unsafe_set, __get, __set) 带 unsafe 的方法没有做边界检测.
    // Pointer<T>, T 的类型和 数组类型一致
    var pa:cpp.Pointer<Int> = cpp.Pointer.arrayElem([1,2,3], 0 )
    var pa2:cpp.Pointer<Float> = cpp.NativeArray.address([1.0, 2.0, 3.0], 0);
    
  • cpp.NativeString, 转换成 char* 也可以调用 var cstr:ConstPointer<Char> = untyped hxstr.__s

    using cpp.NativeString;
    //......
    var str:String = "haxeString2CString";
    // 编译为 ::cpp::Pointer< ::cpp::Char > cstr = ::cpp::Pointer< ::cpp::Char >(::cpp::Pointer_obj::fromPointer(str.__s));
    var cstr:ConstPointer<Char> = str.c_str();
    str = cstr.fromPointer();
    // .__s 这个成员变量在 neko 平台也适用.
    var cstr2:ConstPointer<Char> = untyped str.__s;
    

buildXml

build.xml 用来配置 hxcpp 如何编译 haxe 源码。查看 hxcpp/tools/hxcpp/BuildTool.hx 的源码,所调用相关的类都在同目录下.

// 不需要理会这段代码
var mDefines:StringMap<String>;        // 通过 "-D" 的定义和 "环境变量" 和 <set /><unset /> 设定的值
var mCurrentIncludeFile:String;        //
var mIncludePath:Array<String>;        //

var mCompiler:Compiler;                // 将 .cpp.c 等源文件编译到 .o 中间文件. <compiler>
var mStripper:Stripper;                // strip工具, 用于去除调试多余信息. <strip>
var mPrelinkers:StringMap<Prelinker>;  // ,预链接器, Target 通过 toolId 指定匹配. <prelinker id="">
var mLinkers:StringMap<Linker>;        // ,链接器, Target 通过 toolId 指定. <linker id="">
var mCopyFiles:Array<CopyFile>;        // 需要复制的文件配置 <copyFile>
var mFileGroups:StringMap<FileGroup>;  // ,文件分组. <files id="">
var mTargets:StringMap<Target>;        // ,目标. <target id="">
var mFileStack:Array<String>;          //
var mMakefile:String;                  //
var m64:Bool;                // -D HXCPP_M64
var m32:Bool;                // -D HXCPP_M32

条件检测(if, unless)可以用于检测 haxe -D 定义的值,以及用 set 元素设置的变量.

如果多个属性同时用于一个 xml元素, 那么只要第一个属性有效,便会忽略后边的,排序为 if, unless, ifExists

<!-- function valid(inEl:Fast,inSection:String):Bool -->

<set name="var1" value="v1" if="D1" />        <!-- 如果 D1 存在, 则设置 var1=v1 -->
<set name="var2" value="v2" if="D1 D2" />     <!-- 如果 D1 与 D2 定义同时存在, ... -->
<set name="var3" value="v3" if="D1 || D2" />  <!-- 如果 D1 或 D2 定义存在, ... -->

<echo value="hello" unless="D1" />            <!-- 只有当 D1 不存在时, 才打印 hello , 支持:或(||),与(" ") -->
<echo value="world" ifExists="path/to/file" /><!-- 检测文件或目录存在 -->

除了 if, 其它元素属性都支持使用 ${} 来调用 set 设置的变量.

<echo value="${var1}" />
<echo value="hello ${var1}" />

${var} 支持下边前缀: [第 1572 行: function substitute(str:String…]

haxelib:        - 返回haxelib库所在路径.  ${haxelib:hxcpp} => `G:\...`

# 示例: <flag value='-DHXCPP_API_LEVEL=${removeQuotes:hxcpp_api_level}' />
removeQuotes:   - 去除变量返回值的双引号

dospath:        - ??? 将变量返回值转换成DOS path.

dir:            - ??? 和 dospath 一样, 只是多了检测

this_dir        - (注: 没有冒号,即值为 ${this_dir} 时)

在 haxe 代码中通过 @:buildXml('') 来嵌入这些 xml。你可以参看 toolchain 目录下的 xml 文件.

当要自定义一些 linker 时用到的参数, 可以嵌入一个相同 id 的 linker 元素,然后加要需要的参数

你可以在 tools/hxcpp/BuildTool 中找到 parseXML 方法

<set name="" value="" />        <!-- 设置变量 name=value, 在其它元素中通过 ${name} 来引用 value 的值 -->
                                <!-- 可用于变量条件检测, 示例 -->
    <set name="someMsg" value="Hello world!" />
    <echo value="${someMsg}" if="someMsg" />

<unset name="" />               <!-- 移除变量 或 haxe -D -->

<!-- hxcpp编译设置, 细节查看 Setup.hx -->
<setup name="androidNdk|blackberry|msvc|mingw|emscripten" />

<echo value="" />               <!-- log -->
<error value="" />              <!-- error, 将中断编译 -->

<setevn name="" value="" />     <!-- 设置环境变量, 通过 Sys.putEnv 阶段性(session)  -->

<path name="" />                <!-- 设置环境路径, 通过 Sys.putEnv("PATH",) 阶段性(session)  -->

<include name="path/to/file.xml" />     <!-- 导入另一个 xml, 覆盖到当前(以当前目录为根) -->
<import name="relative.xml"/>           <!-- 同上(以 source.xml 的目录为根) -->

<!-- copyFile, 当你需要复制 文件 到输出文件夹(target.outputDir)时 -->
  <!-- name: 要复制的文件名,可以是任意文件 -->
  <!-- form: 从哪个目录 -->
  <!-- [toolId]: 可选, 所选择 编译工具的 id 值, 某个 linker 元素的 id 值   -->
  <!-- [allowMissing]: 是否允许缺失, 值为 1|t|true 时为真, 其它为假 -->
<copyFile name="libstdc++-6.dll" from="${MINGW_ROOT}/bin" toolId="exe" allowMissing="true" unless="no_shared_libs" />

<section if="Some"></section>           <!-- 所有条件相同的元素集合, 用于避免给每个元素加上同样的条件检测 -->


<pleaseUpdateHxcppTool version="1" />   <!-- Int类型. 如果 hxcpp.n 小于某个版本,将出错误. 这个值似乎很久没用了 -->

<!-- 文件组,id 为文件组标识符, 用于被其它元素引用, 同 id 的文件组, 后边的将覆盖前边的  -->
<files id="__main__">
  <compilerflag value="-Iinclude" />    <!-- 文件分组编译参数 -->
  <compilervalue name="-D" value="Some" />
  <file name="src/__main__.cpp" />      <!-- 分组内的文件 -->
    <depend name="include/Main.h" />    <!-- 所依剌的头文件,位于 file 元素内部只能有 name 属性  -->
    <depend name="include/other.h" />
  </file>
  <file name = "src/hx/gc/Immix.cpp" tags="haxe,gc" />   <!-- tags: TODO -->

  <depend name="path/file.h" />         <!-- 依赖另一个文件, 注意 depend 属性要么是 name 或 files -->
  <depend files="id" />                 <!-- 依赖另一个文件组内的所有 depend.has.name 元素  -->

  <!-- TODO:  fxc.exe /nologo /T %profile% %file% /Vn %variable% /Fh %target% -->
  <hlsl name="path/tofile.fx" profile="" variable="" target="" />

  <options name="Options.txt"/>         <!-- TODO: 不明确,反正都是 Options.txt -->

  <precompiledheader name="" dir="" />  <!-- 预编译头文件, name: 输出文件名, dir: 需要编译的头文件所在目录  -->
                                        <!-- 输出的 .pch 文件目录由 files.id 值来确定-->

  <include name="path/to/file.xml" />   <!-- 导入另一个 xml, 覆盖到当前 -->
  <section></section>

  <config name=""  />        <!-- TODO -->
  <tag value=""  />          <!-- TODO: 添加 tag 标记到 tags, files 有 tags 属性 -->
  <addTwice if="linux" />    <!-- TODO -->
  <cache value="true" project="" asLibrary="true"    />    <!-- TODO -->
</files>

<!-- 编译, 将源码编译为中间文件时用到 -->
  <!-- id: 名称标识符 -->
  <!-- exe: 编译器命令名, 如 gcc, cl.exe ... -->
<compiler id="android-gcc" exe="g++">
 <exe name="${EXEPREFIX}-g++" if="" />  <!-- 同上,一般写在这里的会有条件检测, 如果重复将覆盖前边设置 -->
 <flag value="" />                      <!-- 编译器参数,用于所有 .c .cpp .m .mm -->
 <flag value="-Dsome=value" />
 <flag value="-I${HXCPP}/include" />

 <cflag value="" />             <!-- 仅用于 .c 的编译参数 -->
 <cppflag value="" />           <!-- 仅用于 .cpp 或 .c++ 的编译参数 -->
 <objcflag value="" />          <!-- 仅用于 .m(object-c) 的编译参数 -->
 <mmflag value="" />            <!-- 仅用于 .mm(object-c++) 的编译参数 -->
 <pchflag value="" />           <!-- 仅用于 .pch(预编译头文件) 的编译参数 -->

 <objdir value="" />            <!-- 输出文件夹目录, 默认为 obj -->
 <outflag value="" />           <!-- 输出 编译标记, 通常为 -o 或 --output -->
 <pch value="" />               <!-- TODO: 未知 - if(pch == "gcc"){...} -->
 <getversion value="some.exe" /><!-- TODO: 一个输出一些字符到 stderr 的工具命令,输出的字符将通过 md5 计算  -->
 <ext value="" />               <!-- 输出 扩展名, 默认为 .o -->
 <include name="" />            <!-- 导入另一个 xml, 覆盖到当前 -->
 <section unless=""></section>
</compiler>


<!-- strip 是一个去除编译生成的调试内容工具 -->
  <!-- id: 名称标识符 -->
  <!-- exe: strip 工具命令 -->
<stripper id="strip" exe="strip" unless="nostrip">
  <exe name="strip"/>           <!-- 执行 strip 命令名称 -->
  <exe name="arm-none-linux-gnueabi-strip" if="webos" />
  <exe name="${EXEPREFIX}-strip" if="EXEPREFIX" />
  <exe name="${HXCPP_STRIP}" if="HXCPP_STRIP" />
  <exe name="mipsel-linux-strip" if="gcw0" />

  <flag value="-u" if="macos"/> <!-- strip 命令参数 -->
  <flag value="-r" if="macos"/>
  <flag value="-x" if="macos"/>
  <flag value="-d" if="linux"/>
</stripper>



<!-- 链接器 -->
   <!-- id: 链接器标识符 -->
   <!-- exe: 链接器命令名称 -->
<linker id="dll" exe="link.exe" if="windows">
  <exe name="" if="" />           <!-- 链接器命令名称 -->
  <flag value="-nologo"/>         <!-- linker 命令行参数 -->

  <lib name="libm.so" if="demo" /><!-- 依赖的链接库 -->
  <lib name="-llog" if="demo"/>
  <lib name="${dll_import_link}" if="dll_import_link" />

  <libdir name="obj/lib" />       <!-- 依赖的链接库的文件夹 -->

  <fromfile value="@" />          <!-- 如果为 @,所有.obj 将以清单的形式写入到 all_objs. 默认为 @ -->
                                  <!-- windows 需要这个 @ 符号 -->

  <ext value=".dll" />            <!-- 输出扩展名, .exe,.dll,"",.so -->
  <outflag value="-out:" />       <!-- 输出参数: windows通常是 -out:,而非windows通常为 -o -->

  <prefix value="" />             <!-- 输出文件名前缀 -->
  <ranlib name="" />              <!-- 命令工具,用于更新静态库的符号索引表,似乎只适用于 gcc 编译器 -->
  <recreate value="1" />          <!-- 是否删除输出重新建立, 留空为 false,否则为 true.  -->
  <expandAr value="" />           <!-- 解开 .a 库文件为多个 .o, 留空为 false,否则为 true.-->
  <section></section>
</linker>

<!-- linker 示例 -->
<linker id="exe" exe="link.exe" unless="winrt">
  <fromfile value="@"/>
  <flag value="-nologo"/>
  <flag value="-machine:${MACHINE}"/>
  <flag value="-debug" if="HXCPP_DEBUG_LINK"/>
  <flag value="-subsystem:windows${SUBSYSTEM_VER}" if="SUBSYSTEMWINDOWS" />
  <flag value="-subsystem:console${SUBSYSTEM_VER}" if="SUBSYSTEMCONSOLE" />
  <flag value="-libpath:lib"/>
  <flag value="user32.lib"/>
  <ext value=".exe"/>
  <outflag value="-out:"/>
</linker>

<!-- 预链接器, 似乎根本没使用过这个. 子元素参考 linker -->
<prelinker id="" exe="">
  <exe name="" />
  <flag value="" />
  <outflag value="" />
  <expandAr value="" />
  <fromfile value="" />
  <section></section>
</prelinker>

<!-- 目标, 所属的一些元素将覆盖 linker 的设定,所以通过 target 元素来修改或增加 linker 的设定 -->
  <!-- id: 标识符, 可以被其它 target 元素当子模块引用, 如果是主 target,请设值为 "default" -->
  <!-- tool: 只有 "linker"" 这一个值,   -->
  <!-- toolid: id值用于链接器选择, 编译顺序将从 prelinker 到 linker  -->
  <!-- output:  输出文件名,不带扩展名.-->
  <!-- overwrite: 重写, 自已看源码定义 -->
<target id="msvccompat" output="" tool="linker" toolid="${STD_MODULE_LINK}" >
  <target id="" />             <!-- 子模块, 指定 id 值就行了 -->
  <lib name="some.lib" />      <!-- 库名, [将覆盖加到 linker] -->
  <flag value="" />            <!-- 编译参数,[将覆盖加到 linker]  -->
  <depend name="" />           <!-- 文件依赖,将检测这些文件是否发生改动来决定是否重新编译 -->
  <dir name="" />              <!-- (好像没用), 当调用 target.clean 时被将删除的目录 -->
  <outdir name="path/to/" />   <!-- 最终输出文件存放目录 -->
  <builddir name="" />         <!-- 设置编译时的当前目录, 目录必须存在. 之后所有相对目录以这个为基准 -->
  <ext value="" />             <!-- 输出扩展名, 如果指定将覆盖 linker 的设定 -->
  <files id="__main__" />      <!-- 选择需要编译的文件分组, 不同 id 表示不同分组 -->
  <section></section>
</target>

<!-- 最后建议参考 hxcpp/toolchain/haxe-target.xml -->

实际上很多 xml 配置 hxcpp 已经写好了,只要简单的写个 <files></files><target></target> 就可以了.

重要 由于 XML 先后顺序的原因,你只能引用或覆盖 finish-setup.xmlhaxe-target.xml 定义的变量和元素


附录

cppia

需要先编译得到 cppia.exe, windows 下直接双击 hxcpp/project/compile-cppia.hxml 就行了

编译 cppia 后,其实得到一个 Host.exe 的文件就可以用于运行 cppia 脚本.

-cpp out.cppia	# 定义为一个文件名称,而不是目录
-D cppia		#
-main Main
-cp src
-cmd haxelib run hxcpp out.cppia

这样可以快速地调试 cpp 代码, 但这种脚本的性能好像不怎么样.

Defines

http://haxe.org/manual/target-cpp-defines.html

一些定义你可以在 hxcpp/toolchain 目录下的 xml 文件中找到. 特别是 “common-defines.xml”

HAXE_OUTPUT_FILE      # 指定输出文件名称, 因为 -cpp bin 指定的是个目录

static_link           # 生成成静态链接库, 例如 windows 下生成 .lib 文件

dll_import            # ???生成动态链接库, 例如 windows 下导出 .dll 文件

no_console            # 没有控制台窗口, 例如如果你使用 SDL 来分创建窗口的话

API

由于没有文档,只能在 google group 中的 haxelang 频道搜索相关文档。

xxx

cpp.Star

这个类型通常用于包装 extern 类或方法的参数或返回值类型。(感觉 hxcpp 目前处理这个十分混乱, 估计连编译器自已都弄不清楚是”值”还是”地址”)

和这个类似的还有 cpp.Reference;

可通过 cpp.RawPointer.addressOf() 获得某一变量的地址, 然后 cast 传递给 cpp.Star, 不过这样好像没什么意义. 因为在 haxe 端你无法获

cpp.Struct

通常 haxe 编译的类默认使用 -> 访问其成员, 如果一些 extern 类或其它, 需要以 . 的形式访问成员时,则可使用 cpp.Struct 将其包装起来:

cpp.vm.Gc

在 haxe 调用 new 关键字(或匿名结构及匿名函数)将会使得 Gc 跟踪这个对象

  • Tips : 因此减少 new 和匿名结构及匿名函数的使用似乎能降低 gc 的遍历以达到优化目的
# 一些文档可以在 cpp 源码中的头文件中找到...
# 强制 GC 回收
run(major: Bool): Void`

enable(inEnable:Bool) : Void

# TODO: 未知,大概和 `HXCPP_GC_MOVING` 有些关系,
compact(): Void

# estimate of how much is needed by program (at last collect)
memUsage(): Int`

# 获得下一个将被清理的对象, (TODO: -debug 下似乎有个bug, 似乎需要先空调用一次这个方法在 gc.run 之前???)
getNextZombie(): Dynamic

# 防止某一对象被 Gc 清除,但调用 Gc.run 之后仍然会被清楚,但是可以从 getNextZombie 获得这个对象
# 但是不会再触发 setFinalizer 所设置的函数了。由为对象被传递到 getNextZombie,
doNotKill(inObj: Dynamic): Void

# 设置回调,当某个对象被回收时将调用,你可以通过 cpp.Function 来创建第二个参数
setFinalizer<T>(inObject:T, inFinalizer:cpp.Callable<T->Void> ): Void

# https://github.com/HaxeFoundation/hxcpp/issues/171
#     如果一个线程没有 GC 调用(例如 new 一个 haxe 类, 或调用 system-block),
# 这时其它的线程将会一直等待其 "check in"
# 为了确保安全, 你需要调用 Gc.enterGCFreeZone, 然后做一些"无GC分配"的操作最后调用
# Gc.exitGCFreeZone, 或者也可以周期性地调用 Gc.safePoint
# http://blog.csdn.net/iter_zc/article/details/41847887
safePoint(): Void
# hxcpp 将这二个方法放置于各种调用 os 方法的二边(见src/hx/libs/std/Sys.cpp).
enterGCFreeZone()/exitGCFreeZone(): Void

# TODO
setMinimumFreeSpace(inBytes: Int): Void
setTargetFreeSpacePercentage(inPercentage: Int) : Void
setMinimumWorkingMemory(inBytes: Int) : Void

trace(sought:Class<Dynamic>,printInstances:Bool=true): Int

cpp.abi.Abi

The “ABI” allows for differences between “stdcall” and “cdecl” which is important when using WINAPI functions - otherwise use the cpp.abi.Abi default.

关于 Abi 的描述: http://my.oschina.net/superpdm/blog/215826

cpp.Function

用于调用源生的 c/c++ 代码, 见示例: test/snippets/MessageBox.hx

cpp.Callable

抽象类,编译器将会把这个类转换成 cpp.Function,可以参考 cpp.Prime 是如何加载 Prime 的.

下边的示例是展示如何直接调用 cpp 中的全局方法的.

@:cppFileCode("void Func(int x) { printf(\"%d\\n\", x); }")
class Test {
   static function takeCallback(f:cpp.Callable<Int->cpp.Void>) f.call(10);
   public static function main() {
      untyped __cpp__("takeCallback(Func)");
      // 实际上改成 takeCallback(untyped Func); 也行
   }
}

CFFI 以 Prime 的形式从 NDLL 中加载方法的原型类似于:

static var add:cpp.Callable<Int -> Int -> Int> = cast cpp.Prime._loadPrime("NDLL_NAME", "addInts", "iii", false);