Debugging Haxe cross-platform?

Have you found the whole multi-target cross-platform support work as advertised? All VMs / engines / transpilers (Java, Mono, Unity, Adobe Air) - I’ve ever worked on always end up “write once, debug everywhere” instead of what they advertise (“write once, run everywhere”). So I’d suspect you leverage the individual target debugging pretty often no? If so, any big gotchas to look out for?

I agree, that there are risks in cross-platform development, that debugging will become more complicated.

Some features are platform-specific. Using a native extension on a mobile platform is a good example of this.

Setting those obviously platform-specific features aside, the biggest “gotcha” is some subtle differences between AVM, JavaScript, Neko or C++.

There are two big principles that will avoid these problems:

  1. Don’t use integers unless they’ve been initialized. Don’t expect uninitialized Bool values to always be == false

The behavior on dynamic and static platforms are a bit different when values are uninitialized. var number:Int on Flash Player will be 0, on JavaScript, it is undefined. Neko is null.

You can avoid headache if you expect to initialize your value (like number = 0) before you do number++ or number + anotherValue or another kind of math. The platforms are pretty consistent from that point on.

Boolean values are similar, an unitialized Bool while evaluate as false in an if (!boolean) check on all platforms, but an exact check (like if (boolean == false)) may fail on some of these platforms. Better to avoid that pattern or to initialize first if you want to work that way.

String, objects and other types that are default null behave basically the same, except Haxe simplifies things to a basic if (object == null) check, so you don’t need to bother with triple equal signs or various null/undefined types.

  1. Don’t use Dynamic code, or if you do, be aware that it will most likely be where your code breaks

Haxe will turn even untyped code into strictly typed code by default (such as var int = 100; being typed as Int automatically), which keeps things working well, nearly always if the compiler allows compilation.

However, you can use :Dynamic explicitly, or Reflect, and other APIs which allow you to work with loose types. Dynamic targets (like JavaScript or AVM) behave differently than static targets (like C++), so this can be a problem area if you rely on dynamics. Haxe has a bunch of features to do neat things in a strictly typed way, which often feels more elegant and is going to perform faster, anyway.

A few Haxe features will be dynamic under the hood on the C++ target, so there are some things you may choose to avoid for performance reasons, but these features are different than a true Dynamic type because the compiler has a type for it during compilation, which guards against issues that could come up.

It helps to test different target types periodically, to be sure things are still compiling. Particularly, it helps to test the non-Flash targets, because that tests our open-source runtime instead of Adobe’s runtime in Flash Player :slight_smile:

2 Likes

Works like a charm! (flash,html5,android,linux,windows - ios comes next)
Yes, sometimes(seldom) there is platform specific stuff to solve, but thats normal in a developing/growing environment.

Luckily i am old-school(C++) and do initialize by principal.
Same as i use Destructors to clean up, GC can do the stuff i overlook :wink:

Thanks so much Josh for the detailed break down. Seems reasonably straightforward!

Thankfully I only hire C++ game developers (even to do flash work!) so I have the same benefit. :slight_smile:

One of my concerns is how as3hx handles dictionary / array conversion. Based on this nice looking GitBook (is this the official, most up to date one?) - it implies it attempts to figure it out automatically.

Thanks again for all the support guys!