SWIG en el mapa de tipos funciona, pero argout no lo hace

Tengo este archivo de foobar.h

 class Foobar { public: void method(int arg[2]) {}; }; 

Después de comstackr la interfaz SWIG para Python, si bash ejecutar este método desde Python, se indica

 TypeError: in method 'Foobar_method', argument 2 of type 'int [2]' 

Ciertamente. Así que escribo este mapa de tipo SWIG:

 %typemap(in) int [2] {} 

y cuando compilo esto, Python ejecuta este método sin quejarse. Así que creo, entiendo cómo escribir un typemap.

Pero, si cambio el mapa de tipo a argout :

 %typemap(argout) int [2] {} 

Ahora, Python vuelve al error anterior.

Simplemente hago esto directamente desde el manual de SWIG, esto debería funcionar sin ese error, al igual que in mapa de tipo.

¿¿¿Qué estoy haciendo mal???

Que pasa

En resumen, no es una proposición con estos tipos de mapas.

La parte clave de la información que se está perdiendo es la forma en que varios tipos de mapas cooperan para envolver una sola función.

argout se inserta en el contenedor generado después de que se haya producido la llamada. Es tu oportunidad de copiar la entrada (ahora modificada) de nuevo a Python de una manera sensata.

Sin embargo, eso no aborda el problema de cómo se crea y se pasa el argumento antes de la llamada.

Puedes ver esto claramente al inspeccionar el código generado por esta interfaz:

 %module test %{ #include "test.h" %} %typemap(in) int[2] { // "In" typemap goes here } %typemap(argout) int[2] { // "argout" goes here } %include "test.h" 

Que, cuando test.h es tu ejemplo produce:

  // ...  arg1 = reinterpret_cast< Foobar * >(argp1); { // "In" typemap goes here } (arg1)->method(arg2); resultobj = SWIG_Py_Void(); { // "argout" goes here } return resultobj; // ...  

En esos mapas de tipo, el objective del mapa de tipo “en” es hacer de arg2 un valor razonable antes de la llamada y el mapa de tipo “discutir” debería hacer algo sensible con los valores después de la llamada (posiblemente cambiando el valor de retorno si lo desea).


¿Qué debería estar en los typemaps?

Por lo general, para una función como esa, es posible que desee que el mapa de tipo de entrada rellene una matriz temporal de algunas entradas de Python.

Para hacerlo, primero deberemos cambiar el mapa de entrada y pedir a SWIG que cree una matriz temporal para nosotros:

Es importante que obtengamos SWIG para hacer esto por nosotros, usando la notación de agregar paréntesis después del tipo en lugar de agregarlo dentro del cuerpo del mapa de tipos para que el scope sea correcto para la variable. (Si no lo hiciéramos, el temporal no sería accesible desde el mapa de tipo “argout” aún y se limpiaría antes de que se hiciera la llamada).

 %typemap(in) int[2] (int temp[2]) { // If we defined the temporary here then it would be out of scope too early. // "In" typemap goes here } 

El código generado por SWIG ahora incluye esa matriz temporal para nosotros, por lo que queremos usar la API de Python C para iterar sobre nuestra entrada. Eso podría parecer algo como:

 %typemap(in) int[2] (int temp[2]) { // "In" typemap goes here: for (Py_ssize_t i = 0; i < PyList_Size($input); ++i) { assert(i < sizeof temp/sizeof *temp); // Do something smarter temp[i] = PyInt_AsLong(PyList_GetItem($input, i)); // Handle errors } $1 = temp; // Use the temporary as our input } 

(Podríamos haber elegido usar el protocolo iterador de Python en su lugar si lo preferimos).

Si comstackmos y ejecutamos la interfaz ahora tenemos suficiente para pasar una entrada, pero nada regresa todavía. Antes de que escribamos el "typout" del mapa de ruta, todavía hay una cosa que notar en el código generado. Nuestra matriz temporal en el código generado en realidad parece int temp2[2] . Eso no es un error, SWIG ha cambiado el nombre de la variable que se derivará de la posición del argumento para permitir que el mismo mapa de tipos se aplique varias veces a una sola llamada de función, una vez por argumento si es necesario.

En mi mapa de tipo "argout" voy a devolver otra lista de Python con los nuevos valores. Sin embargo, esta no es la única opción sensata, hay otras opciones si lo prefiere.

 %typemap(argout) int[2] { // "argout" goes here: PyObject *list = PyList_New(2); for (size_t i = 0; i < 2; ++i) { PyList_SetItem(list, i, PyInt_FromLong(temp$argnum[i])); } $result = list; } 

Los dos puntos a destacar en esto son, en primer lugar, que debemos escribir temp$argnum explícitamente para que coincida con la transformación que SWIG realizó en nuestra matriz temporal y, en segundo lugar, que estamos usando $result como resultado.

Puramente argumentos de salida

A menudo tenemos un argumento que solo se usa para salida, no entrada. Para estos, no tiene sentido forzar al usuario de Python a proporcionar una lista que simplemente será ignorada.

Podemos hacerlo modificando el mapa de tipo "in", usando numinputs=0 para indicar que no se espera ninguna entrada de Python. También deberás encargarte de inicializar el temporizador de forma adecuada. El mapa de tipos ahora se convierte simplemente en:

 %typemap(in,numinputs=0) int[2] (int temp[2]) { // "In" typemap goes here: memset(temp, 0, sizeof temp); $1 = temp; } 

Así que ahora el mapa de tipo "en" no toma ninguna entrada de Python. Puede verse como simplemente preparar la entrada para la llamada nativa.

De manera aparte, puede evitar la mutilación de nombres que aplica SWIG (con el costo de no poder usar el mismo mapa de tipos varias veces en la misma función, o con otro mapa de tipos que tiene un conflicto de nombres) utilizando noblock=1 en el "in" typemap. Aunque no lo recomendaría.

¿Longitud de matriz no fija?

Finalmente, vale la pena señalar que podemos escribir todos estos mapas de tipos para que sean más generics y trabajen para cualquier matriz de tamaño fijo. Para hacer eso, cambiamos 2 a "ANY" en la coincidencia del mapa de tipo y luego usamos $1_dim0 lugar de 2 dentro de los cuerpos del mapa de tipo, por lo que toda la interfaz al final se convierte en:

 %module test %{ #include "test.h" %} %typemap(in,numinputs=0) int[ANY] (int temp[$1_dim0]) { // "In" typemap goes here: memset(temp, 0, sizeof temp); $1 = temp; } %typemap(argout) int[ANY] { // "argout" goes here: PyObject *list = PyList_New($1_dim0); for (size_t i = 0; i < $1_dim0; ++i) { PyList_SetItem(list, i, PyInt_FromLong(temp$argnum[i])); } $result = list; } %include "test.h"