The polyglot bindings generator for your library (C#, C, Python, …) 🐙

Overview

Latest Version docs MIT Rust Rust

Interoptopus 🐙

The polyglot bindings generator for your library.

Interoptopus allows you to deliver high-quality system libraries to your users, and enables your users to easily consume those libraries from the language of their choice:

  • Design a single .dll / .so in Rust, consume it from any language.
  • Use patterns (e.g., classes, strings) in languages that have them.
  • Always be fully C compatible.
  • Painless workflow, no external tools required.

We strive to make our generated bindings zero cost. They should be as idiomatic as you could have reasonably written them yourself, but never magic or hiding the interface you actually wanted to expose.

Code you write ...

use interoptopus::{ffi_function, ffi_type, inventory};

#[ffi_type]
#[repr(C)]
pub struct Vec2 {
    pub x: f32,
    pub y: f32,
}

#[ffi_function]
#[no_mangle]
pub extern "C" fn my_function(input: Vec2) {
    println!("{}", input.x);
}

// This defines our FFI interface as `ffi_inventory` containing
// no constants, a single function `my_function`, no additional
// types (types are usually inferred) and no codegen patterns.
inventory!(ffi_inventory, [], [my_function], [], []);

... Interoptopus generates

Language Crate Sample Output1
C# interoptopus_backend_csharp Interop.cs
C interoptopus_backend_c my_header.h
Python interoptopus_backend_cpython reference.py
Other Write your own backend2 -

1 For the reference project.
2 Create your own backend in just a few hours. No pull request needed. Pinkie promise.

Getting Started 🍼

If you want to ...

Supported Rust Constructs

See the reference project for an overview:

  • functions (extern "C" functions and delegates)
  • types (composites, enums, opaques, references, ...)
  • constants (primitive constants; results of const evaluation)
  • patterns (ASCII pointers, options, slices, classes, ...)

Performance 🏁

Generated low-level bindings are zero cost w.r.t. hand-crafted bindings for that language.

That said, even hand-crafted bindings encounter some target-specific overhead at the FFI boundary (e.g., marshalling or pinning in managed languages). For C# that cost is often nanoseconds, for Python CFFI it can be microseconds.

While ultimately there is nothing you can do about a language's FFI performance, being aware of call costs can help you design better APIs.

Detailed call cost tables can be found here: 🔥

For a quick overview, this table lists the most common call types in ns / call:

Construct C# Python
primitive_void() 7 272
primitive_u32(0) 8 392
many_args_5(0, 0, 0, 0, 0) 10 786
callback(x => x, 0) 43 1168

Feature Flags

Gated behind feature flags, these enable:

  • derive - Proc macros such as ffi_type, ...
  • serde - Serde attributes on internal types.
  • log - Invoke log on FFI errors.

Changelog

  • v0.13 - Python backend uses ctypes now.
  • v0.12 - Better compat using #[ffi_service_method].
  • v0.11 - C# switch ctors to static methods.
  • v0.10 - C# flavors DotNet and Unity (incl. Burst).
  • v0.9 - 150x faster C# slices, Python type hints.
  • v0.8 - Moved testing functions to respective backends.
  • v0.7 - Make patterns proc macros for better FFI docs.
  • v0.6 - Renamed and clarified many patterns.
  • v0.5 - More ergonomic slice usage in Rust and FFI.
  • v0.4 - Enable logging support in auto-generated FFI calls.
  • v0.3 - Better compatibility with generics.
  • v0.2 - Introduced "patterns"; working interop for C#.
  • v0.1 - First version.

Also see our upgrade instructions.

FAQ

Contributing

PRs are welcome.

  • Submit small bug fixes directly. Major changes should be issues first.
  • Anything that makes previously working bindings change behavior or stop compiling is a major change;
  • This doesn't mean we're opposed to breaking stuff just that we'd like to talk about it before it happens.
  • New features or patterns must be materialized in the reference project and accompanied by an interop test (i.e., a backend test running C# / Python against a DLL invoking that code) in at least one included backend.
Comments
  • C# passing Refs to Slices

    C# passing Refs to Slices

    In order to use extern functions in burst, structs have to be passed by reference. If I try changing all my slices to be &mut slices, the extern functions are generated nicely with out SliceMut but I lose the other functions that are normally generated with them. (The ones with the pinning of arrays and conversion to SliceMut. If I want those helper functions for both Slice and &mut Slice does this mean that I need to add a pattern for handling the &mut version of a Slice the same way that the Slice is handled to the C# generator? As an aside I would also like the ability to generate slices from native arrays. I see that you've already got some Unity specific flagging, would you be interested in PRs to add some more Unity specific interoperability to the generated c# behind those flags?

    c-backend-c# enhancement 
    opened by dbenson24 10
  • Returning String from rs to cs via service pattern

    Returning String from rs to cs via service pattern

    Thank you for this awesome library!

    It was really easy to get started with the provided examples, but I'm having hard time to return String (AsciiPointer) more than once from Rust to CS via the Service pattern.

    With the following snippet I managed to return the string once, but the second call is causing the program to crash:

    use crate::result::{Error, FFIError};
    use interoptopus::{ffi_service, ffi_service_ctor, ffi_service_method, ffi_type};
    use interoptopus::patterns::string::AsciiPointer;
    use std::ffi::CString;
    
    #[ffi_type(opaque)]
    #[derive(Default)]
    pub struct Sample {
        pub number: usize,
        name: CString,
    }
    
    
    #[ffi_service(error = "FFIError", prefix = "sample_")]
    impl Sample {
        #[ffi_service_ctor]
        pub fn new_with(number: u32) -> Result<Self, Error> {
            Ok(Self {
                number: number as usize,
                name: CString::new(format!("HELLO_{}", number)).unwrap(),
            })
        }
    
        #[ffi_service_method(direct)]
        #[allow(non_snake_case)]
        pub fn Name(&self) -> AsciiPointer {
            // FIXME: Name can only be called once. second invocation causes crash.
            AsciiPointer::from_cstr(&self.name)
        }
    }
    
    interoptopus::inventory!(
        my_inventory, 
        [], 
        [], 
        [], 
        [Sample]);
    

    My theory is that the Sample.CString gets freed after AsciiPointer::from_cstr is returned. I tried to solve this by introducing new lifetime for name, but didn't succeed: always ended up having obscure error coming from the ffi_service macro.

    Any suggestion / help would be greatly appreciated!

    bug c-backend-c# 
    opened by wgershwitz 8
  • SliceMutu32 does not contain a constructor that takes 1 arguments

    SliceMutu32 does not contain a constructor that takes 1 arguments

    I am using Interoptopus to generate C# bindings for Unity. This is my Generator:

        Generator::new(config, rust_lib::my_inventory())
            //.add_overload_writer(DotNet::new())
            .add_overload_writer(Unity::new())
            .write_file("bindings/csharp/Interop.cs")?;
    

    I am trying to pass a mutable slice from C# to Rust and write into it like so:

    #[ffi_function]
    #[no_mangle]
    pub extern "C" fn mutate_slice_u32(slice: &mut FFISliceMut<u32>) {
        let data = vec![1, 2, 3, 4, 5];
        for (idx, el) in data.iter().enumerate() {
            slice[idx] = *el;
        }
    }
    

    The relevant generated binding looks like this:

            [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "mutate_slice_u32")]
            public static extern void mutate_slice_u32(ref SliceMutu32 slice);
    
            #if UNITY_2018_1_OR_NEWER
            public static void mutate_slice_u32(NativeArray<uint> slice)
            {
                var slice_slice = new SliceMutu32(slice);
                mutate_slice_u32(ref slice_slice);;
            }
            #endif
    

    Issue is I am getting an error in Unity saying: Assets/Plugins/Interop.cs(53,35): error CS1729: 'SliceMutu32' does not contain a constructor that takes 1 arguments

    Am I doing anything especially crazy here?

    question c-backend-c# 
    opened by slasktotten 6
  • Added filter function to inventory

    Added filter function to inventory

    Added a function to remove items from an inventory which are present in any of a collection of other inventories, and return the result as a new inventory. I'm using this in a project where I want to generate multiple bindings files which expose different levels of access (private functions and public SDK functions). As the private consuming project needs to incorporate both bindings, I need to remove duplicate symbols.

    opened by pixsperdavid 6
  • Add span properties to slices in C#

    Add span properties to slices in C#

    I feel like there's no good way to access all of (instead of indexer (which copies data anyway)) the slice data in c# right now without copying it.

    I propose adding a method to the c# generated version of FFISlice and FFISliceMut that has a ReadOnlySpan and Span respectively.

    For example:

    ///A pointer to an array of data someone else owns which may be modified.
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public partial struct SliceMutf32
    {
        ///Pointer to start of mutable data.
        IntPtr data;
        ///Number of elements.
        ulong len;
    }
    
    public partial struct SliceMutf32
    {
        public Span<float> AsSpan()
        {
            unsafe
            {
                return new Span<float>((float*)data, (int)len);
            }
        }
    }
    

    Not sure how you'd do it without unsafe though.

    Should probably also make it optional, since Span is part of System.Memory which isn't included in the standard framework on all .net versions.

    c-backend-c# enhancement 
    opened by Skippeh 6
  • Prevent emitting type declaration

    Prevent emitting type declaration

    I've been looking around for this option and I don't think it exists. I'm basically looking to use types in function signatures like Vec3 or Mat4 which I already have full implementations of on the target language. I want the named types emitted in function signatures, but I don't want them declared on their own. I'm happy to take a look at implementing this, I think it would make the most sense as an attribute on ffi-type

    c-core needs-discussion enhancement 
    opened by dbenson24 5
  • Added ability to customise naming style for C backend

    Added ability to customise naming style for C backend

    Added ability to customise the config to select the naming style used for C types, enum variants, constants and function arguments.

    I've added a dependency to the "heck" crate to perform the case conversion. If there is an objection to this I can implement the conversion methods natively in the library.

    opened by pixsperdavid 4
  • Allow customisation of C# access modifiers in generated output

    Allow customisation of C# access modifiers in generated output

    Would be useful to have an option in C# code generation config to customize access modifiers on generated classes/structs. In my usage of interoptopus, I'm creating a friendlier C# wrapper around a native DLL so would prefer not to expose the generated classes in the wrapper library interface. Currently I'm doing a regex find and replace on the file after generation to change the accessors to 'internal'.

    c-backend-c# needs-discussion 
    opened by pixsperdavid 4
  • Added FFICChar pattern to allow better string representation in C

    Added FFICChar pattern to allow better string representation in C

    Fix for #41. I initially tried to implement as a primitive, however unfortunately because c_char is just an alias for i8 in rust, this wasn't possible with the current way interoptopus implements primitives as by the time the type info gets to the backend, it appears as an i8.

    My approach is to create a new pattern, FFICChar which explicitly represents a char in C. This is allows the backends to then handle this correctly. I've also added special handling for the Read/ReadWrite pointers to this type for C# because it's far more useful to express this as an IntPtr rather than a ref to an sbyte.

    opened by pixsperdavid 2
  • FFISlice points to garbage data if called from a simple function, as opposed to a service

    FFISlice points to garbage data if called from a simple function, as opposed to a service

    Not sure if i'm doing something wrong, but i'm fairly sure i'm not...

    If you have an FFISlice parameter in a function it seems to point to garbage data or something on the rust side. But moving the same function to a service makes it work like expected.

    I made a project as an example:

    https://github.com/Skippeh/interoptopus_slice_issue

    Outputs: image

    It seems to behave the same on 0.13.15. Haven't tried earlier versions.

    I have only tried C# but i think the problem is in the rust code.

    c-backend-c# 
    opened by Skippeh 2
  • Nested AsciiPointer is not PInvoke compatible.

    Nested AsciiPointer is not PInvoke compatible.

    Hi, first of all thanks for the great library! I am able to return a AsciiPointer from a method of my service, but when I try to return a AsciiPointer nested in a struct I get a runtime error:

    System.Runtime.InteropServices.MarshalDirectiveException: Method's type signature is not PInvoke compatible.
       at My.Company.Interop.counter_service_nested_string(IntPtr context)
       at My.Company.CounterService.NestedString() in C:\Users\nico\tmp\lib_test\bindgen\generated\Interop.cs:line 288
       at ConsoleApplication42.Program.Main(String[] args) in C:\Users\nico\source\tmp\ConsoleApplication42\ConsoleApplication42\Program.cs:line 51
    

    edit: example is here: https://github.com/njust/interoptopus_test/blob/master/lib_test/src/lib.rs

    bug c-backend-c# wontfix 
    opened by njust 2
  • Add FFIPointer to `interoptopus::patterns::ptr`

    Add FFIPointer to `interoptopus::patterns::ptr`

    Somewhat experimental idea, needs validation, also see #68:

    Names tbd:

    • A generic FFIPointer<T, SingleElementOrMultiple, RustRW, FFIRW> indicating how that pointer is being used (type T of target, single or multiple elements, and who reads and writes; bonus points if we can make T so that it also accepts and enum of type alternatives although I wouldn't know how to do that)
    • various aliases to common FFIPointer<A, B, C, D> combinations.
    • The generic FFIPointer would fallback to *T or *c_void in backends for now or could be used for other optimizations (e.g., deciding if an #[In, out] should be applied.
    • bonus-bonus points if MultipleElements can also encode variable or constant many elements.

    This pointer could also subsume Callbacks with the help of const generics, e.g., Callback<Name="Bla"> once &str are available.

    Main questions to resolve are:

    • Can this be implemented?
    • Will this make writing APIs more ergonomic (e.g., better C# bindings w.r.t IntPtr, #[In/Out], ...)?
    • Will this be cleaner or more messy than having multiple independent types (e.g., w.r.t. our maintenance)?
    c-core needs-discussion enhancement 
    opened by ralfbiedert 0
  • Added ArrayPointer pattern

    Added ArrayPointer pattern

    Added an ArrayPointer pattern. This is a lighter-weight alternative to the FFISlice pattern which allows expression of a pointer to an array of a type. This allows backends to generate more relevant bindings. Crucially, it forces the C# backend to declare the parameter as an IntPtr rather than a ref T (which makes little sense for an array of values).

    c-backend-c# needs-discussion enhancement 
    opened by pixsperdavid 8
  • Desired QoL changes for 0.15

    Desired QoL changes for 0.15

    The following changes I'd like to land in 0.15, help wanted

    • [ ] Infer prefix in #[ffi_service] and make it optional
    • [ ] Add sample how to use feature flags to disable Interoptopus unless generating bindings
    • [ ] Improve the surrogate type situation, e.g., by instead relying on transmutable surrogates. Needs some thought.
    • [ ] Maybe struct methods #29 (depends on how much work they are)
    • [ ] Maybe implement #27, or rule out why they are a bad idea
    • [ ] Make sure void service methods don't need annotations
    good-first-issue enhancement 
    opened by ralfbiedert 0
  • AsciiPointer is UTF8Pointer?

    AsciiPointer is UTF8Pointer?

    To my pleasant surprise, I discovered that the AsciiPointer pattern plainly uses Rust CString, which is UTF-8 instead of ASCII. This is not an issue when transferring strings into Rust, as ASCII is a subset of UTF-8, so Rust will happily handle both. However, it might surprise users when accepting strings from Rust, when the Rust code accidentally outputs unicode characters (there's currently no check on this).

    There seem to be two solutions to me: either perform a check in the Rust-implementation of AsciiPointer to make sure the string actually is ASCII, or, for me preferable, just support the unicode. For C both are null-terminated byte-strings anyway, so it just needs a clear warning sign for developers. For C# I've made a few small changes to the backend to explicitly marshal strings as UTF-8 which seems to work quite nicely (see https://github.com/seaeagle1/interoptopus/tree/utf8 ). I don't know much Python, but I'm sure it's also able to handle unicode strings.

    c-core c-backend-c# c-backend-cpython enhancement 
    opened by seaeagle1 3
  • C# backend generates unoptimized enumerator pattern

    C# backend generates unoptimized enumerator pattern

    If you look at the generated IEnumerable code, it generates a plain yield return enumerator. That means there's an allocation on every GetEnumerator call - which is normally avoided by providing a concrete StructEnumerator GetEnumerator() overload. The code seems to do a lot of redundant work (exception is checked on the indexer hot path, so indexer will never be inlined).

    Would it make a bit more sense to return a ReadOnlySpan<T> view over the data? That would also give a bit of flexibility to the caller at no performance expense. That would also remove the inheritance information from the struct,s.

    c-backend-c# enhancement 
    opened by Therzok 3
  • Support for dataful enums?

    Support for dataful enums?

    Dataful enums are a powerful feature of rust's type system, and unfortunately this means they are difficult to represent elegantly in many other languages. However, at minimum dataful enums in rust can be represented as a tagged union in C, and in languages with structural inheritance, this could be translated to a class or record type hierarchy. Adding support for dataful enums, even rudimentary support only mapping them to tagged unions in the target languages, would be a very valuable feature for rust interoperability.

    As an example of a simple dataful enum on rust side:

    #[repr(u32,C)]
    pub enum Value {
        VByte(u8),
        VFloat(f32),
    }
    

    And its equivalent on the C# side, including the facility to perform basic matching on the variant:

    public struct Value
    {
        private enum Tag
        {
            VByte,
            VFloat,
        }
    
        [FieldOffset(0)] private Tag tag;
        [FieldOffset(4)] private Byte byte_data;
        [FieldOffset(4)] private Single float_data;
    
        public void Match(Action<byte> byte_f, Action<Single> float_f)
        {
            Match<object>(b =>
            {
                byte_f(b); return null;
            },
            f =>
            {
                float_f(f); return null;
            });
        }
        public R Match<R>(Func<byte, R> byte_f, Func<Single, R> float_f)
        {
            switch (tag)
            {
                case Tag.VByte:
                    return byte_f(byte_data);
                case Tag.VFloat:
                    return float_f(float_data);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
    

    Optionally, this tagged union could be converted to a class hierarchy, if this is deemed valuable:

    public abstract class ValueClass
    {
        public static ValueClass From(Value v)
        {
            return v.Match<ValueClass>(
                b => new ByteValue(b),
                f => new FloatValue(f)
            );
        }
        public class ByteValue : ValueClass
        {
            public Byte b;
    
            public ByteValue(byte b)
            {
                this.b = b;
            }
        }
        public class FloatValue : ValueClass
        {
            public Single f;
    
            public FloatValue(float f)
            {
                this.f = f;
            }
        }
    }
    
    c-core c-proc needs-discussion enhancement 
    opened by Zoybean 3
Owner
Ralf Biedert
Principal Engineer. Working on VR, AR, eye tracking, mostly in Rust.
Ralf Biedert
Distribute a wasm SPA as HTML by wrapping it as a polyglot "html+wasm+zip"

A packer that adds a webpage to WASM module, making it self-hosted! Motivation At the moment, Browsers can not execute WebAssembly as a native single

Andreas Molzer 3 Jan 2, 2023
Create, open, manage your Python projects with ease, a project aimed to make python development experience a little better

Create, open, manage your Python projects with ease, a project aimed to make python development experience a little better

Dhravya Shah 7 Nov 18, 2022
Very experimental Python bindings for the Rust biscuit-auth library

Overview This is a very experimental take on Python bindings for the biscuit_auth Rust library. It is very much a work in progress (limited testing, m

Josh Wright 5 Sep 14, 2022
Python bindings for heck, the Rust case conversion library

pyheck PyHeck is a case conversion library (for converting strings to snake_case, camelCase etc). It is a thin wrapper around the Rust library heck. R

Kevin Heavey 35 Nov 7, 2022
Create a Python project automatically with rust (like create-react-app but for python)

create-python-project Create a Python project automatically with rust (like create-react-app but for python) Installation cargo install create-python-

Dhravya Shah 2 Mar 12, 2022
Rust <-> Python bindings

rust-cpython Rust bindings for the python interpreter. Documentation Cargo package: cpython Copyright (c) 2015-2020 Daniel Grunwald. Rust-cpython is l

Daniel Grunwald 1.7k Dec 29, 2022
Rust bindings for the Python interpreter

PyO3 Rust bindings for Python. This includes running and interacting with Python code from a Rust binary, as well as writing native Python modules. Us

PyO3 7.2k Jan 4, 2023
lavalink-rs bindings for Python

lavasnek_rs Dev Docs: Main Site | Fallback: GitHub Pages GitHub repo GitLab repo Using the library The library is available on PyPi, and you can insta

Victoria Casasampere Fernandez 39 Dec 27, 2022
Implementation of Monte Carlo PI approximation algorithm in Rust Python bindings

rusty_pi Implementation of Monte Carlo PI approximation algorithm in Rust Python bindings. Time of 100M iterations approximation on Core i7 10th gen:

Aleksey Popov 1 Jul 6, 2022
Pyo3 - Rust bindings for the Python interpreter

PyO3 Rust bindings for Python, including tools for creating native Python extension modules. Running and interacting with Python code from a Rust bina

PyO3 7.2k Jan 2, 2023
Python bindings for akinator-rs using pyo3

Akinator-py python bindings for akinator-rs using pyo3 Installation Prebuilt wheels are uploaded onto pypi, if you platform is supported, you can inst

Tom-the-Bomb 4 Nov 17, 2022
Standalone python3.dll import library generator

Standalone python3.dll import library generator Generates import libraries for the Stable ABI Python DLL for MinGW-w64 and MSVC (cross-)compile target

PyO3 7 Oct 9, 2022
Build a python wheel from a dynamic library

build_wheel Small utility to create a Python wheel given a pre-built dynamic library (.so, .dylib, .dll). If you are just trying to produce a wheel fr

Tangram 1 Dec 2, 2021
A simple library to allow for easy use of python from rust.

Rustpy A simple library to allow for easy use of python from rust. Status Currently this library has not received much love (pull requests welcome for

Luke 74 Jun 20, 2022
Robust and Fast tokenizations alignment library for Rust and Python

Robust and Fast tokenizations alignment library for Rust and Python Demo: demo Rust document: docs.rs Blog post: How to calculate the alignment betwee

Explosion 157 Dec 28, 2022
Arrowdantic is a small Python library backed by a mature Rust implementation of Apache Arrow

Welcome to arrowdantic Arrowdantic is a small Python library backed by a mature Rust implementation of Apache Arrow that can interoperate with Parquet

Jorge Leitao 52 Dec 21, 2022
High-level memory-safe binding generator for Flutter/Dart <-> Rust

flutter_rust_bridge: High-level memory-safe binding generator for Flutter/Dart <-> Rust Want to combine the best between Flutter, a cross-platform hot

fzyzcjy 2.1k Dec 31, 2022
Node.js bindings to the ripgrep library, for fast file searching in JavaScript without child processes!

ripgrepjs ripgrepjs: Node.js bindings to the ripgrep library, for direct integration with JS programs without spawning an extra subprocess! This proje

Annika 1 May 10, 2022
Rust bindings for Supabase JavaScript library via WebAssembly.

supabase-js-rs Rust bindings for Supabase JavaScript library via WebAssembly. Usage Add supabase-js-rs to Cargo.toml supabase-js-rs = { version = "0.1

Valery Stepanov 8 Jan 13, 2023