Programas C/C++ utilizando arrays

Autor: rubens
Data: 28/04/2009

Typemaps do numpy

O numpy é fornecido com um conjunto de 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.

 1 //
 2 //    Python image allocation
 3 //
 4 int sum2_(int h1, int w1, int *in1,
 5           int h2, int w2, int *in2,
 6           int h3, int w3, int *out)
 7 
 8 {
 9     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]
 1 from main.numpyobj import sum2, summ2, summ
 2 
 3 arr = array([[1,2,3],[1,2,3]], int32)
 4 print arr
 5 print
 6 arr1 = sum2(arr, arr)
 7 print arr1
 8 print
 9 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]]