FFI Abréviation de Foreign function Interface, qui est une fonction permettant d'utiliser des fonctions écrites dans un autre langage de programmation. Dans Ruby, le gem ffi et la bibliothèque standard Fiddle emballée dans Ruby fournissent des fonctionnalités équivalentes.
Cette fois, j'aimerais utiliser le programme C de Ruby en utilisant le ffi fourni qui est un joyau.
Habituons-nous d'abord à ffi. Le code d'appel est ci-dessous.
lib.c
int add(int a, int b) {
return a + b;
}
Nous avons défini ʻadd` qui prend deux arguments de type int et renvoie le résultat de leur addition.
Pour appeler cet ajout depuis Ruby, il doit être converti au format de bibliothèque partagée (* .so). Utilisez la commande suivante pour convertir.
$ gcc -shared lib.c -o libadd.so
Si vous passez l'option -shared
au compilateur gcc, il crachera le fichier objet en tant que bibliothèque partagée.
Le fichier à appeler depuis Ruby est libadd.so
.
add.rb
require 'ffi'
module AddFFI
extend FFI::Library
ffi_lib 'libadd.so'
attach_function :add, [:int, :int], :int
end
puts AddFFI.add(1, 2)
Chargez la bibliothèque partagée que vous venez de générer avec la méthode ffi_lib
.
Ensuite, avec ʻattach_function, mappez la fonction définie dans le programme C afin qu'elle puisse être manipulée depuis Ruby. Donnez le
nom de la méthode au premier argument, le type d'argument
de la fonction définie dans C au deuxième argument et le
type de valeur de retour` au troisième argument.
Le nom de la méthode doit être le même que la fonction définie dans le programme C.
Exécutez ce fichier Ruby.
$ ruby add.rb
3
J'ai pu appeler avec succès le programme C via FFI.
Le passage par référence est un mécanisme de partage de la valeur d'une variable en passant l'adresse mémoire de la variable. La valeur n'est pas copiée, donc si vous réécrivez les informations sur cette variable à la destination de référence, la valeur de la variable conservée d'origine changera également.
[Une addition] ~~ Le passage par référence est un mécanisme pour partager la valeur d'une variable en passant l'adresse mémoire de la variable. La valeur n'est pas copiée, donc si vous réécrivez les informations sur cette variable à la destination de référence, la valeur de la variable conservée d'origine changera également. ~~ La compréhension de l'auteur du passage par référence était incorrecte, et @shiracamus et @ c-yan l'ont souligné. Pour la définition du mot passer par référence, veuillez vous référer à la section commentaires de cet article pour les conseils de vous deux. (Merci de l'avoir signalé!)
sansyo.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int *p, *q;
p = (int *)malloc(sizeof(int));
*p = 1;
printf("%d\n", *p);
//Passer par référence ici
// [Postscript]Ceci n'est pas passé par référence, mais par valeur de référence(Voir les commentaires pour plus de détails)
q = p;
*q = 2;
printf("%d\n", *p);
}
Si vous faites cela, vous verrez que la valeur de * p
a changé après l'attribution de * q
.
$ gcc -o sansyo sansyo.c
$ ./sansyo
1
2
Ce que je veux faire, c'est définir une fonction qui renvoie une valeur d'adresse (pointeur) dans le programme C et obtenir la valeur de Ruby en faisant référence à cette adresse.
Le programme C créé est le suivant.
add_sansyo.c
#include <stdio.h>
#include <stdlib.h>
int *calc(int a, int b) {
int *p;
p = (int *)malloc(sizeof(int));
*p = a + b;
printf("sansyo function address = %p\n", p);
return p;
}
int main(int argc, char *argv[]) {
int *q = calc(1, 2);
printf("calc(1, 2) = %d\n", *q);
printf("calc(1, 2)'s address = %p\n", q);
}
Pour le débogage, «calc» est appelé dans «main». Je veux renvoyer la valeur d'adresse à Ruby, donc calc renvoie un pointeur de type int.
Le résultat de l'exécution est le suivant. Cela semble fonctionner comme prévu.
$ gcc -o add_sansyo add_sansyo.c
$ ./add_sansyo
sansyo function address = 0x7f94d54026b0
calc(1, 2) = 3
calc(1, 2)'s address = 0x7f94d54026b0
Convertissez ce programme C en une bibliothèque partagée en tant que ʻadd_sansyo.so` et Ruby le chargera.
sansyo.rb
require 'ffi'
module AddFFI
extend FFI::Library
ffi_lib "add_sansyo.so"
attach_function :sansyo, [:int, :int], :pointer
end
result = AddFFI.sansyo(1, 2)
puts result
puts result.read(:int)
[Dépôt] de ffi
(https://github.com/ffi/ffi/wiki/Pointers) et rubydoc J'y fais allusion.
Pour le mappage effectué par ʻattach_function, le nom est également Zubari et la valeur de retour est spécifiée sous la forme
: pointer. ʻLa valeur de retour de la méthode AddFFI.sansyo
est la classe FFI :: Pointer
, donc la méthode d'instance appelée read
est utilisée pour obtenir la valeur de cette adresse.
Une fois exécuté, ce sera comme suit, et vous pouvez comprendre que le résultat de sortie de la fonction peut être obtenu comme prévu en touchant l'adresse référencée dans le programme C de Ruby.
$ ruby sansyo.rb
sansyo function address = 0x7fa1aad31860
#<FFI::Pointer address=0x00007fa1aad31860>
3
En utilisant ffi de Rubygems, j'ai pu appeler une fonction écrite dans un programme C de Ruby en utilisant une valeur ou un pointeur. J'aime personnellement l'idée d'optimiser un programme en combinant des fonctionnalités linguistiques.
Recommended Posts