Skip to content

ayersdecker/ferrum

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Ferrum

NuGet NuGet Build Status License: MIT

C/C++ to C# β€” from UI to the bare metal, get it done.

Ferrum is a reusable native-compute framework for .NET MAUI. It provides the scaffolding that lets any MAUI app call into a C/C++ static library with:

  • Zero-copy buffers β€” NativeBuffer<T> pins a Span<T>/Memory<T> region so you can pass a pointer directly to native code without copying.
  • NativeAOT-compatible P/Invoke β€” every binding uses [LibraryImport] (source-generated), not [DllImport]. iOS NativeAOT is a first-class target.
  • Code-generation tool β€” ferrum-codegen parses a C header and emits ready-to-use [LibraryImport] bindings. It fails loudly (never silently) on constructs it cannot safely bind.
  • Cross-platform CMake templates β€” build an XCFramework for iOS or .so per ABI for Android from any C/C++17 source tree.

Ferrum is domain-agnostic β€” it does not know or care what your native core does. Audio, ML inference, signal processing, computer vision β€” all equally welcome.


Repository Layout

/native/                CMake project: iOS/Android toolchain files, test fixtures
/tools/codegen/         ferrum-codegen β€” header-to-[LibraryImport] generator
/src/Framework/         Ferrum.Framework C# library (NativeBuffer<T>, etc.)
/src/Framework.Tests/   xUnit tests for the framework and codegen
/samples/MinimalDemo/   End-to-end MAUI sample calling native test functions
/templates/maui-ferrum/ dotnet new template for scaffolding new Ferrum apps
/docs/                  Architecture notes, getting-started guide, open questions
.github/workflows/      CI: native build (iOS/Android), codegen tests, MAUI build

Quick Start

Option 1: Use the Project Template (Fastest Way to Start)

Create a new Ferrum-enabled MAUI app in seconds:

# Install the template (from local repo)
dotnet new install templates/maui-ferrum

# Create a new project
dotnet new maui-ferrum -n MyApp
cd MyApp

# See README.md in the generated project for next steps

The template creates a complete MAUI project with Ferrum.Framework already configured and example NativeBuffer<T> usage.

Option 2: Use the Sample (Recommended for First-Time Users)

Clone this repository and run the MinimalDemo sample:

git clone https://github.com/ayersdecker/ferrum.git
cd ferrum

# Build native test fixtures for your target platform
# iOS (requires macOS + Xcode):
./native/scripts/build_ios.sh

# Android (requires NDK β€” set ANDROID_NDK_HOME):
export ANDROID_NDK_HOME=/path/to/ndk
./native/scripts/build_android.sh

# Open and run the sample
cd samples/MinimalDemo
dotnet build -f net9.0-android  # or net9.0-ios

See samples/MinimalDemo/README.md for details.

Option 3: Add to Your Existing MAUI App

1. Install the NuGet package

dotnet add package Ferrum.Framework
dotnet tool install --global Ferrum.Codegen

2. Build your native library

Create your C/C++ library with a plain-C API header:

// mylib/include/mylib.h
#ifdef __cplusplus
extern "C" {
#endif

void process_samples(float* data, int count);

#ifdef __cplusplus
}
#endif

Build for iOS and Android using the provided CMake scripts (see docs/getting-started.md).

3. Generate C# bindings

ferrum-codegen \
  --input  mylib/include/mylib.h \
  --output Interop/MylibBindings.cs \
  --ns     MyApp.Interop \
  --lib    __Internal

4. Call native code with zero-copy buffers

using Ferrum.Framework.Buffers;
using MyApp.Interop;

using var buffer = new NativeBuffer<float>(1024);
for (int i = 0; i < buffer.Length; i++)
    buffer.Span[i] = (float)i * 0.5f;

unsafe 
{ 
    MylibBindings.process_samples(buffer.TypedPointer, buffer.Length); 
}

// Read results back from buffer.Span

πŸ“– Full Guide: docs/getting-started.md
πŸ—οΈ Architecture: docs/architecture.md


Design Constraints

Constraint Detail
No domain logic This repo is framework-only. Application-specific code is scope creep.
NativeAOT on iOS No runtime codegen, no reflection-based marshalling.
Loud failures The codegen tool exits non-zero rather than emitting an incorrect binding.
Blittable only Only types with a 1:1 memory layout between C and C# are supported.

Troubleshooting

iOS: dllimport not found or __Internal issues

Problem: The app crashes with DllNotFoundException when calling native code on iOS.

Solution: Ensure your native library is built as an XCFramework and properly referenced:

./native/scripts/build_ios.sh

Then add to your .csproj:

<ItemGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == 'iOS'">
  <NativeReference Include="path/to/libmylib.xcframework">
    <Kind>Framework</Kind>
    <SmartLink>true</SmartLink>
  </NativeReference>
</ItemGroup>

Use --lib __Internal in ferrum-codegen for iOS.

Android: Native library not loaded

Problem: DllNotFoundException: libmylib on Android.

Solution: Build for all required ABIs and include them in your project:

export ANDROID_NDK_HOME=/path/to/ndk
./native/scripts/build_android.sh

Add to .csproj:

<ItemGroup Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == 'Android'">
  <AndroidNativeLibrary Include="artifacts/android/jniLibs/arm64-v8a/libmylib.so" />
  <AndroidNativeLibrary Include="artifacts/android/jniLibs/armeabi-v7a/libmylib.so" />
  <AndroidNativeLibrary Include="artifacts/android/jniLibs/x86_64/libmylib.so" />
</ItemGroup>

Use --lib libmylib (without .so) in ferrum-codegen for Android.

Codegen fails with "Unsupported type"

Problem: ferrum-codegen exits with an error about unsupported types.

Solution: Check that your header only uses blittable types:

  • βœ… int32_t, int64_t, float, double, pointers to structs
  • ❌ char*, long, function pointers, unions

NativeBuffer AccessViolation

Problem: Crash when accessing NativeBuffer.Span after disposal.

Solution: Keep the NativeBuffer alive while native code is using it:

using var buffer = new NativeBuffer<float>(1024);
unsafe { MyFunc(buffer.TypedPointer, buffer.Length); }
// Native call completes before 'using' disposes buffer

For more issues, see GitHub Issues or docs/getting-started.md.


Contributing

We welcome contributions! See CONTRIBUTING.md for:

  • How to build and test the framework
  • Code style guidelines
  • PR submission process

Roadmap

  • NuGet package publication (Ferrum.Framework and Ferrum.Codegen dotnet tool)
  • dotnet new project template for quick scaffolding (maui-ferrum)
  • Codegen parser decision (tokenizer-based, fails loudly on unsupported constructs)
  • Windows and macOS desktop MAUI support
  • Community samples (audio processing, ML inference, computer vision)

See docs/open-questions.md for detailed discussion on pending decisions.


License

MIT Β© 2026 Decker Ayers

About

C/C++ to C# - From UI to the bare metal, get it done. Using .NET MAUI, develop interfaces that can access most native sensors and raw compute of mobile devices

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors