1

I have to wrap the following c++ function:

class Foo {
  unsigned int *getVector3();
};

The member function getVector3, returns a (fixed) 3D array, eg [1,2,3]. How should I use arrays_csharp.i for a return type ? The documentation only describes for input parameters:

In my case the return type is always a fixed size array (of 3 elements).

malat
  • 12,152
  • 13
  • 89
  • 158

1 Answers1

1

I have an answer, although it's not entirely satisfactory in my view. That's mostly limited by my knowledge of C# though, so you can probably make it work better than I can.

I don't think arrays_csharp is what you're looking for here. It seems to be about pinning memory so it can be used as input to a function, but in your scenario you've got memory already allocated that you'd like to work with.

That's fairly easy (and for a 3D vector pretty cheap), using System.InteropServices.Marshal normally. So I put together a few typemaps using that which does what you want:

%module test
%typemap(csout,excode=SWIGEXCODE) unsigned *getVector {
    global::System.IntPtr cPtr = $imcall;$excode
    int[] tmp = new int[3];
    // I have no idea why Marshal.Copy does not seem to have any support for unsigned types...
    global::System.Runtime.InteropServices.Marshal.Copy(cPtr, tmp, 0, 3);
    // There is probably a better way to go from int[3] -> uint[3], but it is not obvious to me
    return new $typemap(cstype, $*1_type)[3]{($typemap(cstype, $*1_type))tmp[0],($typemap(cstype, $*1_type))tmp[1],($typemap(cstype, $*1_type))tmp[2]};
}

%typemap(cstype) unsigned *getVector "$typemap(cstype, $*1_type)[]"

%inline %{
unsigned *getVector() {
  static unsigned arr[3] = {1,2,3};
  return arr;
}
%}

A few notes though:

  • $typemap(cstype, $*1_type) is a fancy way of saying find me the C# type corresponding to my C element type. I tend to try and avoid explicitly writing types in typemaps as it makes things more generic.
  • Having said that Marshal.Copy only seems to work with signed rather than unsigned array types, for reasons I can't quite figure out. And I can't see an automatic way to find the corresponding signed type for an unsigned one, so I did have to explicitly write int[]
  • I'm not sure that signed -> unsigned casting is actually well defined behaviour in C#. Maybe this won't work properly for values where the last bit is set. You can work around that by increasing the size of the int type for tmp. (E.g. use int64 instead of int32, but that's not pretty)
  • There ought to be a better way to cast a whole array than what I've done, but I don't know the C# language all that well.

That said this is sufficient that I can run the following program (with Mono) and get the output expected

public class runme {
  static void Main(string[] args) {
    uint[] arr = test.getVector();
    System.Console.WriteLine(arr[0]);
    System.Console.WriteLine(arr[1]);
    System.Console.WriteLine(arr[2]);
  }
}

We could do a bunch more work to make this generic (i.e. other size vectors, other datatypes int16[4], etc.) if that was useful.

Flexo
  • 87,323
  • 22
  • 191
  • 272