牟骞

C++ → AS3 代码移植10坑


在工作中,我们虽然有Flascc可以将C++代码编译为AS3,但是实际在工作中,该方法有诸多不便,比如调试非常麻烦,基于GDB的命令行调试对于不熟悉的人来说简直是噩梦,扩展开发、编译配置等也存在非常高的门槛,不利于整体一致性开发和部署,这样就存在需要将C++的算法进行AS3的移植,这里记录了一些坑和注意事项,和大家进行分享。

1.对应基础类型

image

2.内存操作

image

3.数组操作

1
2
3
4
5
6
unsigned char* pa = &cont.verts[(cont.nverts-2)*4];
unsigned char* pb = &cont.verts[(cont.nverts-1)*4];
if ((int)pb[3] == r)
{
。。。。。。
}

正确的翻译为

1
2
3
4
5
6
Var pa : int = (cont.nverts-2)*4;
Var pb : int = (cont.nverts-1)*4;
if (int(cont.verts[pb+3] == r))
{
。。。。。。
}

4.类型转换

1
pb[1] = (unsigned char)y  => pb[1] = uint(y)

5.函数回值

函数的返回值对于基础类型,在调用函数中可能被改变,例如

1
2
3
4
5
6
7
8
9
10
11
static void projectPoly(const float* axis, const float* poly, const int npoly,
float& rmin, float& rmax)
{
rmin = rmax = dtVdot2D(axis, &poly[0]);
for (int i = 1; i < npoly; ++i)
{
const float d = dtVdot2D(axis, &poly[i*3]);
rmin = dtMin(rmin, d);
rmax = dtMax(rmax, d);
}
}

正确的翻译为:

1
2
3
4
5
6
7
8
9
10
11
public static function projectPoly(axis : Vector.<Number>, axisIndex : int, poly : Vector.<Number>, polyIndex : int , npoly : int, rmin : Number, rmax : Number):Object
{
rmin = rmax = dtVdot2D(axis, axisIndex, poly, polyIndex);
for (var i : int = 1; i < npoly; ++i)
{
var d : Number = dtVdot2D(axis, 0, poly, i*3);
rmin = dtMin(rmin, d);
rmax = dtMax(rmax, d);
}
return {"rmin":rmin,"rmax":rmax};
}

6.序列化

C++的序列化可以直接将对象的内存导出和导入,但是AS3不可以,所以只能自己写fromBytes和toBytes的方式,并且自己手工拼流。

1
2
3
4
5
6
7
8
9
dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize);
float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);

其中dtGetThenAdvanceBufferPointer是获取指向该数据的指针,这里翻译需要改造该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public static function dtGetThenAdvanceBufferPointer(bindingMemoryDatas : Vector.<MemoryBinding>, __className : String, __bytes : ByteArray, __index : int, __size : int):Object
{
var __result : Object;
var i : int;
switch(__className)
{
case "float":
{
__result = new Vector.<Number>(__size/4);
for(i = 0; i < __size / 4; i++)
{
__result[i] = 0;
}
break;
}
case "UnsignedChar":
{
__result = new Vector.<uint>(__size);
for(i = 0; i < __size; i++)
{
__result[i] = 0;
}
break;
}
case "dtMeshHeader":
{
__result = new dtMeshHeader();
break;
}
case "dtPoly":
{
__result = new Vector.<dtPoly>(__size/dtPoly.bytesSize);
for(i = 0; i < __size/dtPoly.bytesSize; i++)
{
__result[i] = new dtPoly();
}
break;
}
case "dtPolyDetail":
{
__result = new Vector.<dtPolyDetail>(__size/dtPolyDetail.bytesSize);
for(i = 0; i < __size/dtPolyDetail.bytesSize; i++)
{
__result[i] = new dtPolyDetail();
}
break;
}
case "dtBVNode":
{
__result = new Vector.<dtBVNode>(__size/dtBVNode.bytesSize);
for(i = 0; i < __size/dtBVNode.bytesSize; i++)
{
__result[i] = new dtBVNode();
}
break;
}
case "dtOffMeshConnection":
{
__result = new Vector.<dtOffMeshConnection>(__size/dtOffMeshConnection.bytesSize);
for(i = 0; i < __size/dtOffMeshConnection.bytesSize; i++)
{
__result[i] = new dtOffMeshConnection();
}
break;
}
}
bindingMemoryDatas.push(new MemoryBinding(__className, __bytes, __index, __size, __result));
return __result;
}

PS:原作者表达的含义将这一堆的数据顺序写入内存一个连续区域,这个内存块其实是个配置文件,可以方便导出整个内存块,注意这其中使用了一个很特殊的MemoryBinding的AS3对象,当AS3对象改变的时候,对应序列化的结果也会发生改变,这时候如果后面有需要读取这个内存块的情况下,需要手动再次调用序列化的方式来更新连续内存。

7.模板

AS3中除了Vector均不可支持模板操作,这里需要使用泛型,如果需要确定类型,需要手工进行识别。不过好在大多模板都和数组连用,基本可以使用Vector翻译,但是Map之类就需要自己判断类型了。

8.重载操作符

使用function的方式实现,将操作符改成函数方式,但是需要注意左右项

9.多态

多函数名自己手工改变调用。

10.自定义数组

这次移植过程中遇到一个作者自己封装的IntArray数组,看字面意思是个int数组,改变数据长度的时候不改变数据本身,可以做到快速回收,但是遇到了一个不知道为啥这么写的坑

1
2
3
4
IntArray aaa = [1, 1, 2];
aaa.length = 0;
aaa.length = 3;
function(aaa[0])……

原本使用Vector.来替代

1
2
3
4
var aaa : Vector.<int> = Vector.<int>([1,1,2]);
aaa.length = 0;
aaa.length = 3;
function(aaa[0])……

结果最后aaa[0]的值为0,而原始代码的值为1,这里就需要特别谨慎,之后改用类封装后解决该问题。