Skip to content

Instantly share code, notes, and snippets.

@Verdagon
Created April 7, 2024 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Verdagon/a0dab844e134bffb3189a29f11ced3e5 to your computer and use it in GitHub Desktop.
Save Verdagon/a0dab844e134bffb3189a29f11ced3e5 to your computer and use it in GitHub Desktop.

For example, if we have normal rust function:

pub fn kork(zork: u64) -> bool

We'd generate this Rust wrapper library code:

#[no_mangle]
pub extern "C" fn kork_extern(zork: u64) -> bool {
    inner_lib::kork(zork)
}

When structs and enums get involved, a twist: C should only see sized, opaque structs. Let's assume that C can know the size of Rust structs via preprocessor #define's handed in via command line.

Example:

struct MyRustStruct {
  // Arbitrary fields here
}
pub fn bork(splork_raw: MyRustStruct) -> MyRustStruct

Rust wrapper:

#[repr(C)]
pub struct MyRustStruct_extern([u8; std::mem::size_of::<MyStruct>()]);

#[no_mangle]
pub extern "C" fn bork_extern(
    splork_raw: *const MyRustStruct_extern,
    result_raw: *mut MyRustStruct_extern) {
  let splork_ref: &MyRustStruct = &*(splork_raw as *const MyRustStruct);
  let result_struct = bork(*splork_ref);
  (result_raw as *mut MyRustStruct).write(result_struct);
}

C header:

#ifndef RUST_WRAPPER_H
#define RUST_WRAPPER_H
#pragma pack(1)

#include <stdint.h>

// MyRustStruct_extern_SIZE must be handed in via command line
#ifndef MyRustStruct_extern_SIZE
    #error "MyRustStruct_extern_SIZE must be defined"
#endif

typedef struct {
    uint8_t data[MyRustStruct_extern_SIZE];
} MyRustStruct_extern;

void bork_extern(
    MyRustStruct_extern* splork_raw,
    MyRustStruct_extern* result_raw);

#endif /* RUST_WRAPPER_H */

Another example:

pub fn bloop(path: &Path) -> Vec<u8>

Rust wrapper code:

#[repr(C)]
pub struct Path_extern([u8; std::mem::size_of::<Path>()]);
#[repr(C)]
pub struct VecU8_extern([u8; std::mem::size_of::<Vec<u8>>()]);

#[no_mangle]
pub extern "C" fn bloop_extern(
    path_raw: *const Path_extern,
    result_raw: *mut VecU8_extern) {
  let path_ref: &Path = &*(path_raw as *const Path);
  let result_struct = bloop(*path_ref);
  let result_ptr: *mut VecU8_extern = result_raw as *mut VecU8_extern;
  result_ptr.write(result_struct);
}

C header:

#ifndef RUST_WRAPPER_H
#define RUST_WRAPPER_H
#pragma pack(1)

#include <stdint.h>

// Path_extern_SIZE must be handed in via command line
#ifndef Path_extern_SIZE
    #error "Path_extern_SIZE must be defined"
#endif
typedef struct {
    uint8_t data[Path_extern_SIZE];
} Path_extern;

// VecU8_extern_SIZE must be handed in via command line
#ifndef VecU8_extern_SIZE
    #error "VecU8_extern_SIZE must be defined"
#endif
typedef struct {
    uint8_t data[VecU8_extern_SIZE];
} VecU8_extern;

void bloop_extern(
    Path_extern* splork_raw,
    VecU8_extern* result_raw);

#endif /* RUST_WRAPPER_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment