传递和返回整数
整数是 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
。