Interoperabilitate Nativă C/C++ cu Dart FFI (Foreign Function Interface): Apeluri Directe de Biblioteci C fără Overhead în Dart

Ghid tehnic complet pentru Dart FFI (dart:ffi): încărcarea dinamică a fișierelor .so / .dylib / .dll, maparea tipurilor native C (Pointer, Struct, Int32), gestionarea memoriei manuale cu malloc/free și apelarea funcțiilor C la performanță nativă.

Sintaxă
DynamicLibrary.open('libcrypto.so').lookupFunction<NativeFn, DartFn>('hash');

Pentru decenii întregi, apelarea codului nativ de sistem (C, C++, Rust, Go) din mediile de nivel înalt a necesitat punți de comunicare lente bazate pe canale de platformă (cum sunt Platform Channels din Flutter) sau pe mașina Java Native Interface (JNI). Aceste mecanisme impuneau serializarea și copierea fiecărui bloc de memorie între limbaje.

Dart FFI (Foreign Function Interface) elimină complet această barieră: biblioteca standard dart:ffi permite codului Dart să aloce memorie fizică nativă pe heap, să inspecteze pointeri și să apeleze direct funcții din biblioteci partajate (.so pe Linux/Android, .dylib pe macOS/iOS, .dll pe Windows) la viteza nativă a instrucțiunilor mașină!

Arhitectura Apelului Nativ prin Dart FFI
# Execuție Dart FFI:
# [Cod Dart]
#      │
#      ▼ (Mapare de tipuri: Int32 -> int, Pointer)
# [dart:ffi Binding]
#      │
#      ▼ (Apel direct de adresă în spațiul de proces - Zero-Copy!)
# [Bibliotecă Nativă C / C++ / Rust (libnative.so)]
#      │
#      ▼ (Execuție directă pe hardware, fără punte sau serializare JSON!)

Maparea Tipurilor Primitive între C și Dart

Tip Nativ C (dart:ffi) Tip Corespondent în Dart Dimensiune Memorie
Int32 / Uint32 int 4 octeți (32 biți)
Int64 / Uint64 int 8 octeți (64 biți)
Double / Float double 8 / 4 octeți
Pointer<T> Pointer<T> 8 octeți pe arhitecturi de 64 biți
Struct Subclasă a Struct Structură binară compactă aliniată nativ în C
Exemplu Complet: Încărcare Bibliotecă și Apel Funcție C
import 'dart:ffi' as ffi;
import 'dart:io' show Platform;
import 'package:ffi/ffi.dart';

// Semnătura funcției native în C:
// int32_t add_numbers(int32_t a, int32_t b);
typedef NativeAddFunc = ffi.Int32 Function(ffi.Int32, ffi.Int32);

// Semnătura corespondentă în Dart:
typedef DartAddFunc = int Function(int, int);

void main() {
  // 1. Încărcarea bibliotecii dinamice în funcție de sistemul de operare:
  final libraryPath = Platform.isMacOS
      ? 'libmath_native.dylib'
      : (Platform.isWindows ? 'math_native.dll' : 'libmath_native.so');

  final dylib = ffi.DynamicLibrary.open(libraryPath);

  // 2. Căutarea funcției prin adresa simbolică:
  final DartAddFunc addNumbers = dylib
      .lookup>('add_numbers')
      .asFunction();

  // 3. Execuție directă nativă:
  final sum = addNumbers(15, 27);
  print('Rezultat obținut direct din C nativ: $sum');

  // 4. Alocare manuală de memorie nativă (Zero-Copy String):
  final ffi.Pointer nativeString = 'Text alocat în heap-ul C'.toNativeUtf8();
  try {
    print('Lungime octeți: ${nativeString.length}');
  } finally {
    // Memoria alocată nativ trebuie eliberată manual pentru a evita memory leaks!
    calloc.free(nativeString);
  }
}