Vantaggi e svantaggi di una FFI rispetto a un'API C / C ++ / etc

1

Sto cercando di capire i vantaggi e gli svantaggi di una FFI (Foreign Function Interface) (in cui il linguaggio di alto livello può chiamare direttamente la maggior parte delle funzioni C e può manipolare le strutture dati C) rispetto a C / C ++ / etc. API, in cui la funzione nativa è passata a strutture di dati specifiche del runtime e deve utilizzare le API fornite dal runtime per manipolarle.

Per me, il primo di due soli vantaggi di quest'ultimo approccio sembra essere in linguaggi che non supportano l'incapsulamento corretto, nel qual caso un'estensione nativa può incapsulare codice non sicuro in un modo che altrimenti sarebbe difficile. Ma Python ha ctypes e CFFI, Ruby ha FFI e LuaJIT ha il suo FFI (su cui è basato CFFI). Questo sembra davvero valido solo per le macchine virtuali che devono eseguire in modo sicuro codice non attendibile (Javascript, Lua, Dart) o che sono interpretate in modo puramente formale.

Java ha solo JNI integrato, ma sta cambiando. I linguaggi di famiglia .NET possono usufruire di una superba FFI (P / Invoke).

In particolare, l'interfacciamento di C con OCaml e HHVM richiede la scrittura di codice C e C ++, anche se entrambi sono compilati. Perché?

Modifica: la mia domanda è "Quali vantaggi e svantaggi ha un FFI rispetto a un API C?" Uno o l'altro è necessario; la mia domanda riguarda i loro rispettivi vantaggi e svantaggi.

    
posta Demi 08.05.2016 - 08:46
fonte

3 risposte

1

Prendi una libreria C complessa che è stata scritta secoli fa, non scritta specificamente per essere usata da Lua o Python o qualsiasi altra cosa, con un enorme numero di funzioni.

Puoi:

  1. Prova a scrivere un wrapper dylib su di esso in C che chiama l'altra libreria C e si concentra su tutto il lavoro di collegamento necessario per creare un modulo Python o Lua appropriato e idiomatico, ad es.
  2. Basta usare la libreria subito in Lua o Python o in qualsiasi altra lingua attraverso la FFI e testarla, giocarci, sperimentare, iniziare a completarla, ecc.

La seconda opzione è piuttosto allettante per me. Inoltre, non è necessario utilizzare direttamente la FFI. Puoi ancora, ad esempio, usare l'FFI di LuaJIT per chiamare direttamente le funzioni C dietro una bella tabella Lua che è conforme agli idiomi Lua (indicizzazione basata su 1, ad esempio). In realtà trovo che impiegare molto meno tempo prima del codice FFI per l'esportazione di un vero modulo Lua che deve tradurre tutto in tabelle e numeri e così via usando l'API C di Lua per costruire un modulo Lua conforme.

L'FFI ti consente di fare più del tuo lavoro direttamente nella lingua che hai scelto come target. L'alternativa richiederà inevitabilmente di passare molto più tempo in C.

Prendi OpenGL come esempio e facciamo finta che non ci fossero moduli OGL Lua già disponibili. In tal caso, con l'FFI di LuaJIT, è possibile disegnare forme in una finestra in pochissimo tempo semplicemente chiamando direttamente le funzioni OpenGL attraverso l'FFI. Presto potresti iniziare a confezionarlo nei tavoli Lua con delle belle funzioni adatte alla tua applicazione. Altrimenti potresti dover trascorrere un intero weekend o legare più a lungo e tradurre un carico di barche di funzioni OpenGL, costanti, ecc. In C per costruire un modulo Lua in miniatura con solo un piccolo sottoinsieme di funzionalità OGL e quindi importarlo solo per ottenere un triangolo uno schermo, solo per poi legare più funzioni e così via.

È molto, molto più tempo per iniziare a provare ad esportare un modulo conforme alla tua lingua anziché essere in grado di chiamare direttamente le API C direttamente da esso. Ciò vale anche se si utilizzano elementi utili per l'associazione come Boost.Python o LuaBind. È un processo che richiede molto tempo per essere eseguito correttamente e FFI ignora tutto ciò.

In realtà ritengo che la FFI sia la strada da percorrere e preferirei esportare moduli specifici per una lingua. Ad esempio, alcuni utenti hanno avvolto il mio SDK per consentire loro di chiamare le sue funzioni C da C #. Non ho mai dovuto scrivere un modulo C # o .NET per farglielo fare. Chiamano direttamente le mie funzioni C dall'FFI di C #, che consente loro di importare le funzioni C da dylibs e chiamarle subito.

    
risposta data 14.12.2017 - 07:17
fonte
2

Gli FFI sono basati su C. Assomigliano a C. E quindi, le interfacce basate su FFI sono limitate a C.

Userò Lua come esempio, poiché è quello che mi è più familiare.

Se stavi usando l'API Lua C, potresti esporre un sistema a Lua che guarda e si comporta come se Lua si aspettasse di farlo. Potrebbe esporre oggetti che il codice Lua potrebbe memorizzare e chiamare come le altre cose di Lua: variable_name.member_func(...) e così via. Potresti persino implementare l'overloading delle funzioni.

