Programas C/C++ utilizando arrays

Autor: rubens
Data: 28/04/2009

Typemaps do numpy

O numpy é fornecido com um conjunto de

System Message:

INVALID LINK: `main:numpyobj!numpy_swig.pdf typemaps do SWIG`
para facilitar a utilização de seus arrays em programas escritos em C. Estes typemaps tornam os dados de arrays disponíveis na forma de tipos de dados C (normalmente, inteiros para especificar as dimensões do array e apontadores do tipo adequado para a área de dados do array). Na sequência veremos algumas formas de criar uma função summ que recebe dois arrays e devolve sua soma. Queremos utilizar esta função da seguinte forma:

array1 = array([[...]], int32)
array2 = array([[...]], int32)
array3 = summ(array1, array2)

Criando o array resultante em Python

Na primeira versão de nossa função, tirando proveito do conhecimento das dimensões do array resultante, criaremos o array em Python e o passaremos à função C para que ela o preeencha.

  1. Definiremos então, para fins de criação do wrapper, nossa função com o seguinte protótipo:

    int sum2_(int DIM1, int DIM2, int *IN_ARRAY2,
              int DIM1, int DIM2, int *IN_ARRAY2,
              int DIM1, int DIM2, int *INPLACE_ARRAY2);
    
  2. Este protótipo vai corresponder a uma função Python com a seguinte assinatura:

    sum2_(array1, array2, array3) --> int
    

    array3 corresponde a um array criado em Python e que conterá o resultado da soma. Os seguintes typemaps correspondem aos argumentos:

    (int DIM1, int DIM2, int *IN_ARRAY2) --> array1
    (int DIM1, int DIM2, int *IN_ARRAY2) --> array2
    (int DIM1, int DIM2, int *INPLACE_ARRAY2) --> array3
    
  3. A seguinte função Python compatibiliza a função com a requerida:

    def sum2(a, b):
        c = zeros_like(a)
        st = summ1_(a, b, c)
        return c
    

Criando o array resultante em C/C++

Muitas vezes as dimensões do array resultante não são conhecidas a priori. Nestes casos o array (ou, pelo menos, seus dados) deve ser criado pela função C/C++. Embora este não seja o caso de nossa função, faremos uma implementação onde os dados do array-soma são alocado no programa C/C++.

  1. Definiremos então, para fins de criação do wrapper, nossa função com o seguinte protótipo:

    int summ2_(int DIM1, int DIM2, int *IN_ARRAY2,
               int DIM1, int DIM2, int *IN_ARRAY2,
               int **ARGOUT_ARRAY2, int *DIM1, int *DIM2);
    
  2. Este protótipo vai corresponder a uma função Python com a seguinte assinatura:

    summ2_(array1, array2) --> int, array3
    

    array3 corresponde a um array criado em C para conter resultado da soma. Note que são passados à função C apontadores para as dimensões e para a área de dados. A função C deve alocar a área de dados e preencher as dimensões, de modo que o array possa ser criado pelo wrapper.

    Os seguintes typemaps correspondem aos argumentos:

    (int DIM1, int DIM2, int *IN_ARRAY2) --> array1
    (int DIM1, int DIM2, int *IN_ARRAY2) --> array2
    (int **ARGOUT_ARRAY2, int *DIM1, int *DIM2) --> return value
    
  3. Note que, como a função retorna um inteiro, o wrapper vai retornar uma tupla com o inteiro e o array. Para retornar apenas o array, defina a função como void.

  4. A seguinte função Python compatibiliza a função com a requerida:

    def summ2(a, b):
        st, c = summ1_(a, b)
        return c
    

Código completo

Confira na sequência o código completo da demonstração.

01. //
02. //    Python image allocation
03. //
04. int sum2_(int h1, int w1, int *in1,
05.           int h2, int w2, int *in2,
06.           int h3, int w3, int *out)
07. 
08. {
09.     int *inp1 = in1;
10.     int *inp2 = in2;
11.     int *outp = out;
12.     for(int j = 0; j < h1; j++)
13.         for(int k = 0; k < w1; k++)
14.             *outp++ = (*inp1++) + (*inp2++);
15.     return 0;
16. }
17. //
18. //    C/C++ image allocation
19. //
20. int summ1_(int w1, int *in1,
21.            int w2, int *in2,
22.            int **out, int *ww)
23. {
24.     int *inp1 = in1;
25.     int *inp2 = in2;
26.     int *outp = new int[w1];
27.     *ww = w1;
28.     *out = outp;
29.     for(int k = 0; k < w1; k++)
30.         *outp++ = (*inp1++) + (*inp2++);
31.     return 0;
32. }
33. int summ2_(int h1, int w1, int *in1,
34.            int h2, int w2, int *in2,
35.            int **out, int *hh, int *ww)
36. {
37.     int *inp1 = in1;
38.     int *inp2 = in2;
39.     int *outp = new int[h1*w1];
40.     *hh = h1;
41.     *ww = w1;
42.     *out = outp;
43.     for(int j = 0; j < h1; j++)
44.         for(int k = 0; k < w1; k++)
45.             *outp++ = (*inp1++) + (*inp2++);
46.     return 0;
47. }
48. int summ3_(int d1, int h1, int w1, int *in1,
49.            int d2, int h2, int w2, int *in2,
50.            int **out, int *dd, int *hh, int *ww)
51. {
52.     int *inp1 = in1;
53.     int *inp2 = in2;
54.     int *outp = new int[d1*h1*w1];
55.     *dd = d1;
56.     *hh = h1;
57.     *ww = w1;
58.     *out = outp;
59.     for(int i = 0; i < d1; i++)
60.         for(int j = 0; j < h1; j++)
61.             for(int k = 0; k < w1; k++)
62.                 *outp++ = (*inp1++) + (*inp2++);
63.     return 0;
64. }
OK [C/C++ extension is up-to-date]
01. from numpyobj import sum2, summ2, summ
02. 
03. arr = array([[1,2,3],[1,2,3]], int32)
04. print arr
05. print
06. arr1 = sum2(arr, arr)
07. print arr1
08. print
09. arr2 = sum2(arr1, arr)
10. print arr2
11. print
12. 
13. # arr = array([[[1,2,3],[1,2,3]]], int32)
14. 
15. arr3 = summ(arr1, arr2)
16. print arr3
[[1 2 3]
 [1 2 3]]

[[2 4 6]
 [2 4 6]]

[[3 6 9]
 [3 6 9]]

[[ 5 10 15]
 [ 5 10 15]]