Le librerie software sono un modo di riutilizzo del codice di lunga data, facile e sensato. Questo articolo spiega come creare librerie da zero e renderle disponibili ai client. Sebbene le due librerie di esempio siano destinate a Linux, i passaggi per la creazione, la pubblicazione e l'utilizzo di queste librerie si applicano ad altri sistemi simili a Unix.
Le librerie di campioni sono scritte in C, che si adatta bene al compito. Il kernel Linux è scritto principalmente in C con il resto in linguaggio assembly. (Lo stesso vale per i cugini Windows e Linux come macOS.) Anche le librerie di sistema standard per input/output, networking, elaborazione di stringhe, matematica, sicurezza, codifica dei dati e così via sono scritte principalmente in C. Per scrivere una libreria in C è quindi scrivere nella lingua nativa di Linux. Inoltre, C segna il segno delle prestazioni tra i linguaggi di alto livello.
Più risorse Linux
- Comandi Linux cheat sheet
- Cheat sheet sui comandi avanzati di Linux
- Corso online gratuito:Panoramica tecnica RHEL
- Cheat sheet della rete Linux
- Cheat sheet di SELinux
- Cheat sheet dei comandi comuni di Linux
- Cosa sono i container Linux?
- I nostri ultimi articoli su Linux
Ci sono anche due client di esempio (uno in C, l'altro in Python) per accedere alle librerie. Non sorprende che un client C possa accedere a una libreria scritta in C, ma il client Python illustra che una libreria scritta in C può servire client di altri linguaggi.
Librerie statiche e librerie dinamiche
I sistemi Linux hanno due tipi di librerie:
- Una libreria statica (nota anche come archivio di biblioteche) viene inserito in un client compilato staticamente (ad esempio, uno in C o Rust) durante la fase di collegamento del processo di compilazione. In effetti, ogni client ottiene la propria copia della libreria. Uno svantaggio significativo di una libreria statica viene alla ribalta se la libreria deve essere rivista (ad esempio, per correggere un bug), poiché ogni client della libreria deve essere ricollegato alla libreria statica. Una libreria dinamica, descritta di seguito, evita questa mancanza.
- Una libreria dinamica (nota anche come condivisa) viene contrassegnato durante la fase di collegamento di un programma client compilato staticamente, ma il programma client e il codice della libreria rimangono altrimenti disconnessi fino al runtime:il codice della libreria non viene inserito nel client. In fase di esecuzione, il caricatore dinamico del sistema connette una libreria condivisa con un client in esecuzione, indipendentemente dal fatto che il client provenga da un linguaggio compilato staticamente, come C, o da un linguaggio compilato dinamicamente, come Python. Di conseguenza, una libreria dinamica può essere aggiornata senza infastidire i client. Infine, più client possono condividere una singola copia di una libreria dinamica.
In generale, le librerie dinamiche sono preferite rispetto a quelle statiche, sebbene vi sia un costo in termini di complessità e prestazioni. Ecco come viene creato e pubblicato ogni tipo di libreria:
- Il codice sorgente per la libreria viene compilato in uno o più moduli oggetto, che sono file binari che possono essere inclusi in una libreria e collegati a client eseguibili.
- I moduli oggetto sono impacchettati in un unico file. Per una libreria statica, l'estensione standard è
.a
per "archivio". Per una libreria dinamica, l'estensione è.so
per "oggetto condiviso". Le due librerie di esempio, che hanno la stessa funzionalità, vengono pubblicate come filelibprimes.a
(statico) elibshprimes.so
(dinamico). Il prefissolib
viene utilizzato per entrambi i tipi di libreria. - Il file della libreria viene copiato in una directory standard in modo che i programmi client, senza problemi, possano accedere alla libreria. Una posizione tipica per la libreria, statica o dinamica, è
/usr/lib
o/usr/local/lib
; altre posizioni sono possibili.
A breve arriveranno i passaggi dettagliati per la creazione e la pubblicazione di ogni tipo di libreria. Prima, però, introdurrò le funzioni C nelle due librerie.
Le funzioni della libreria di esempio
Le due librerie di esempio sono costruite dalle stesse cinque funzioni C, quattro delle quali sono accessibili ai programmi client. La quinta funzione, che è un'utilità per una delle altre quattro, mostra come C supporta l'occultamento delle informazioni. Il codice sorgente per ciascuna funzione è sufficientemente breve da consentire alle funzioni di essere ospitate in un unico file sorgente, sebbene siano disponibili più file sorgente (ad esempio, uno per ciascuna delle quattro funzioni pubblicate).
Le funzioni di libreria sono elementari e trattano, in vari modi, i numeri primi. Tutte le funzioni prevedono valori interi senza segno (cioè non negativi) come argomenti:
- Il
is_prime
la funzione verifica se il suo singolo argomento è un numero primo. - I
are_coprimes
La funzione controlla se i suoi due argomenti hanno un massimo comun divisore (gcd) di 1, che definisce i co-primi. - I
prime_factors
la funzione elenca i fattori primi del suo argomento. - Il
goldbach
la funzione si aspetta un valore intero pari di 4 o più, elencando la somma di due numeri primi a questo argomento; potrebbero esserci più coppie di sommatori. La funzione prende il nome dal matematico del 18° secolo Christian Goldbach, la cui congettura che ogni intero pari maggiore di due sia la somma di due primi rimane uno dei più antichi problemi irrisolti nella teoria dei numeri.
La funzione di utilità gcd
risiede nei file di libreria distribuiti, ma questa funzione non è accessibile al di fuori del suo file contenitore; quindi, un client di libreria non può invocare direttamente il gcd
funzione. Uno sguardo più da vicino alle funzioni C chiarisce il punto.
Ulteriori informazioni sulle funzioni C
Ogni funzione in C ha una classe di archiviazione, che determina l'ambito della funzione. Per le funzioni ci sono due opzioni:
- La classe di archiviazione predefinita per le funzioni è
extern
, che fornisce una funzione di portata globale. Un programma client può chiamare qualsiasiextern
funzione nelle librerie di campioni. Ecco la definizione per la funzioneare_coprimes
con un esplicitoextern
:extern unsigned are_coprimes(unsigned n1, unsigned n2) {
...
} - La classe di archiviazione
static
limita l'ambito di una funzione al file in cui è definita la funzione. Nelle librerie di esempio, la funzione di utilitàgcd
èstatic
:static unsigned gcd(unsigned n1, unsigned n2) {
...
}
Funziona solo all'interno di primes.c
il file può invocare gcd
, e solo la funzione are_coprimes
lo fa. Quando le librerie statiche e dinamiche vengono compilate e pubblicate, altri programmi possono chiamare un extern
funzione come are_coprimes
ma non il static
funzione gcd
. Il static
storage class nasconde quindi il gcd
funzione dai client della libreria limitando l'ambito della funzione alle altre funzioni della libreria.
Le funzioni diverse da gcd
nel primes.c
file non deve specificare una classe di archiviazione, che per impostazione predefinita sarebbe extern
. Tuttavia, è comune nelle biblioteche creare il extern
esplicito.
C distingue tra definizioni di funzione e dichiarazioni, che è importante per le librerie. Cominciamo con le definizioni. C ha solo funzioni denominate e ogni funzione è definita con:
- Un nome univoco. Non esistono due funzioni in un programma che possono avere lo stesso nome.
- Un elenco di argomenti, che potrebbe essere vuoto. Gli argomenti vengono digitati.
- O un tipo di valore restituito (ad es.
int
per un intero con segno a 32 bit) o void
se non viene restituito alcun valore. - Un corpo racchiuso tra parentesi graffe. In un esempio inventato, il corpo potrebbe essere vuoto.
Ogni funzione in un programma deve essere definita esattamente una volta.
Ecco la definizione completa per la funzione di libreria are_coprimes
:
extern unsigned are_coprimes(unsigned n1, unsigned n2) { /* definition */
return 1 == gcd(n1, n2); /* greatest common divisor of 1? */
}
La funzione restituisce un valore booleano (0 per false o 1 per true), a seconda che i due argomenti interi abbiano un massimo comune divisore di 1. La funzione di utilità gcd
calcola il massimo comun divisore di argomenti interi n1
e n2
.
Una dichiarazione di funzione, a differenza di una definizione, non ha un corpo:
extern unsigned are_coprimes(unsigned n1, unsigned n2); /* declaration */
La dichiarazione termina con un punto e virgola dopo l'elenco degli argomenti; non ci sono parentesi graffe che racchiudono un corpo. Una funzione può essere dichiarata più volte all'interno di un programma.
Perché sono necessarie le dichiarazioni? In C, una funzione chiamata deve essere visibile al suo chiamante. Esistono vari modi per fornire tale visibilità, a seconda di quanto sia esigente il compilatore. Un modo sicuro è definire la funzione chiamata sopra il suo chiamante quando entrambi risiedono nello stesso file:
void f() {...} /* f is defined before being called */
void g() { f(); } /* ok */
La definizione della funzione f
potrebbe essere spostato sotto la chiamata dalla funzione g
se f
sono stati dichiarati sopra la chiamata:
void f(); /* declaration makes f visible to caller */
void g() { f(); } /* ok */
void f() {...} /* easier to put this above the call from g */
Ma cosa succede se la funzione chiamata risiede in un file diverso dal suo chiamante? In che modo le funzioni definite in un file vengono rese visibili in un altro file, dato che ogni funzione deve essere definita esattamente una volta in un programma?
Questo problema ha un impatto sulle librerie, statiche o dinamiche. Ad esempio, le funzioni nelle due librerie primes sono definite nel file sorgente primes.c
, le cui copie binarie si trovano in ogni libreria; ma queste funzioni definite devono essere visibili a un client di libreria in C, che è un programma separato con i propri file sorgente.
Fornire visibilità tra i file è ciò che possono fare le dichiarazioni di funzione. Per gli esempi "primes", c'è un file di intestazione chiamato primes.h
che dichiara le quattro funzioni da rendere visibili ai client della libreria in C:
/** header file primes.h: function declarations **/
extern unsigned is_prime(unsigned);
extern void prime_factors(unsigned);
extern unsigned are_coprimes(unsigned, unsigned);
extern void goldbach(unsigned);
Queste dichiarazioni fungono da interfaccia specificando la sintassi di chiamata per ciascuna funzione.
Per comodità del cliente, il file di testo primes.h
potrebbe essere memorizzato in una directory nel percorso di ricerca del compilatore C. Le posizioni tipiche sono /usr/include
e /usr/local/include
. Un client C dovrebbe #include
questo file di intestazione nella parte superiore del codice sorgente del client. (Un file di intestazione viene quindi importato nella "testata" di un altro file sorgente.) I file di intestazione C servono anche come input per le utilità (ad esempio, bindgen
di Rust ) che consentono ai client in altre lingue di accedere a una libreria C.
In sintesi, una funzione di libreria viene definita esattamente una volta ma dichiarata dove necessario; qualsiasi client di libreria in C necessita della dichiarazione. Un file di intestazione dovrebbe contenere dichiarazioni di funzioni, ma non definizioni di funzioni. Se un file di intestazione conteneva definizioni, il file potrebbe essere incluso più volte in un programma C, violando così la regola secondo cui una funzione deve essere definita esattamente una volta in un programma C.
Il codice sorgente della libreria
Di seguito è riportato il codice sorgente per due librerie. Questo codice, il file di intestazione e i due client di esempio sono disponibili sul mio sito Web.
Le funzioni della libreria
#include <stdio.h>
#include <math.h>
extern unsigned is_prime(unsigned n) {
if (n <= 3) return n > 1; /* 2 and 3 are prime */
if (0 == (n % 2) || 0 == (n % 3)) return 0; /* multiples of 2 or 3 aren't */
/* check that n is not a multiple of other values < n */
unsigned i;
for (i = 5; (i * i) <= n; i += 6)
if (0 == (n % i) || 0 == (n % (i + 2))) return 0; /* not prime */
return 1; /* a prime other than 2 or 3 */
}
extern void prime_factors(unsigned n) {
/* list 2s in n's prime factorization */
while (0 == (n % 2)) {
printf("%i ", 2);
n /= 2;
}
/* 2s are done, the divisor is now odd */
unsigned i;
for (i = 3; i <= sqrt(n); i += 2) {
while (0 == (n % i)) {
printf("%i ", i);
n /= i;
}
}
/* one more prime factor? */
if (n > 2) printf("%i", n);
}
/* utility function: greatest common divisor */
static unsigned gcd(unsigned n1, unsigned n2) {
while (n1 != 0) {
unsigned n3 = n1;
n1 = n2 % n1;
n2 = n3;
}
return n2;
}
extern unsigned are_coprimes(unsigned n1, unsigned n2) {
return 1 == gcd(n1, n2);
}
extern void goldbach(unsigned n) {
/* input errors */
if ((n <= 2) || ((n & 0x01) > 0)) {
printf("Number must be > 2 and even: %i is not.\n", n);
return;
}
/* two simple cases: 4 and 6 */
if ((4 == n) || (6 == n)) {
printf("%i = %i + %i\n", n, n / 2, n / 2);
return;
}
/* for n >= 8: multiple possibilities for many */
unsigned i;
for (i = 3; i < (n / 2); i++) {
if (is_prime(i) && is_prime(n - i)) {
printf("%i = %i + %i\n", n, i, n - i);
/* if one pair is enough, replace this with break */
}
}
}
Queste funzioni servono come grano per il mulino della biblioteca. Le due librerie derivano esattamente dallo stesso codice sorgente e dal file di intestazione primes.h
è l'interfaccia C per entrambe le librerie.
Costruire le biblioteche
I passaggi per la creazione e la pubblicazione di librerie statiche e dinamiche differiscono in alcuni dettagli. Sono necessari solo tre passaggi per la libreria statica e solo altri due per la libreria dinamica. I passaggi aggiuntivi nella creazione della libreria dinamica riflettono la maggiore flessibilità dell'approccio dinamico. Cominciamo con la libreria statica.
Il file sorgente della libreria primes.c
viene compilato in un modulo oggetto. Ecco il comando, con il segno di percentuale come prompt di sistema (i doppi segni acuti introducono i miei commenti):
% gcc -c primes.c ## step 1 static
Questo produce il file binario primes.o
, il modulo oggetto. Il flag -c
significa solo compilare.
Il passaggio successivo consiste nell'archiviare i moduli oggetto utilizzando ar
di Linux utilità:
% ar -cvq libprimes.a primes.o ## step 2 static
I tre flag -cvq
sono l'abbreviazione di "crea", "verbose" e "quick append" (nel caso in cui nuovi file debbano essere aggiunti a un archivio). Ricordiamo che il prefisso lib
è standard, ma il nome della libreria è arbitrario. Naturalmente, il nome file di una libreria deve essere univoco per evitare conflitti.
L'archivio è pronto per essere pubblicato:
% sudo cp libprimes.a /usr/local/lib ## step 3 static
La libreria statica è ora accessibile ai client, i cui esempi sono in arrivo. (Il sudo
è incluso per garantire i diritti di accesso corretti per la copia di un file in /usr/local/lib
.)
La libreria dinamica richiede anche uno o più moduli oggetto per il confezionamento:
% gcc primes.c -c -fpic ## step 1 dynamic
Il flag aggiunto -fpic
indica al compilatore di generare codice indipendente dalla posizione, che è un modulo binario che non deve essere caricato in una posizione di memoria fissa. Tale flessibilità è fondamentale in un sistema di più librerie dinamiche. Il modulo oggetto risultante è leggermente più grande di quello generato per la libreria statica.
Ecco il comando per creare il singolo file di libreria dai moduli oggetto:
% gcc -shared -Wl,-soname,libshprimes.so -o libshprimes.so.1 primes.o ## step 2 dynamic
Il flag -shared
indica che la libreria è condivisa (dinamica) anziché statica. Il -Wl
flag introduce un elenco di opzioni del compilatore, la prima delle quali imposta il soname
della libreria dinamica , che è richiesto. Il soname
prima specifica il nome logico della libreria (libshprimes.so
) e poi, seguendo il -o
flag, il nome del file fisico della libreria (libshprimes.so.1
). L'obiettivo è mantenere costante il nome logico consentendo al tempo stesso di modificare il nome del file fisico con le nuove versioni. In questo esempio, l'1 alla fine del nome del file fisico libshprimes.so.1
rappresenta la prima versione della libreria. I nomi dei file logici e fisici potrebbero essere gli stessi, ma è consigliabile avere nomi separati. Un client accede alla libreria tramite il suo nome logico (in questo caso, libshprimes.so
), come chiarirò a breve.
Il passaggio successivo consiste nel rendere la libreria condivisa facilmente accessibile ai client copiandola nella directory appropriata; per esempio, ancora /usr/local/lib again:
% sudo cp libshprimes.so.1 /usr/local/lib ## step 3 dynamic
È ora impostato un collegamento simbolico tra il nome logico della libreria condivisa (libshprimes.so
) e il nome completo del file fisico (/usr/local/lib/libshprimes.so.1
). È più facile dare il comando con /usr/local/lib
come directory di lavoro:
% sudo ln --symbolic libshprimes.so.1 libshprimes.so ## step 4 dynamic
Il nome logico libshprimes.so
non dovrebbe cambiare, ma la destinazione del collegamento simbolico (libshrimes.so.1
) può essere aggiornato secondo necessità per nuove implementazioni di librerie che risolvono bug, migliorano le prestazioni e così via.
Il passaggio finale (precauzionale) consiste nell'invocare ldconfig
utility, che configura il caricatore dinamico del sistema. Questa configurazione assicura che il caricatore trovi la libreria appena pubblicata:
% sudo ldconfig ## step 5 dynamic
La libreria dinamica è ora pronta per i clienti, inclusi i due esempi che seguono.
Un client di libreria C
Il client C di esempio è il tester del programma, il cui codice sorgente inizia con due #include
direttive:
#include <stdio.h> /* standard input/output functions */
#include <primes.h> /* my library functions */
Le parentesi angolari intorno ai nomi dei file indicano che questi file di intestazione si trovano nel percorso di ricerca del compilatore (nel caso di primes.h
, la directory /usr/local/include
). Senza questo #include
, il compilatore si lamenterebbe delle dichiarazioni mancanti per funzioni come is_prime
e prime_factors
, che sono pubblicati in entrambe le biblioteche. A proposito, non è necessario che il codice sorgente del programma tester cambi per testare ciascuna delle due librerie.
Al contrario, il file sorgente per la libreria (primes.c
) si apre con questi #include
direttive:
#include <stdio.h>
#include <math.h>
Il file di intestazione math.h
è richiesto perché la funzione di libreria prime_factors
chiama la funzione matematica sqrt
nella libreria standard libm.so
.
Per riferimento, ecco il codice sorgente per il programma tester:
Il programma dei tester
#include <stdio.h>
#include <primes.h>
int main() {
/* is_prime */
printf("\nis_prime\n");
unsigned i, count = 0, n = 1000;
for (i = 1; i <= n; i++) {
if (is_prime(i)) {
count++;
if (1 == (i % 100)) printf("Sample prime ending in 1: %i\n", i);
}
}
printf("%i primes in range of 1 to a thousand.\n", count);
/* prime_factors */
printf("\nprime_factors\n");
printf("prime factors of 12: ");
prime_factors(12);
printf("\n");
printf("prime factors of 13: ");
prime_factors(13);
printf("\n");
printf("prime factors of 876,512,779: ");
prime_factors(876512779);
printf("\n");
/* are_coprimes */
printf("\nare_coprime\n");
printf("Are %i and %i coprime? %s\n",
21, 22, are_coprimes(21, 22) ? "yes" : "no");
printf("Are %i and %i coprime? %s\n",
21, 24, are_coprimes(21, 24) ? "yes" : "no");
/* goldbach */
printf("\ngoldbach\n");
goldbach(11); /* error */
goldbach(4); /* small one */
goldbach(6); /* another */
for (i = 100; i <= 150; i += 2) goldbach(i);
return 0;
}
Durante la compilazione di tester.c
in un eseguibile, la parte difficile è l'ordine dei flag di collegamento. Ricordiamo che le due librerie di esempio iniziano con il prefisso lib
, e ognuno ha la consueta estensione:.a
per la libreria statica libprimes.a
e .so
per la libreria dinamica libshprimes.so
. In una specifica dei collegamenti, il prefisso lib
e l'estensione cade. Un flag di collegamento inizia con -l
(L minuscola) e un comando di compilazione può contenere molti flag di collegamento. Ecco il comando di compilazione completo per il programma tester, utilizzando la libreria dinamica come esempio:
% gcc -o tester tester.c -lshprimes -lm
Il primo flag di collegamento identifica la libreria libshprimes.so
e il secondo flag di collegamento identifica la libreria matematica standard libm.so
.
Il linker è pigro, il che significa che l'ordine dei flag di collegamento è importante. Ad esempio, l'inversione dell'ordine delle specifiche del collegamento genera un errore in fase di compilazione:
% gcc -o tester tester.c -lm -lshprimes ## danger!
Il flag che si collega a libm.so
viene prima, ma nessuna funzione da questa libreria viene invocata esplicitamente nel programma tester; quindi, il linker non si collega a math.so
biblioteca. La chiamata a sqrt
la funzione di libreria si verifica solo in prime_factors
funzione che ora è contenuta in libshprimes.so
biblioteca. L'errore risultante nella compilazione del programma tester è:
primes.c: undefined reference to 'sqrt'
Di conseguenza, l'ordine dei flag di collegamento dovrebbe notificare al linker che sqrt
è necessaria la funzione:
% gcc -o tester tester.c -lshprimes -lm ## -lshprimes 1st
Il linker risponde alla chiamata alla funzione di libreria sqrt
nel libshprimes.so
library e, quindi, fa il collegamento appropriato alla libreria matematica libm.so
. C'è un'opzione più complicata per il collegamento che supporta sia l'ordine link-flag; in questo caso, tuttavia, il modo più semplice è disporre i flag di collegamento in modo appropriato.
Ecco alcuni output da un'esecuzione del client tester:
is_prime
Sample prime ending in 1: 101
Sample prime ending in 1: 401
...
168 primes in range of 1 to a thousand.
prime_factors
prime factors of 12: 2 2 3
prime factors of 13: 13
prime factors of 876,512,779: 211 4154089
are_coprime
Are 21 and 22 coprime? yes
Are 21 and 24 coprime? no
goldbach
Number must be > 2 and even: 11 is not.
4 = 2 + 2
6 = 3 + 3
...
32 = 3 + 29
32 = 13 + 19
...
100 = 3 + 97
100 = 11 + 89
...
Per il goldbach
funzione, anche un valore pari relativamente piccolo (ad esempio, 18) può avere più coppie di numeri primi che si sommano ad esso (in questo caso, 5+13 e 7+11). Tali coppie prime multiple sono tra i fattori che complicano un tentativo di dimostrazione della congettura di Goldbach.
Concludere con un client Python
Python, a differenza di C, non è un linguaggio compilato in modo statico, il che significa che il client Python di esempio deve accedere alla versione dinamica anziché statica della libreria primes. Per fare ciò, Python dispone di vari moduli (standard e di terze parti) che supportano un'interfaccia di funzione esterna (FFI), che consente a un programma scritto in una lingua di richiamare funzioni scritte in un'altra. Python ctypes
è una FFI standard e relativamente semplice che consente al codice Python di chiamare funzioni C.
Qualsiasi FFI ha delle sfide perché è improbabile che le lingue di interfaccia abbiano esattamente gli stessi tipi di dati. Ad esempio, la libreria primes utilizza il tipo C unsigned int
, che Python non ha; i ctypes
FFI mappa un C unsigned int
a un int
Python . Dei quattro extern
Funzioni C pubblicate nella libreria primes, due si comportano meglio in Python con ctypes
espliciti configurazione.
Le funzioni C prime_factors
e goldbach
avere void
invece di un tipo restituito, ma ctypes
per impostazione predefinita sostituisce la C void
con Python int
. Quando vengono chiamate dal codice Python, le due funzioni C restituiscono quindi un valore intero casuale (quindi privo di significato) dallo stack. Tuttavia, ctypes
può essere configurato per fare in modo che le funzioni restituiscano None
(tipo null di Python) invece. Ecco la configurazione per prime_factors
funzione:
primes.prime_factors.restype = None
Un'istruzione simile gestisce il goldbach
funzione.
La sessione interattiva di seguito (in Python 3) mostra che l'interfaccia tra un client Python e la libreria primes è semplice:
>>> from ctypes import cdll
>>> primes = cdll.LoadLibrary("libshprimes.so") ## logical name
>>> primes.is_prime(13)
1
>>> primes.is_prime(12)
0
>>> primes.are_coprimes(8, 24)
0
>>> primes.are_coprimes(8, 25)
1
>>> primes.prime_factors.restype = None
>>> primes.goldbach.restype = None
>>> primes.prime_factors(72)
2 2 2 3 3
>>> primes.goldbach(32)
32 = 3 + 29
32 = 13 + 19
Le funzioni nella libreria dei numeri primi utilizzano solo un tipo di dati semplice, unsigned int
. Se questa libreria C utilizzava tipi complicati come le strutture e se i puntatori alle strutture venivano passati e restituiti dalle funzioni della libreria, allora un FFI più potente di ctypes
potrebbe essere migliore per un'interfaccia fluida tra Python e C. Tuttavia, i ctypes
esempio mostra che un client Python può utilizzare una libreria scritta in C. In effetti, la popolare libreria NumPy per il calcolo scientifico è scritta in C e quindi esposta in un'API Python di alto livello.
La semplice libreria numeri primi e la libreria NumPy avanzata sottolineano che il C rimane la lingua franca tra i linguaggi di programmazione. Quasi tutti i linguaggi possono parlare con C e, tramite C, con qualsiasi altro linguaggio che parla con C. Python parla facilmente con C e, come altro esempio, Java potrebbe fare lo stesso quando Project Panama diventa un'alternativa a Java Native Interface (JNI ).