Criando Toolboxes com o Adessowiki (2)

Código C/C++

Autor: rubens
Data: 26/03/2009

Visão Geral

A criação de extensões para o Python em linguagem C/C++ no Adessowiki é feita através de duas diretivas:

.. code:: cpp module

.. code:: cpp header

Em uma página podem aparecer diversas diretivas 'module', mas somente uma diretiva 'header'. As diretivas 'module' devem aparecer na página antes que a diretiva 'header'. O código nelas contido é reunido em um arquivo .cpp que é compilado e ligado com as bibliotecas adequadas para a criação do módulo.

A diretiva 'header' dispara o processo de compilação/ligação. O seu conteúdo especifica as funções a serem exportadas para uso no Python através de seus protótipos. Estes protótipos, além de identificar as funções exportadas, especificam os tipos de dados que devem ser convertidos de/para o Python.

Os protótipos são anotados através de nomes especiais das variáveis de entrada, que orientam o processo de geração das interfaces com o Python (wrappers).

Um Exemplo Simples

Para exemplificar, faremos um módulo que permite a utilização da função C sysinfo:

NAME
     sysinfo - returns information on overall system statistics

SYNOPSIS
     #include <sys/sysinfo.h>

     int sysinfo(struct sysinfo *info);

DESCRIPTION
     Since Linux 2.3.23 (i386), 2.3.48 (all architectures) the structure is:

         struct sysinfo {
             long uptime;             /* Seconds since boot */
             unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
             unsigned long totalram;  /* Total usable main memory size */
             unsigned long freeram;   /* Available memory size */
             unsigned long sharedram; /* Amount of shared memory */
             unsigned long bufferram; /* Memory used by buffers */
             unsigned long totalswap; /* Total swap space size */
             unsigned long freeswap;  /* swap space still available */
             unsigned short procs;    /* Number of current processes */
             unsigned long totalhigh; /* Total high memory size */
             unsigned long freehigh;  /* Available high memory size */
             unsigned int mem_unit;   /* Memory unit size in bytes */
             char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding for libc5 */
         };

     and the sizes are given as multiples of mem_unit bytes.


RETURN VALUE
     On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

O módulo C/C++

O código de nosso módulo está definido a seguir.

01. #include <sys/sysinfo.h>
02. 
03. int sys_info(int *load1, int *load5, int *load15, int *nprocs, unsigned long *total, unsigned long *free)
04. {
05.     struct sysinfo info;
06. 
07.     sysinfo(&info);
08.     *load1  = (int)info.loads[0];
09.     *load5  = (int)info.loads[1];
10.     *load15 = (int)info.loads[2];
11.     *nprocs = (int)info.procs;
12.     *total  = info.totalram;
13.     *free   = info.freeram;
14.     return (int)info.mem_unit;
15. }

Na sequência, identificamos a função a ser interfaceada. Os nomes dos argumentos out? indicam que o argumento será usado para retornar um valor ao Python. A função então, do ponto de vista do Python, terá sete valores de retorno (os argumentos marcados como out e o valor de retorno) que constituirão uma fila de inteiros.

A string retornada em stdout é um log do processo de construção do módulo. É útil para depuração de error de compilação.

1. int sys_info(int *out0, int *out1, int *out2, int *out3, unsigned long *out4, unsigned long *out5);
OK [C/C++ extension is up-to-date]

Para facilitar a utilização de nosso módulo C, criamos um módulo em Python que retorna um dicionário com as informações obtidas.

01. def sysinfo():
02.     unit, load1, load5, load15, nprocs, total, free = sys_info()
03.     return {
04.         'load01':    load1,
05.         'load05':    load5,
06.         'load15':    load15,
07.         'nprocs':    nprocs,
08.         'total_ram': float(total) / (1024*1024),
09.         'free_ram':  float(free) / (1024*1024),
10.         'mem_unit':  unit,
11.     }

Utilizando o módulo

Finalmente, utilizamos o módulo recém criado e mostramos as informações obtidas. Note, em especial, a primeira linha, onde importamos o módulo. O nome é derivado do espaço da wiki e da identificação da página. É importante, portanto, que esses nomes sejam compatíveis com os nomes de packages e módulos do Python.

1. from code_cpp import sysinfo
2. import pprint
3. 
4. pprint.pprint(sysinfo())
{'free_ram': 1124.75,
 'load01': 4768,
 'load05': 3872,
 'load15': 3424,
 'mem_unit': 1,
 'nprocs': 138,
 'total_ram': 1995.12109375}

Parâmetros Adicionais

A diretiva cpp header aceita dois parâmetros adicionais:

.. code:: cpp header
    :require_morph: yes
    :require_opencv: no

Estes dois booleanos indicam se as bibliotecas do OpenCV ou da toolbox morph devem ser ligadas ao módulo C/C++.

Typemaps

O ponto mais importante na geração automática de wrappers para o Python é a conversão dos tipos de dados. O Adessowiki tem suporte para utilização dos seguintes tipos de dados (como argumentos ou retorno de funções):

  • Tipos básicos

    O Adessowiki utiliza o SWIG para a geração automática das interfaces.

    • Inteiros do Python:
      • int, short, long, unsigned int, unsigned short, unsigned long, unsigned char, signed char, bool
    • Ponto Flutuante do Python:
      • float, double
    • Strings do Python:
      • char *
  • Imagens

    Para o uso de imagens em programas C/C++ o Adessowiki oferece interface para o morph (SDC Morphology Toolbox for Python) e o OpenCV. As imagens são representadas no Python como arrays do NumPy. Os tipos são os seguintes:

    • Image * (morph)

      Use :require_morph: yes

    • IplImage * (opencv)

      Use :require_opencv: yes

    Veja exemplos de uso do OpenCV e morph.