Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
326 changes: 322 additions & 4 deletions dart.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Dart's most controversial feature is its ~~Optional Typing~~ Static Type safety
```dart
import "dart:collection";
import "dart:math" as math;
import "dart:ffi" as ffi;
import "dart:io" show Platform;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add an explanation for show


/// Welcome to Learn Dart in 15 minutes. http://dart.dev/
/// This is an executable tutorial. You can run it with Dart or on
Expand Down Expand Up @@ -696,6 +698,318 @@ example33() {
var isBool = true;
var hasString = isBool ?? "default String";

/// Dart supports FFI (Foreign Function Interface) to call C libraries.
/// This allows Dart to interoperate with native C code for performance-critical
/// operations or to use existing C libraries.

/// Example 34: Basic FFI - Loading a C library and calling a simple function
/// Suppose you have a C library with: int add(int a, int b) { return a + b; }
example34() {
/// Load the C library (platform-specific)
/// On Linux/Android: 'libnative.so'
/// On macOS/iOS: 'libnative.dylib'
/// On Windows: 'native.dll'
final libraryPath = Platform.isWindows
? 'native.dll'
: Platform.isMacOS
? 'libnative.dylib'
: 'libnative.so';

/// Open the dynamic library
final dylib = ffi.DynamicLibrary.open(libraryPath);

/// Define the C function signature using typedef
/// C: int add(int a, int b)
typedef AddNative = ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b);
/// Dart signature for calling the function
typedef AddDart = int Function(int a, int b);

/// Lookup and bind the C function
final add = dylib.lookupFunction<AddNative, AddDart>('add');

/// Call the C function from Dart
final result = add(10, 20);
print('Example34 FFI add(10, 20) = $result');
}

/// Example 35: FFI with C Structs
/// C struct: struct Point { double x; double y; };
/// Creating Dart classes that extend ffi.Struct to represent C structs
class Point extends ffi.Struct {
@ffi.Double()
external double x;

@ffi.Double()
external double y;
}

example35() {
/// Allocate memory for a Point struct
final point = ffi.calloc<Point>();

/// Set values
point.ref.x = 10.5;
point.ref.y = 20.3;

print('Example35 Point(${point.ref.x}, ${point.ref.y})');

/// IMPORTANT: Free allocated memory to avoid memory leaks
ffi.calloc.free(point);
}

/// Example 36: FFI with pointers and arrays
example36() {
/// Allocate an array of 5 integers
final ptr = ffi.calloc<ffi.Int32>(5);

/// Access array elements using array indexing or elementAt
for (var i = 0; i < 5; i++) {
ptr[i] = i * 10;
}

print('Example36 Array values:');
for (var i = 0; i < 5; i++) {
print(' ptr[$i] = ${ptr[i]}');
}

/// Free the allocated memory
ffi.calloc.free(ptr);

/// Working with Pointer<Pointer<T>> for C strings (char**)
final stringPtr = "Hello FFI".toNativeUtf8();
print('Example36 C string: ${stringPtr.toDartString()}');
ffi.calloc.free(stringPtr);
}

/// Example 37: FFI Callbacks - passing Dart functions to C
/// C expects: typedef void (*callback_t)(int value);
typedef NativeCallbackType = ffi.Void Function(ffi.Int32);
typedef DartCallbackType = void Function(int);

example37() {
/// Create a Dart callback function
void myCallback(int value) {
print('Example37 Callback received: $value');
}

/// Convert Dart function to a native callback
/// Note: Use ffi.Pointer.fromFunction for top-level or static functions
/// For this example, we demonstrate the concept
print('Example37 FFI callbacks allow C code to call back into Dart');

/// Actual usage would be:
/// final callbackPointer = ffi.Pointer.fromFunction<NativeCallbackType>(
/// topLevelCallback,
/// /* exceptionalReturn if needed */
/// );
/// Then pass callbackPointer to C function
}

/// Example 38: FFI Memory Management Best Practices
example38() {
/// Use Arena for automatic cleanup of multiple allocations
/// This requires package:ffi

print('Example38 FFI Memory Management Tips:');
print(' 1. Always free allocated memory using ffi.calloc.free()');
print(' 2. Use try-finally to ensure cleanup happens');
print(' 3. Consider using Arena from package:ffi for automatic cleanup');
print(' 4. Be careful with Pointer lifecycle - avoid dangling pointers');

/// Example with try-finally
final ptr = ffi.calloc<ffi.Int32>();
try {
ptr.value = 42;
print(' Value: ${ptr.value}');
} finally {
ffi.calloc.free(ptr);
print(' Memory freed safely');
}
}

/// Example 39: Common FFI type mappings between C and Dart
example39() {
print('Example39 C to Dart FFI type mappings:');
print(' C int8_t <-> ffi.Int8 <-> int');
print(' C int16_t <-> ffi.Int16 <-> int');
print(' C int32_t <-> ffi.Int32 <-> int');
print(' C int64_t <-> ffi.Int64 <-> int');
print(' C uint8_t <-> ffi.Uint8 <-> int');
print(' C uint16_t <-> ffi.Uint16 <-> int');
print(' C uint32_t <-> ffi.Uint32 <-> int');
print(' C uint64_t <-> ffi.Uint64 <-> int');
print(' C float <-> ffi.Float <-> double');
print(' C double <-> ffi.Double <-> double');
print(' C void* <-> ffi.Pointer<ffi.Void>');
print(' C char* <-> ffi.Pointer<ffi.Utf8> (with conversion helpers)');
}

/// Example 40: Opaque pointers for hidden C struct implementations
/// When C library internals should remain hidden, use Opaque types
/// C code: typedef struct Database Database;
class Database extends ffi.Opaque {}

example40() {
/// Opaque types cannot be instantiated directly in Dart
/// Only pointers to them can exist: Pointer<Database>
/// This enforces encapsulation - Dart can't access internal fields
/// Memory management stays with the C library

print('Example40 Opaque pointers:');
print(' - Use ffi.Opaque for C structs you dont need to inspect');
print(' - Only work with Pointer<YourOpaque>');
print(' - C library handles all memory layout and management');
print(' - Common pattern for libraries hiding implementation details');
}

/// Example 41: Output parameters - getting data back from C via pointers
/// C function: int get_coordinates(double* x, double* y);
typedef GetCoordsNative = ffi.Int32 Function(
ffi.Pointer<ffi.Double>, ffi.Pointer<ffi.Double>);
typedef GetCoordsDart = int Function(
ffi.Pointer<ffi.Double>, ffi.Pointer<ffi.Double>);

example41() {
/// Allocate space for output parameters
final xPtr = ffi.calloc<ffi.Double>();
final yPtr = ffi.calloc<ffi.Double>();

try {
/// In real code, you would call the C function here:
/// final result = getCoords(xPtr, yPtr);
/// For demonstration, set values manually
xPtr.value = 10.5;
yPtr.value = 20.3;

print('Example41 Output parameters:');
print(' x = ${xPtr.value}, y = ${yPtr.value}');
print(' Pattern: allocate pointer, pass to C, read .value after call');
} finally {
/// Always free in finally block
ffi.calloc.free(xPtr);
ffi.calloc.free(yPtr);
}
}

/// Example 42: Error handling pattern with C return codes
class FFIError implements Exception {
final int code;
final String message;
FFIError(this.code, this.message);

@override
String toString() => 'FFIError($code): $message';
}

example42() {
/// Common pattern: C function returns int status code
/// 0 = success, negative = error
int mockCFunction() => -1; /// Simulate error

void checkResult(int result) {
if (result < 0) {
final messages = {
-1: 'Not found',
-2: 'Permission denied',
-3: 'Out of memory',
-4: 'Invalid argument',
};
throw FFIError(result, messages[result] ?? 'Unknown error');
}
}

try {
final result = mockCFunction();
checkResult(result);
} catch (e) {
print('Example42 Error handling: $e');
}

print('Example42 Pattern: Keep error codes in FFI layer,');
print(' convert to exceptions in wrapper classes');
}

/// Example 43: Finalizers for automatic resource cleanup
/// Finalizers call cleanup functions when Dart objects are garbage collected
example43() {
print('Example43 Finalizers (advanced pattern):');
print(' - Attach finalizer to Dart object wrapping C resource');
print(' - Finalizer calls C cleanup when Dart object is GC\'d');
print(' - Still provide explicit dispose() for deterministic cleanup');
print(' - Example: finalizer = Finalizer((token) => _free(token));');
print(' - Usage: finalizer.attach(this, pointer, detach: this);');
print(' - Best practice: Use both finalizer AND dispose pattern');
}

/// Example 44: Platform-specific library loading
example44() {
/// Different approaches to loading libraries
print('Example44 Library loading strategies:');

/// 1. DynamicLibrary.open() - load from file path
print(' 1. DynamicLibrary.open("path/to/lib.so")');
print(' - Explicit path to library file');
print(' - Platform-specific extensions (.so, .dylib, .dll)');

/// 2. DynamicLibrary.process() - symbols from current process
print(' 2. DynamicLibrary.process()');
print(' - Access symbols already loaded in process');
print(' - Useful for system libraries (libc, etc)');

/// 3. DynamicLibrary.executable() - symbols from executable
print(' 3. DynamicLibrary.executable()');
print(' - Access symbols from the executable itself');
print(' - Less common for typical FFI use cases');

/// Platform detection example
final libPath = Platform.isWindows
? 'mylib.dll'
: Platform.isMacOS
? 'libmylib.dylib'
: 'libmylib.so';
print(' Current platform would use: $libPath');
}

/// Example 45: Practical FFI wrapper class pattern
class NativeResource {
final ffi.Pointer<Database> _handle;
bool _disposed = false;

NativeResource(this._handle);

/// Check if resource is still valid before use
void _checkDisposed() {
if (_disposed) {
throw StateError('Cannot use disposed resource');
}
}

/// Example operation using the resource
void doSomething() {
_checkDisposed();
/// Use _handle to call C functions
print('Using native resource');
}

/// Explicit cleanup
void dispose() {
if (!_disposed) {
/// Call C cleanup function: native_free(_handle);
_disposed = true;
print('Resource disposed');
}
}
}

example45() {
print('Example45 FFI wrapper pattern:');
print(' - Wrap raw pointers in Dart classes');
print(' - Validate state before operations');
print(' - Provide explicit dispose() method');
print(' - Use bool flag to prevent double-free');
print(' - Makes FFI code safer and more idiomatic');
}

/// Programs have only one entry point in the main function.
/// Nothing is expected to be executed on the outer scope before a program
/// starts running with what's in its main function.
Expand All @@ -709,8 +1023,9 @@ main() {
example11, example12, example13, example14, example15,
example16, example17, example18, example19, example20,
example21, example22, example23, example24, example25,
example26, example27, example28, example29,
example30 // Adding this comment stops the dart formatter from putting all items on a new line
example26, example27, example28, example29, example30,
example31, example32, example33, example35,
example36, example38 // Adding this comment stops the dart formatter from putting all items on a new line
].forEach((ef) => ef());
}
```
Expand All @@ -719,5 +1034,8 @@ main() {

Dart has a comprehensive web-site. It covers API reference, tutorials, articles and more, including a
useful DartPad (a cloud-based Dart coding playground).
[https://dart.dev/](https://dart.dev)
[https://dartpad.dev/](https://dartpad.dev)
* [https://dart.dev/](https://dart.dev)
* [https://dartpad.dev/](https://dartpad.dev)
* [Dart FFI Documentation](https://dart.dev/guides/libraries/c-interop)
* [dart:ffi API Reference](https://api.dart.dev/stable/dart-ffi/dart-ffi-library.html)
* [FFI package with utilities](https://pub.dev/packages/ffi)