Saltar al contenido principal

Interoperabilidad con C usando dart:ffi

Para usar código C en tu programa Dart, usa la librería dart:ffi.

Las aplicaciones móviles, de línea de comandos y de servidor de Dart que se ejecutan en la plataforma nativa de Dart (Dart Native platform) pueden usar la librería dart:ffi para llamar a las APIs nativas de C, así como para leer, escribir, asignar y liberar memoria nativa. FFI significa interfaz de funciones externas (foreign function interface). Otros términos para funcionalidades similares incluyen interfaz nativa (native interface) y enlaces de lenguaje (language bindings).

La documentación de la API está disponible en la referencia de la API de dart:ffi.

Descarga los archivos de ejemplo

#

Para trabajar con los ejemplos de esta guía, descarga el directorio completo de ejemplos de ffi. Este incluye los siguientes ejemplos que muestran cómo usar la librería dart:ffi:

ExampleDescription
hello_world Cómo llamar a una función C sin argumentos ni valor de retorno.
primitives Cómo llamar a funciones C que tienen argumentos y valores de retorno que son enteros (ints) o punteros (pointers).
structs Cómo usar structs para pasar cadenas (strings) hacia y desde C y para manejar estructuras C simples y complejas.
test_utils Utilidades de prueba comunes para todos estos ejemplos.

Revisa el ejemplo hello_world

#

El ejemplo hello_world tiene el código mínimo necesario para llamar a una librería C. Este ejemplo se puede encontrar en samples/ffi que descargaste en la sección anterior.

Archivos

#

El ejemplo hello_world contiene los siguientes archivos:

Source fileDescription
hello.dart Un archivo Dart que utiliza la función hello_world() de una librería C.
pubspec.yaml El archivo pubspec de Dart, con un límite inferior del SDK de 3.4.
hello_library/hello.h Declara la función hello_world().
hello_library/hello.c Un archivo C que importa hello.h y define la función hello_world().
hello_library/hello.def Un archivo de definición de módulo que especifica la información utilizada al compilar una DLL.
hello_library/CMakeLists.txt Un archivo de compilación de CMake para compilar el código C en una librería dinámica.

La compilación de la librería C crea varios archivos, incluyendo un archivo de librería dinámica llamado libhello.dylib (macOS), libhello.dll (Windows) o libhello.so (Linux).

Compila y ejecuta

#

Los comandos para compilar la librería dinámica y ejecutar la aplicación Dart se parecerían a la siguiente serie.

cd hello_library
cmake .
...
make
...
cd ..
dart pub get
dart run hello.dart
Hello World

Aprovecha dart:ffi

#

Para aprender cómo llamar a una función C usando la librería dart:ffi, revisa el archivo hello.dart. Esta sección explica el contenido de este archivo.

  1. Importa dart:ffi.

    dart
    import 'dart:ffi' as ffi;
    
  2. Importa la librería path que usarás para almacenar la ruta de la librería dinámica.

    dart
    import 'dart:io' show Platform, Directory;
    import 'package:path/path.dart' as path;
    
  3. Crea un typedef con la firma del tipo FFI de la función C.
    Para conocer los tipos más utilizados de acuerdo con la librería dart:ffi, consulta Interactuar con tipos nativos.

    dart
    typedef hello_world_func = ffi.Void Function();
    
  4. Crea un typedef para la variable a usar cuando llames a la función C.

    dart
    typedef HelloWorld = void Function();
    
  5. Crea una variable para almacenar la ruta de la librería dinámica.

    dart
    final String libraryPath;
    if (Platform.isMacOS) {
      libraryPath = path.join(
        Directory.current.path,
        'hello_library',
        'libhello.dylib',
      );
    } else if (Platform.isWindows) {
      libraryPath = path.join(
        Directory.current.path,
        'hello_library',
        'Debug',
        'hello.dll',
      );
    } else {
      libraryPath = path.join(
        Directory.current.path,
        'hello_library',
        'libhello.so',
      );
    }
    
  6. Abre la librería dinámica que contiene la función C.

    dart
    final dylib = ffi.DynamicLibrary.open(libraryPath);
    
  7. Obtén una referencia a la función C y colócala en una variable. Este código utiliza los typedefs de los pasos 2 y 3, junto con la variable de librería dinámica del paso 4.

    dart
    final HelloWorld hello = dylib
        .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
        .asFunction();
    
  8. Llama a la función C.

    dart
    hello();
    

Una vez que comprendas el ejemplo hello_world, consulta los otros ejemplos de dart:ffi.

Empaqueta y carga librerías C

#

El método para empaquetar / empaquetar / distribuir y luego cargar una librería C nativa depende de la plataforma y del tipo de librería.

Para aprender cómo, consulta las siguientes páginas y ejemplos.

Interactúa con tipos nativos

#

La librería dart:ffi proporciona múltiples tipos que implementan NativeType y representan tipos nativos en C. Puedes instanciar algunos tipos nativos. Algunos otros tipos nativos pueden usarse únicamente como marcadores en firmas de tipos.

Pueden instanciar estos marcadores de firma de tipo

#

Los siguientes tipos nativos se pueden utilizar como marcadores en firmas de tipos. Ellos o sus subtipos pueden ser instanciados en código Dart.

Dart typeDescription
Array Un array de elementos de tamaño fijo. Supertipo de los arrays específicos de tipo.
Pointer Representa un puntero a la memoria nativa de C.
Struct El supertipo de todos los tipos de struct de FFI.
Union El supertipo de todos los tipos de unión de FFI.

Sirven únicamente como marcadores de firma de tipo

#

La siguiente lista muestra los tipos nativos independientes de la plataforma que sirven como marcadores en firmas de tipos. No se pueden instanciar en código Dart.

Dart typeDescription
Bool Representa un bool nativo en C.
Double Representa un double nativo de 64 bits en C.
Float Representa un float nativo de 32 bits en C.
Int8 Representa un entero nativo de 8 bits con signo en C.
Int16 Representa un entero nativo de 16 bits con signo en C.
Int32 Representa un entero nativo de 32 bits con signo en C.
Int64 Representa un entero nativo de 64 bits con signo en C.
NativeFunction Representa un tipo de función en C.
Opaque El supertipo de todos los tipos opacos en C.
Uint8 Representa un entero nativo de 8 bits sin signo en C.
Uint16 Representa un entero nativo de 16 bits sin signo en C.
Uint32 Representa un entero nativo de 32 bits sin signo en C.
Uint64 Representa un entero nativo de 64 bits sin signo en C.
Void Representa el tipo void en C.

También existen muchos tipos nativos marcadores específicos de la ABI que extienden AbiSpecificInteger. Para aprender cómo se mapean estos tipos en plataformas específicas, consulta la documentación de la API vinculada en la siguiente tabla.

Dart typeDescription
AbiSpecificInteger El supertipo de todos los tipos de enteros específicos de la ABI.
Int Representa el tipo int en C.
IntPtr Representa el tipo intptr_t en C.
Long Representa el tipo long int (long) en C.
LongLong Representa el tipo long long en C.
Short Representa el tipo short en C.
SignedChar Representa el tipo signed char en C.
Size Representa el tipo size_t en C.
UintPtr Representa el tipo uintptr_t en C.
UnsignedChar Representa el tipo unsigned char en C.
UnsignedInt Representa el tipo unsigned int en C.
UnsignedLong Representa el tipo unsigned long int en C.
UnsignedLongLong Representa el tipo unsigned long long en C.
UnsignedShort Representa el tipo unsigned short en C.
WChar Representa el tipo wchar_t en C.

Genera enlaces FFI con package:ffigen

#

Para superficies de API grandes, puede llevar mucho tiempo escribir los enlaces de Dart que se integran con el código C. Para que Dart cree envolturas (wrappers) FFI a partir de archivos de cabecera (header files) C, utiliza el generador de enlaces package:ffigen.

Para obtener información sobre el soporte para activos de código (code assets) en Dart FFI, consulta la referencia de la API de dart:ffi para Native y DefaultAsset.

Compila y empaqueta código nativo

#

Los build hooks de Dart (anteriormente conocidos como native assets) permiten que los paquetes contengan activos de código nativo que se compilan, empaquetan y ponen a disposición en tiempo de ejecución de manera transparente. Para obtener más información, consulta Hooks.