Ho un file sorgente/header C che fa parte di un progetto più grande. Vorrei testarlo come un'unità, indipendentemente dal vero progetto. Mentre sarebbe possibile farlo in C creando un nuovo progetto con un diverso main()
, mi piacerebbe vedere se posso usare Python (3) e le sue strutture (ad esempio il naso) per accelerare la costruzione di test, usare esistenti quadri di riferimento, ecc.Come si può usare CFFI per chiamare una funzione C esistente dato il codice sorgente?
Avevo l'impressione che avrei potuto farlo con CFFI. Ecco un esempio di file C:
// magic.c
// Implementation of magic.
int add(int a, int b)
{
return a;
}
L'intestazione:
// magic.h
// Add two numbers (where a + b is not greater than INT_MAX).
int add(int a, int b);
Ecco uno script che cerca solo di compilarlo così posso chiamare alcune funzioni:
# cffi_test.py
import cffi
INCLUDE_DIRS = ('.',)
SOURCES = ('magic.c',)
ffi = cffi.FFI()
ffi.set_source(
'_magic_tests',
'#include "magic.h"',
include_dirs = INCLUDE_DIRS,
sources = SOURCES,
libraries = [],
)
ffi.compile()
In definitiva ho intenzione di fare in modo che questo sia parte della configurazione prima di una serie di test unitari, ad es. una pura funzione Python test_add()
chiamerà e controllerà il risultato della funzione C add()
tramite l'oggetto ffi
, che è stato creato nell'impostazione di prova.
Lo script sopra sembra funzionare; funziona senza errori, crea un file _magic_tests.c
, un file _magic_tests.cp35-win32.pyd
e una directory Release
. Posso anche import _magic_tests
senza errori.
Ma non riesco a capire come chiamare effettivamente una funzione C tramite CFFI. Non riesco a trovare alcuna documentazione per la funzione set_source()
e sembra piuttosto integrale per l'intero processo. Il overview lo menziona molto, ma lo reference contiene occorrenze zero di esso. I documenti do hanno una sezione su calling functions, ma si riferisce ad alcuni oggetti lib
senza mostrare come è stato creato. Se guardo all'esempio precedente, c'è un oggetto lib
creato da ffi.dlopen()
, ma non vedo come applicarlo a qualcosa che lo stesso CFFI sta producendo.
La mia domanda (. Cioè la mia X problem) è:
- CFFI è uno strumento ragionevole da utilizzare per la chiamata e la sperimentazione C funzioni in un multi-piattaforma (Windows 7-10, Linux, OS X) strada e se lo è, come?
Le domande che derivano dal mio approccio attuale sono (vale a dire la mia Y problems.):
- Dove si trova la documentazione per
set_source()
? Come posso scoprire quali argomenti ci vuole? - Come si producono gli oggetti
lib
che contengono le funzioni che si desidera chiamare? - È questo il modo più semplice per utilizzare CFFI per chiamare una funzione C? Non ho particolarmente bisogno o voglio una libreria condivisa o un pacchetto ridistribuibile da produrre; se deve succedere, va bene, ma non è necessario. Quali altri approcci potrei provare?
La mia messa a punto corrente è:
- SO: Windows 10
- Python: CPython 3.5.1 32 bit
- Pip: 8.1.2
- CFFI: 1.6.0
- compilatore C : tutto viene fornito con Visual C++ Build Tools 2015, collegato da this MSDN post
Sto utilizzando CFFI e pycparser da Christoph Gohlke's repository.
Ottima risposta! La mia unica ulteriore domanda è: sembra che ci sia qualche duplicazione tra ciò che è nel file di intestazione e ciò che è nella chiamata 'ffibuilder.cdef()'; cioè, dichiarerei la funzione due volte, e c'è il rischio che possa uscire dalla sincronizzazione o introdurre un errore. Pensi che ci sia un modo per ridurre questa duplicazione? – detly
Purtroppo, penso che questo sia al momento impossibile, almeno non ho trovato un modo per farlo. IMHO questo è uno dei principali svantaggi di 'cffi'. Si potrebbe provare a generare automaticamente l'intero 'build_magic_tests.py' usando ancora un altro script. Tuttavia, questo script deve aggirare le limitazioni del metodo 'ffibuilder.cdef()'. – Nicolas
Beh, questo è fastidioso, ma non un rompiscatole. Vale comunque la pena di avere accesso all'ecosistema di test delle unità di Python. (Ed è ancora meno lastra rispetto a qualsiasi framework di test dell'unità C). – detly