A09 - Misturando Python e linguagens compiladas
Questão
Como podemos lidar com projetos multilinguagens com CMake?
Objetivos
Aprenda a construir ligações Python pybind11.
Aprenda a construir ligações CFFI Python.
Python é uma linguagem de programação dinâmica flexível. Como o próprio Python é escrito na linguagem de programação C, é possível escrever módulos de extensão em uma linguagem compilada. Obtém-se toda a flexibilidade evitando as penalidades de desempenho inerentes às linguagens interpretadas. Muitos frameworks estão disponíveis para preencher a lacuna entre linguagens compiladas e Python. Todos eles contam com alguma forma de geração automática de código:
SWIG. Possivelmente o framework com a história mais longa.
CFFI. Funciona com C e Fortran.
Cython. Funciona com C e pode exigir muito esforço.
Misturando C++ e Python com pybind11
Se você está escrevendo C++, você tem ainda mais opções de frameworks de vinculação:
Boost.Python. Adaptado para C++ e conta com metaprogramação de modelo para gerar ligações em tempo de compilação.
pybind11. Mesma filosofia do Boost.Python, mas projetado para C++11 e além.
Se você escrever modern C++, pybind11 deve ser sua estrutura de escolha:
É uma biblioteca somente de cabeçalho e, portanto, uma dependência bastante fácil de satisfazer.
O código de ligação será bastante compacto: você não terá que manter uma base de código excessivamente grande.
Possui excelente integração com o CMake.
Exercício 27: Código bancário com C++ e Python
Nosso objetivo é compilar wrappers Python para uma pequena biblioteca C++ simulando uma conta bancária. A dependência pybind11 será satisfeita no momento da configuração usando
FetchContent
.O projeto base está em
source/code/day-2/27_cxx-pybind11
. A estrutura da pasta é a seguinte:27_cxx-pybind11 └── account ├── account.cpp ├── account.hpp └── test.py
Crie um
CMakeLists.txt
na raiz do programa, com requisito e projeto mínimo do CMake.Encontre o Python com
find_package
. Solicite pelo menos a versão 3.6 com a palavra-chaveREQUIRED
e o interpretador e os cabeçalhos de desenvolvimento com a palavra-chaveCOMPONENTS
. Consulte a documentação:$ cmake --help-module FindPython | lessHabilite o teste e adicione a pasta
account
.Preencha o
CMakeLists.txt
base na pastaaccount
, seguindo os prompts doFIXME
. Queremos baixar o tarball lançado para a versão 2.6.2 do pybind11.Configure, construa e execute o teste.
Uma solução funcional está na subpasta solution
.
Note
A função
pybind11_add_module
é um wrapper de conveniência paraadd_library
para gerar módulos de extensão Python. É oferecido por pybind11 e você pode ler mais sobre isso aqui.A sintaxe especial usada na definição do comando test definirá o local da extensão Python como uma variável de ambiente.
Misturando C/Fortran e Python com CFFI
CFFI, abreviação de “C Foreign Function Interface”, é um módulo Python que ajuda na criação de interfaces Python para projetos C-interoperáveis. Usar CFFI pode ser um pouco mais simples do que trabalhar com pybind11. No entanto, ele permite que você crie interfaces Python para projetos Fortran de forma mais direta do que com Cython ou SWIG. Isso requer alguns passos:
escrever um arquivo de cabeçalho C definindo a interface de programação de aplicativos (API) do seu código.
invocando CFFI para analisar o arquivo de cabeçalho da API e produzir o código de wrapper C correspondente.
compilando o código wrapper gerado em um módulo Python.
Embora a etapa 1 dependa do código para o qual você deseja fornecer o código de vinculações do Python, as etapas 2 e 3 podem ser automatizadas em um sistema de compilação CMake.
Exercício 28: Código bancário usando CFFI
Nosso objetivo é compilar wrappers Python para uma pequena biblioteca simulando uma conta bancária. O código de exemplo já tem um arquivo de cabeçalho da API. Este exercício mostrará como realizar as etapas 2 e 3 acima:
- O script Python
cffi_builder.py
analisa o arquivo de cabeçalho da API e irá gerar o arquivo fonte
_pyaccount.c
no tempo de compilação. Conseguimos isso no CMake usando um comando personalizado, emparelhado com um destino personalizado.
add_custom_command( OUTPUT ${PROJECT_BINARY_DIR}/generated/_pyaccount.c COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/cffi_builder.py MAIN_DEPENDENCY ${CMAKE_CURRENT_LIST_DIR}/cffi_builder.py DEPENDS ${CMAKE_CURRENT_LIST_DIR}/account.h WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/generated ) add_custom_target( pyaccount-builder ALL DEPENDS ${PROJECT_BINARY_DIR}/generated/_pyaccount.c )
- O script Python
Isso garante que o arquivo seja regenerado sempre que o cabeçalho da API for alterado.
- Uma vez que o
_pyaccount.c
esteja disponível, nós o construímos como um módulo Python, usando a função
Python_add_library
, fornecida no móduloFindPython
do CMake.
Python_add_library(_pyaccount MODULE account.f90 ${PROJECT_BINARY_DIR}/generated/_pyaccount.c ) # add dependency between _pyaccount target and pyaccount-builder custom target add_dependencies(_pyaccount pyaccount-builder)
- Uma vez que o
Um projeto base está em
source/code/day-2/28_fortran-cffi
. A estrutura do código é a seguinte:28_fortran-cffi/ ├── account │ ├── account.f90 │ ├── account.h │ ├── cffi_builder.py │ ├── CMakeLists.txt │ ├── __init__.py │ └── test.py └── CMakeLists.txtSiga os prompts do
FIXME
emCMakeLists.txt
para fazer o projeto compilar.
Declare um projeto usando C e Fortran.
Encontre o Python com
find_package
. Solicite pelo menos a versão 3.6 com a palavra-chaveREQUIRED
e o interpretador e os cabeçalhos de desenvolvimento com a palavra-chaveCOMPONENTS
. Consulte a documentação:$ cmake --help-module FindPython | lessAdicione a pasta
account
e ative o teste.Preencha
CMakeLists.txt
na pastaaccount
, seguindo os prompts doFIXME
.Configure, construa e execute o teste.
Uma solução funcional está na subpasta solution
.
Um projeto base está em content/code/day-2/28_cxx-cffi
.
A estrutura do código é a seguinte:
28_fortran-cffi/
├── account
│ ├── account.cpp
│ ├── account.hpp
│ ├── c_cpp_interface.cpp
│ ├── account.h
│ ├── cffi_builder.py
│ ├── CMakeLists.txt
│ ├── __init__.py
│ └── test.py
└── CMakeLists.txt
Declare o projeto C++.
FEncontre o Python com
find_package
. Solicite pelo menos a versão 3.6 com a palavra-chaveREQUIRED
e o interpretador e os cabeçalhos de desenvolvimento com a palavra-chaveCOMPONENTS
. Consulte a documentação:$ cmake --help-module FindPython | less
Adicione a pasta
account
e ative o teste.Preencha
CMakeLists.txt
na pastaaccount
, seguindo os prompts doFIXME
.Configure, construa e execute o teste.
A estrutura do código é a seguinte:
Resumo
O CMake pode simplificar o sistema de compilação para projetos complexos e multilíngues.