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.