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.

 1 #include <sys/sysinfo.h>
 2 
 3 int sys_info(int *load1, int *load5, int *load15, int *nprocs, unsigned long *total, unsigned long *free)
 4 {
 5     struct sysinfo info;
 6 
 7     sysinfo(&info);
 8     *load1  = (int)info.loads[0];
 9     *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

--------------------------------------------------------------------------------
    Thu Oct  3 14:32:29 2013
--------------------------------------------------------------------------------
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Found SWIG: /usr/bin/swig2.0 (found version "2.0.4")
-- Configuring done
-- Generating done
-- Build files have been written to: <pkg_dir>/_build/_code_cpp
make: Warning: File `Makefile' has modification time 7.4 s in the future
make[1]: Warning: File `CMakeFiles/Makefile2' has modification time 7.4 s in the future
make[2]: Warning: File `CMakeFiles/__code_cpp.dir/flags.make' has modification time 7.4 s in the future
[ 25%] Swig source
Scanning dependencies of target __code_cpp
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
make[2]: Warning: File `CMakeFiles/__code_cpp.dir/flags.make' has modification time 6.4 s in the future
[ 50%] Building CXX object CMakeFiles/__code_cpp.dir/_code_cppPYTHON_wrap.o
[ 75%] Building CXX object CMakeFiles/__code_cpp.dir/_code_cpp.o
[100%] Building CXX object CMakeFiles/__code_cpp.dir<pkg_dir>/_build/simple_arrays.o
Linking CXX shared module __code_cpp.so
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
[100%] Built target __code_cpp
make[1]: warning:  Clock skew detected.  Your build may be incomplete.
make: warning:  Clock skew detected.  Your build may be incomplete.

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.

 1 def sysinfo():
 2     unit, load1, load5, load15, nprocs, total, free = sys_info()
 3     return {
 4         'load01':    load1,
 5         'load05':    load5,
 6         'load15':    load15,
 7         'nprocs':    nprocs,
 8         'total_ram': float(total) / (1024*1024),
 9         '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': 1350.67578125,
 'load01': 9120,
 'load05': 6656,
 'load15': 10848,
 'mem_unit': 1,
 'nprocs': 127,
 'total_ram': 4003.06640625}

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.