How to compile a native array for c# target

Hello community. I am stuck trying to build a native Array type for c# target. The doc suggests using cs.NativeArray instead of Array I didn’t find any other solution. But I want to keep single code base, so for now my code doesn’t match Haxe concept. If there is another solution? macros for example? I tried adding nativeGen etc - it doesn’t work. NativeArray is also not compatible with Lambda, which is not very convenient.

@:nativeGen
@:strict(SerializableAttribute)
class ProfileData {
#if cs
public var Cards:NativeArray<CardData>;
public var Items:NativeArray<ItemData>;
public var Events:NativeArray<GameRequest>;
....
#elseif js
public var Cards:Array<CardData>;
public var Items:Array<ItemData>;
public var Events:Array<GameRequest>;
...
#end
}

So the problem is the conditional compilation flag and you are trying to avoid that?

1 Like

You can use a typedef:

#if cs
import cs.NativeArray;
#else
typedef NativeArray = Array;
#end

and then you can use the name NativeArray in all targets.

What’s your use case? A native array will not behave exactly the same as a Haxe array.

Correct. Looks like I should cast it into one type.

Great. However, I still can’t use Lambda because NA is not Iterable. I don’t know how to get iterator from NA. But that’s no longer an issue, I just can use ‘for’ instead. My case is shared logic between two platforms, iterating array, adding elements. So it might be better to use List_1 instead of NativeArray

#if cs
import cs.system.collections.generic.List_1;
#else
typedef List_1 = Array;
#end

But Array is available on the c# target, why do you specifically want a native array?
Using the same Array type will allow you to share logic more easily, since they’d have the same behavior.

Most part of the code is already done on the native target, I wouldn’t change it. And I would like to use native data types to avoid any platform (Unity) compatibility problems now and in the future.

Here’s some code we use to iterate on native CS arrays inside Haxe, hopefully they can get you a step further. They’re extension methods so do using CSTools.

#if cs
import cs.system.collections.generic.List_1;

class CSTools {

    public static function iterator<T>(hashSet:cs.system.collections.generic.IEnumerable_1<T>):Iterator<T> {
        return new IEnumerableIterator(hashSet);
    }

    public static function toNative<T>(array:Array<T>):cs.NativeArray<T> {
        var nativeArray = new cs.NativeArray<T>(array.length);
        for(i in 0 ... array.length) nativeArray[i]=array[i];
        return nativeArray;
    }

    public inline static function toNativeList<T>(array:Array<T>):List_1<T> {
        var nativeList = new List_1<T>(array.length);
        for(i in array) nativeList.Add(i);
        return nativeList;
    }
}

class IEnumerableIterator<T> {
    var enumerator:cs.system.collections.generic.IEnumerator_1<T>;
    var hasNextBool = false;
    var nextCalled = false;
    public function new(enumerable:cs.system.collections.generic.IEnumerable_1<T>) {
        enumerator = enumerable.GetEnumerator();
    }

    public function hasNext():Bool {
        if(!nextCalled) {
            hasNextBool = enumerator.MoveNext();
            nextCalled = true;
        }
        return hasNextBool;
    }

    public function next():T {
        hasNext();
        nextCalled = false;
        return enumerator.Current;
    }
}

#end

Usage looks like this:

var intArray = [ 1, 2, 3, 4 ]; // Haxe Array
var csArray = intArray.toNative(); // Converted to C# Array
for(i in csArray) trace(i); // Iterate over C# Array

Thank for iterator. I supplemented your code collection:

#if js
typedef NativeArray<T> = Array<T>;
typedef List_1<T> = Array<T>;
typedef Dictionary_2<T, K> = Map<T, K>;
#end

class CSTools {
	public static function iterator<T>(hashSet:cs.system.collections.generic.IEnumerable_1<T>):Iterator<T> {
		return new IEnumerableIterator(hashSet);
	}

	@:generic public static function push<T>(_this:cs.system.collections.generic.List_1<T>, x:T):Int {
		_this.Add(x);
		return _this.Count;
	}

	public static function pop<T>(_this:cs.system.collections.generic.List_1<T>):T {
		var i:T = _this[0];
		_this.RemoveAt(0);
		return i;
	}

	public static function sort<T>(_this:cs.system.collections.generic.List_1<T>, f:T->T->Int):Void {
		_this.Sort(f);
	}

	@:generic public static function get<T, K>(_this:cs.system.collections.generic.Dictionary_2<K, T>, key:K):T {
		return _this.get_Item(key);
	}

	@:generic public static function set<T, K>(_this:cs.system.collections.generic.Dictionary_2<K, T>, key:K, value:T):Void {
		return _this.set_Item(key, value);
	}

	@:generic public static function remove<T, K>(_this:cs.system.collections.generic.Dictionary_2<K, T>, key:K):Bool {
		return _this.Remove(key);
	}
}

so now we can use these in haxe code like

 var deck:List_1<Int> = profile.Deck;
 var next:Int = request.Value;
 deck.push(next);

var events:Dictionary_2<String, GameRequest> = profile.Events;
events.get(request.Hash);

Using your toNative is bad practice,you’re creating a new copy array instead of make an interface with the current data. so, for example, on a server platform, with a large initial array, this isn’t applicable.

An alternative to a set of extension methods is making an abstract Abstract - Haxe - The Cross-platform Toolkit, that way you don’t have to add an import every time.