Não é possível empacotar a matriz de estruturas de C ++ para C # no Unity

Estou tentando passar uma matriz destructs de C ++ para um script do Unity em C #. Quando estou usando o código na produção, o tamanho da matriz varia bastante, portanto, preciso efetivamente passar uma matriz de comprimento desconhecido.

Minha idéia inteligente foi armazenar a matriz na pilha e passar uma referência a essa matriz para o Unity. Encontrei posts do StackOverflow sobre como fazer isso. Mas então o Unity reclama que uma referência é nula.

Aqui está parte do meu código C ++:

extern "C" struct S; // Not currently used.

struct S {
  int i;
  float f;
};

extern "C" bool helloWorld(S ** a, int * i);

S * s;

bool helloWorld(S ** a, int * i) {
  s = new S[4];
  for (int j = 0; j < 4; j++) {
    s[j].i = j;           // Fill in placeholder
    s[j].f = (float) j;   // data for now.
  }
  *a = s; // I don't know if this works.
  *i = 4; // Works.
  return true;
}

Eu tentei isso comints em vez destructse funcionou.

Agora, meu código C # é:

[StructLayout(LayoutKind.Sequential),Serializable]
public struct S {
  int i;
  float f;
};

void Start() {
  IntPtr ptrNativeData = IntPtr.Zero;
  int itemsLength = 0;

  bool success = helloWorld(ref ptrNativeData, ref itemsLength);
  if (!success) {
    return;
  }

  S[] SArray = new S[itemsLength];  // Where the final data will be stored.
  IntPtr[] SPointers = new IntPtr[itemsLength];

  Debug.Log("Length: " + itemsLength); // Works!
  Marshal.Copy(ptrNativeData, SPointers, 0, itemsLength); // Seems not to work.

  for (int i = 0; i < itemsLength; i++) {
    Debug.Log("Pointer: " + SPointers[i]); // SPointers[0] prints 0.
    Marshal.PtrToStructure(SPointers[i], SArray[i]); // Crashes here. Boom.
  }
}

[DllImport("TestUnity")]
private static extern bool helloWorld(ref IntPtr ptrResultVerts,
    ref int resultVertLength);

oMarshal.PtrToStructure&nbsp;A instrução diz que o argumento SPointers [i] é nulo. Eu verifiquei com o comando Debug.Log e, de fato, parece nulo: ele é impresso como 0.

Mas tentei algo semelhante com uma variedade deints mais cedo e que funcionou. O que não tenho certeza é: meu problema está no C ++ ou no C #? Não estou transmitindo as informações corretas ou estou processando as informações corretas da maneira errada?

Solução 1

Essa foi a primeira solução que eu encontrei principalmente por conta própria. A segunda solução é melhor.

Totalmente graças a Alex Skalozub, eu descobri. Um nível de indireção do ponteiro é abstraído durante o empacotamento. Então, agora, meu código C ++ contém:

S ** s; // Not a pointer to Ss, but a pointer to pointers to Ss.

bool helloWorld(S *** a, int * i) { // Pass a triple pointer.
  s = new S * [4]; // Create array of pointers.
  for (int j = 0; j < 4; j++) {
    s[j] = new S; // Actually create each object.
    s[j]->i = j;
    s[j]->f = (float) j;
  }
  *a = s;
  *i = 4;
  return true;
}

E meu código C # contém:

for (int i = 0; i < itemsLength; i++) {
  Debug.Log("Pointer: " + SPointers[i]);
  SArray[i] = (S) Marshal.PtrToStructure(SPointers[i], typeof(S));
}

E é isso! Todos os dados são transferidos.

Agora, isso me deixa com um problema de gerenciamento de memória:todo objeto criado no código C ++ precisará ser liberado. Estou ciente disso e vou cuidar disso a seguir.

Solução 2

Veja a resposta verificada. A resposta de Alex é muito melhor, pois faz muito menos uso de indicadores desnecessários. Usei mais ponteiros porque acabei de descobrir como usá-los em C #.