Rust functions that accept and return tuples
C has no notion of tuples, but the closest analog is a plain struct. You will need to create individual structs for each unique combination of types. Here, we create a structure that represents two 32-bit unsigned integers.
use std::convert::From;
// A Rust function that accepts a tuple
fn flip_things_around_rust(tup: (u32, u32)) -> (u32, u32) {
let (a, b) = tup;
(b + 1, a - 1)
}
// A struct that can be passed between C and Rust
#[repr(C)]
pub struct Tuple {
x: u32,
y: u32,
}
// Conversion functions
impl From<(u32, u32)> for Tuple {
fn from(tup: (u32, u32)) -> Tuple {
Tuple { x: tup.0, y: tup.1 }
}
}
impl From<Tuple> for (u32, u32) {
fn from(tup: Tuple) -> (u32, u32) {
(tup.x, tup.y)
}
}
// The exported C method
#[no_mangle]
pub extern "C" fn flip_things_around(tup: Tuple) -> Tuple {
flip_things_around_rust(tup.into()).into()
}
#[repr(C)]
is used to inform the compiler that it should arrange the
fields of the struct as a C compiler would. The two conversion
implementations use std::convert::From
to provide ergonomic
conversion between the struct and a corresponding tuple.
C
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
typedef struct {
uint32_t x;
uint32_t y;
} tuple_t;
extern tuple_t
flip_things_around(tuple_t);
int main(void) {
tuple_t initial = { .x = 10, .y = 20 };
tuple_t result = flip_things_around(initial);
printf("(%" PRIu32 ",%" PRIu32 ")\n", result.x, result.y);
}
Since we are conforming to C-compatible idioms, the implementation is
straight-forward. We define a struct
with fields that match the
types and order of the Rust struct, then create an instance and call
the method.
Ruby
require 'ffi'
class Tuple < FFI::Struct
layout :x, :uint32,
:y, :uint32
def to_s
"(#{self[:x]},#{self[:y]})"
end
end
module Tuples
extend FFI::Library
ffi_lib 'tuples'
attach_function :flip_things_around, [Tuple.by_value], Tuple.by_value
end
tup = Tuple.new
tup[:x] = 10
tup[:y] = 20
puts Tuples.flip_things_around(tup)
To mirror the structure definition, we create a subclass of
FFI::Struct
and use layout
to specify the field names and types.
When attaching the function, we use by_value
to indicate that the
struct will be passed directly, without the need for indirection via
pointers.
Python
#!/usr/bin/env python3
import sys, ctypes
from ctypes import c_uint32, Structure
class Tuple(Structure):
_fields_ = [("x", c_uint32),
("y", c_uint32)]
def __str__(self):
return "({},{})".format(self.x, self.y)
prefix = {'win32': ''}.get(sys.platform, 'lib')
extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so')
lib = ctypes.cdll.LoadLibrary(prefix + "tuples" + extension)
lib.flip_things_around.argtypes = (Tuple, )
lib.flip_things_around.restype = Tuple
tup = Tuple(10, 20)
print(lib.flip_things_around(tup))
To mirror the structure definition, we create a subclass of
ctypes.Structure
and use _fields_
to specify the field names and
types.
Haskell
Unfortunately, Haskell does not currently support passing or returning arbitrary structs. Pointer indirection is always required.
Node.js
const ffi = require('ffi-napi');
const ref = require('ref-napi');
const struct = require('ref-struct-di')(ref);
const Tuple = struct({
x: 'uint32',
y: 'uint32',
});
const lib = ffi.Library('libtuples', {
flip_things_around: [Tuple, [Tuple]],
});
const tup = new Tuple({x: 10, y: 20});
const result = lib.flip_things_around(tup);
console.log('(%d,%d)', result.x, result.y);
The ref-struct-di
package allows us to build struct types
which can be passed to FFI functions.
C#
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
struct IntTuple {
public uint x;
public uint y;
public static implicit operator Tuple<uint, uint>(IntTuple t)
{
return Tuple.Create(t.x, t.y);
}
public static implicit operator IntTuple(Tuple<uint, uint> t)
{
return new IntTuple { x = t.Item1, y = t.Item2 };
}
};
class Tuples
{
[DllImport("tuples")]
private static extern IntTuple flip_things_around(IntTuple t);
public static Tuple<uint, uint> FlipThingsAround(Tuple<uint, uint> t)
{
return flip_things_around(t);
}
static public void Main()
{
var tuple = Tuple.Create(10u, 20u);
var newTuple = Tuples.FlipThingsAround(tuple);
Console.WriteLine($"({newTuple.Item1},{newTuple.Item2})");
}
}
To mirror the tuple structure definition, we create a struct
using
the StructLayout
property and define the layout as sequential. We
also provide some implicit conversion operators to make going between
types fairly seamless.
Julia
#!/usr/bin/env julia
using Libdl
libname = "tuples"
if !Sys.iswindows()
libname = "lib$(libname)"
end
lib = Libdl.dlopen(libname)
flipthingsaround_sym = Libdl.dlsym(lib, :flip_things_around)
struct Tuple
x::UInt32
y::UInt32
end
flipthingsaround(t:: Tuple) = ccall(
flipthingsaround_sym,
Tuple, (Tuple,),
t)
initial = Tuple(10, 20)
newtuple = flipthingsaround(initial)
println("($(newtuple.x),$(newtuple.y))")
Julia struct types defined with the exact same field layout are
already compatible with C’s data arrangement. Since all fields are isbits
, then so is the Tuple
type. As such, it
will store each member inline and will be passed to the native
function by value.