Pular para o conteúdo principal

Interoperabilidade C usando dart:ffi

Para usar código C no seu programa Dart, use a biblioteca dart:ffi.

Aplicações Dart mobile, de linha de comando e servidor executando na plataforma Dart Native podem usar a biblioteca dart:ffi para chamar APIs C nativas, e para ler, escrever, alocar e desalocar memória nativa. FFI significa foreign function interface. Outros termos para funcionalidades similares incluem native interface e language bindings.

A documentação da API está disponível na referência da API dart:ffi.

Baixar arquivos de exemplo

#

Para trabalhar com os exemplos neste guia, baixe o diretório completo de exemplos ffi. Ele inclui os seguintes exemplos que mostram como usar a biblioteca dart:ffi:

ExemploDescrição
hello_world Como chamar uma função C sem argumentos e sem valor de retorno.
primitives Como chamar funções C que têm argumentos e valores de retorno que são ints ou pointers .
structs Como usar structs para passar strings de e para C e para lidar com estruturas C simples e complexas .
test_utils Utilitários de teste comuns para todos estes exemplos.

Revisar o exemplo hello_world

#

O exemplo hello_world tem o código mínimo necessário para chamar uma biblioteca C. Este exemplo pode ser encontrado em samples/ffi que você baixou na seção anterior.

Arquivos

#

O exemplo hello_world tem os seguintes arquivos:

Arquivo fonteDescrição
hello.dart Um arquivo Dart que usa a função hello_world() de uma biblioteca C.
pubspec.yaml O arquivo pubspec do Dart, com SDK lower bound de 3.4.
hello_library/hello.h Declara a função hello_world().
hello_library/hello.c Um arquivo C que importa hello.h e define a função hello_world().
hello_library/hello.def Um arquivo module-definition que especifica informações usadas ao compilar uma DLL.
hello_library/CMakeLists.txt Um arquivo de build CMake para compilar o código C em uma biblioteca dinâmica.

Compilar a biblioteca C cria vários arquivos, incluindo um arquivo de biblioteca dinâmica chamado libhello.dylib (macOS), libhello.dll (Windows), ou libhello.so (Linux).

Compilar e executar

#

Os comandos para compilar a biblioteca dinâmica e executar a aplicação Dart se assemelham à seguinte série.

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

Aproveitar dart:ffi

#

Para aprender como chamar uma função C usando a biblioteca dart:ffi, revise o arquivo hello.dart. Esta seção explica o conteúdo deste arquivo.

  1. Importe dart:ffi.

    dart
    import 'dart:ffi' as ffi;
    
  2. Importe a biblioteca path que você usará para armazenar o caminho da biblioteca dinâmica.

    dart
    import 'dart:io' show Platform, Directory;
    import 'package:path/path.dart' as path;
    
  3. Crie um typedef com a assinatura de tipo FFI da função C. Para aprender sobre os tipos mais usados de acordo com a biblioteca dart:ffi, consulte Interface com tipos nativos.

    dart
    typedef hello_world_func = ffi.Void Function();
    
  4. Crie um typedef para a variável a ser usada ao chamar a função C.

    dart
    typedef HelloWorld = void Function();
    
  5. Crie uma variável para armazenar o caminho da biblioteca 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. Abra a biblioteca dinâmica que contém a função C.

    dart
    final dylib = ffi.DynamicLibrary.open(libraryPath);
    
  7. Obtenha uma referência para a função C e coloque-a em uma variável. Este código usa os typedefs dos passos 2 e 3, junto com a variável da biblioteca dinâmica do passo 4.

    dart
    final HelloWorld hello = dylib
        .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
        .asFunction();
    
  8. Chame a função C.

    dart
    hello();
    

Depois de entender o exemplo hello_world, consulte os outros exemplos dart:ffi.

Empacotar e carregar bibliotecas C

#

O método para empacotar / distribuir e então carregar uma biblioteca C nativa depende da plataforma e tipo de biblioteca.

Para aprender como, consulte as seguintes páginas e exemplos.

Interface com tipos nativos

#

A biblioteca dart:ffi fornece múltiplos tipos que implementam NativeType e representam tipos nativos em C. Você pode instanciar alguns tipos nativos. Alguns outros tipos nativos podem ser usados apenas como marcadores em assinaturas de tipo.

Podem instanciar estes marcadores de assinatura de tipo

#

Os seguintes tipos nativos podem ser usados como marcadores em assinaturas de tipo. Eles ou seus subtipos podem ser instanciados em código Dart.

Tipo DartDescrição
Array Um array de tamanho fixo de itens. Supertipo de arrays de tipos específicos.
Pointer Representa um pointer na memória C nativa.
Struct O supertipo de todos os tipos struct FFI.
Union O supertipo de todos os tipos union FFI.

Servem apenas como marcadores de assinatura de tipo

#

A seguinte lista mostra quais tipos nativos agnósticos de plataforma servem como marcadores em assinaturas de tipo. Eles não podem ser instanciados em código Dart.

Tipo DartDescrição
Bool Representa um bool nativo em C.
Double Representa um double de 64 bits nativo em C.
Float Representa um float de 32 bits nativo em C.
Int8 Representa um inteiro signed de 8 bits nativo em C.
Int16 Representa um inteiro signed de 16 bits nativo em C.
Int32 Representa um inteiro signed de 32 bits nativo em C.
Int64 Representa um inteiro signed de 64 bits nativo em C.
NativeFunction Representa um tipo de função em C.
Opaque O supertipo de todos os tipos opaque em C.
Uint8 Representa um inteiro unsigned de 8 bits nativo em C.
Uint16 Representa um inteiro unsigned de 16 bits nativo em C.
Uint32 Representa um inteiro unsigned de 32 bits nativo em C.
Uint64 Representa um inteiro unsigned de 64 bits nativo em C.
Void Representa o tipo void em C.

Também existem muitos tipos nativos marcadores específicos de ABI que estendem AbiSpecificInteger. Para aprender como estes tipos mapeiam em plataformas específicas, consulte a documentação da API vinculada na seguinte tabela.

Tipo DartDescrição
AbiSpecificInteger O supertipo de todos os tipos inteiros específicos de ABI.
Int Representa o tipo int em C.
IntPtr Representa o tipo intptr_t em C.
Long Representa o tipo long int (long) em C.
LongLong Representa o tipo long long em C.
Short Representa o tipo short em C.
SignedChar Representa o tipo signed char em C.
Size Representa o tipo size_t em C.
UintPtr Representa o tipo uintptr_t em C.
UnsignedChar Representa o tipo unsigned char em C.
UnsignedInt Representa o tipo unsigned int em C.
UnsignedLong Representa o tipo unsigned long int em C.
UnsignedLongLong Representa o tipo unsigned long long em C.
UnsignedShort Representa o tipo unsigned short em C.
WChar Representa o tipo wchar_t em C.

Gerar bindings FFI com package:ffigen

#

Para superfícies de API grandes, pode ser demorado escrever os bindings Dart que integram com o código C. Para fazer o Dart criar wrappers FFI a partir de arquivos header C, use o gerador de bindings package:ffigen.

Para aprender sobre suporte para code assets em Dart FFI, consulte a referência da API dart:ffi para Native e DefaultAsset.

Compilar e empacotar código nativo

#

Os build hooks do Dart (anteriormente conhecidos como native assets) permitem que pacotes contenham code assets nativos que são transparentemente compilados, empacotados e disponibilizados em tempo de execução. Para mais informações, veja Hooks.

Encontrou essa página sem tradução ou que precisa de correção? Abra uma issue e nos ajude a manter esse site em PT-BR para ajudar pessoas como você.