Questo non è possibile direttamente tramite FFI. Oh certo, l'FFI di LuaJIT ti consente di applicare i metatables ai tipi. Ma ciò accade dopo la parte FFI quando si caricano le strutture dati. Richiede lavoro manuale nello script. Considerando che farlo in C API richiede zero lavori di script; per te è stata creata un'API corretta.

Ed è importante esaminare quale sarebbe una "API appropriata". Come la maggior parte dei linguaggi di scripting, Lua consente molte cose che C non fa. Con l'API Lua C, puoi esporre a funzioni Lua C che:

  • Prendi numeri arbitrari di parametri.
  • Restituisce numeri arbitrari di valori.
  • Gestisci direttamente i valori dei parametri Lua (dati utente, tabelle, funzioni, ecc.)
  • Fornisci errori tramite il meccanismo di errore Lua standard.
  • Restituisci i dati compatibili con l'istruzione generica for di Lua.

Le interfacce create da FFI non possono fare nulla direttamente. Di nuovo, puoi sempre scrivere wrapper e così via per loro, ma poi stai scrivendo un wrapper.

E proprio per interesse aggiunto, LuaJIT rompe le convenzioni di Lua sull'indicizzazione degli array. Nella normale Lua, gli array sono sempre basati su 1. Ma con l'FFI di LuaJIT, gli array che vengono allocati sono a base zero come C. Qualunque cosa si possa desiderare di dire su quale convenzione sia meglio, avere entrambe le convenzioni contemporaneamente è molto peggio di entrambe.

Gli FFI hanno una tendenza verso quel tipo di interfaccia. Poiché sono distorti verso C, a volte entrano in conflitto con le convenzioni del linguaggio di scripting.

Poi c'è il problema della sicurezza. Con una FFI, in particolare una come LuaJIT, stai praticamente permettendo che il codice C sia scritto in Lua. Quindi, invece di isolare gli elementi non sicuri del tuo programma in alcune librerie C, stai permettendo agli aspetti non sicuri di C di infiltrarsi nell'ambiente di scripting. E invece di ottenere errori Lua che possono essere gestiti e recuperati, di solito si verificano arresti anomali.

Ciò renderà molto più difficile il debug poiché la maggior parte degli ambienti di scripting non ha i debugger validi come quelli C / C ++.

A seconda delle esigenze, questi problemi di sicurezza potrebbero essere completamente inaccettabili. Non vorrei che la gente potesse mandare in crash il processore di script Lua di Wikipedia sul server, solo perché qualcuno ha casualmente fatto un brutto cast di FFI in uno script Lua.

Alla fine della giornata, FFI è la soluzione migliore per l'interfaccia con sistemi a cui non è mai stato previsto l'accesso dalla lingua host. In quanto tale, un'interfaccia oddball è meglio di nessuna interfaccia. Puoi isolare le stranezze per renderlo più simile alla lingua host.

Ma se stai scrivendo un sistema che stai progettando esponendo a un linguaggio di scripting, darai ai programmatori di script un'esperienza di programmazione molto più ragionevole. Ciò non significa che non puoi utilizzare FFI per rendere alcune cose più veloci (array di grandi dimensioni, ecc.). Ma se puoi evitarlo, non dovresti semplicemente lanciare un'interfaccia DLL raw a qualcuno se puoi aiutarlo.

    
risposta data 11.06.2016 - 07:31
fonte
1

Cerco di spiegare la mia esperienza con Java e JNI.

Con Java hai un ottimo linguaggio per creare applicazioni di lavoro veloci e multipiattaforma. È abbastanza facile da capire e funziona bene. Ma sai, se stiamo cercando di utilizzare qualcosa come Audio IO, che Java non riesce. Per questo caso stiamo usando il JNI per implementare IO su C ++. Per cose del genere nel mio parere, C ++ è davvero grandioso, ma .... per la creazione di applicazioni veloci e ben funzionanti. Applicazioni C ++ non è molto ... Handy. .. Così come piccola conclusione:

interfacciamento:

  • Ci dà l'opportunità di creare applicazioni IO, yeaaah:)

  • hai la possibilità di ottimizzare al meglio le parti critiche e il resto è ottimizzato automaticamente.

  • Big Minus per Java è che troppe chiamate dal JNI in un secondo possono produrre un sovraccarico pesante. Ho parlato con qualcuno e mi ha detto che il JNI non funziona più bene con una chiamata ogni 100 microsecondi. [Ma normale se non stai sviluppando un'applicazione critica per il tempo funziona ...]

  • A volte è scomodo e non è trasparente; Ci possono essere problemi

-L'uso di un linguaggio come java stesso rende il codice non trasparente. Non sai cosa sta facendo la JVM.

Non interfacciamento :

+ Il codice è completamente trasparente e puoi ottimizzare quanto vuoi.

  • Eg. Le applicazioni grafiche non sono facili da programmare in C ++ o devi inventare molte novità che già esistono (molto lavoro!)

Se ci sono ulteriori domande si prega di commentare Migliorerò questa domanda [Spero che aiuti:)]!

    
risposta data 11.05.2016 - 21:56
fonte