传递和返回整数

整数是 FFI 的 “你好世界!”,因为它们通常更容易越过柏林墙(边界)。让我们创建一个库,是关于 两个无符号的 32 位整数.

extern crate libc;
use libc::uint32_t;

#[no_mangle]
pub extern fn addition(a: uint32_t, b: uint32_t) -> uint32_t {
    a + b
}

#[allow(dead_code)]
pub extern fn fix_linking_when_not_using_stdlib() { panic!() }

cargo build编译它,这将产生一个库在 target/debug/。确切的文件名取决于您的平台:

平台 模式
Windows *.dll
OS X lib*.dylib
Linux lib*.so

C

#include <stdio.h>
#include <stdint.h>

extern uint32_t addition(uint32_t, uint32_t);

int main(void) {
  uint32_t sum = addition(1, 2);
  printf("%u\n", sum);
  return 0;
}

我们首先使用 正确的参数和返回类型,声明一个extern函数 。然后可以编译并链接到 Rust 库,通过使用gcc --std=c11 -o c-example src/main.c -L target/debug/-lintegers

如 基本部分 所述,这可以在 mac OS X 和 Linux 上 运行 使用 LD_LIBRARY_PATH=target/debug/ ./c-example,而在 Windows 上将target\debug\integers.dll复制到当前目录和 运行.\c-example

Ruby

require 'ffi'

module Integers
  extend FFI::Library
  ffi_lib 'integers'
  attach_function :addition, [:uint32, :uint32], :uint32
end

puts Integers.addition(1, 2)

这可以使用

LD_LIBRARY_PATH=target/debug/ ruby ./src/main.rb

Python

#!/usr/bin/env python3

import sys, ctypes
from ctypes import c_uint32

prefix = {'win32': ''}.get(sys.platform, 'lib')
extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so')
lib = ctypes.cdll.LoadLibrary(prefix + "integers" + extension)

lib.addition.argtypes = (c_uint32, c_uint32)
lib.addition.restype = c_uint32

print(lib.addition(1, 2))

如基本部分所述,这可以在 mac OS X 和 Linux 上运行 使用LD_LIBRARY_PATH=target/debug/python src/main.py,然后打开 Windows 通过将target\debug\integers.dll复制到当前 目录并运行py src\main.py.

Haskell

{-# LANGUAGE ForeignFunctionInterface #-}

import Data.Word (Word32)

foreign import ccall "addition"
  addition :: Word32 -> Word32 -> Word32

main :: IO ()
main = print (addition 1 2)

我们必须启用ForeignFunctionInterface语言扩展和 在包含一个foreign import声明之前,导入相关的低级类型 。这包括调用约定 (ccall),符号名称("addition"),相应的 Haskell 名字(addition),和函数的类型。这个函数是 纯的,所以我们不用在类型中包含IO,而一个显式不纯的函数,会想要返回一个IO值,用来表明它有副作用。

这可以使用ghc src/main.hs target/debug/libintegers.so -o haskell-example编译。

Node.js

const ffi = require('ffi');

const lib = ffi.Library('libintegers', {
  addition: ['uint32', ['uint32', 'uint32']],
});

console.log(lib.addition(1, 2));

Library函数指定要链接的动态库的名称, 以及导出函数的签名列表(以 function_name:[return_type,[argument_types]]的形式)。然后是这些函数方法会在,Library返回对象中。

这可以使用LD_LIBRARY_PATH=target/debug node src/main.js 运行.

C\

using System;
using System.Runtime.InteropServices;

class Integers
{
    [DllImport("integers", EntryPoint="addition")]
    public static extern uint Addition(uint a, uint b);

    static public void Main()
    {
        var sum = Integers.Addition(1, 2);
        Console.WriteLine(sum);
    }
}

我们使用 Platform Invoke 功能来访问动态库中的函数 。DllImport属性列出了该库名称,和可以找到的函数名。这些函数是可用作类的静态方法。坚持 C#命名标准,我们使用EntryPoint属性给暴露的函数,用上大写的名称。

这可以用mcs -out:csharp-example src/main.cs编译和用LD_LIBRARY_PATH=target/debug mono csharp-example执行。

Julia

#!/usr/bin/env julia
using Libdl

libname = "integers"
if !Sys.iswindows()
    libname = "lib$(libname)"
end

libintegers = Libdl.dlopen(libname)
addition_sym = Libdl.dlsym(libintegers, :addition)

addition(a, b) = ccall(
    addition_sym,
    UInt32, (UInt32, UInt32), 
    a, b)

println(addition(1, 2))

使用原语 ccall 调用外部函数。如果早只知道函数名, 我们也可以跳过,dlsym获取函数指针的步骤, 改为传递一个 (func, lib) 字面量元组:

addition(a, b) = ccall(
    (:addition, "libintegers"), # ← must be a constant expression!
    UInt32,
    (UInt32, UInt32),
    a, b)

如基本部分所述,这可以在 macOS 和 Linux 上运行 ,使用 LD_LIBRARY_PATH=target/debug/ julia src/main.jl,和在 Windows 通过将target\debug\integers.dll 复制到当前 目录并运行julia src\main.jl