将元组的 Rust 向量转换为 C 兼容结构

     2023-02-18     34

关键词:

【中文标题】将元组的 Rust 向量转换为 C 兼容结构【英文标题】:Convert Rust vector of tuples to a C compatible structure 【发布时间】:2015-09-08 04:25:17 【问题描述】:

按照theseanswers,我目前定义了一个Rust 1.0函数如下,以便可以使用ctypes从Python调用:

use std::vec;

extern crate libc;
use libc::c_int, c_float, size_t;
use std::slice;

#[no_mangle]
pub extern fn convert_vec(input_lon: *const c_float, 
                          lon_size: size_t, 
                          input_lat: *const c_float, 
                          lat_size: size_t) -> Vec<(i32, i32)> 
    let input_lon = unsafe 
        slice::from_raw_parts(input_lon, lon_size as usize)
    ;
    let input_lat = unsafe 
        slice::from_raw_parts(input_lat, lat_size as usize)
    ;

    let combined: Vec<(i32, i32)> = input_lon
        .iter()
        .zip(input_lat.iter())
        .map(|each| convert(*each.0, *each.1))
        .collect();
    return combined

我正在像这样设置 Python 部分:

from ctypes import *

class Int32_2(Structure):
    _fields_ = [("array", c_int32 * 2)]

rust_bng_vec = lib.convert_vec_py
rust_bng_vec.argtypes = [POINTER(c_float), c_size_t, 
                         POINTER(c_float), c_size_t]
rust_bng_vec.restype = POINTER(Int32_2)

这似乎没问题,但我是:

不确定如何将combinedVec&lt;(i32, i32)&gt;)转换为与 C 兼容的结构,以便可以将其返回到我的 Python 脚本中。 不确定我是否应该返回一个引用 (return &amp;combined?),如果我这样做了,我将如何使用适当的生命周期说明符来注释函数

【问题讨论】:

不要在您的问题中提出解决方案。如果您想分享对您有用的代码,您可以发布自己的答案。 【参考方案1】:

最需要注意的是,C 中没有元组这样的东西。C 是库互操作性的通用语言,你会被要求限制自己使用这种语言的能力。如果你在 Rust 和另一种高级语言之间交谈,这并不重要;你必须说C。

C 中可能没有元组,但有structs。一个二元素元组只是一个有两个成员的结构!

让我们从我们要编写的 C 代码开始:

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

typedef struct 
  uint32_t a;
  uint32_t b;
 tuple_t;

typedef struct 
  void *data;
  size_t len;
 array_t;

extern array_t convert_vec(array_t lat, array_t lon);

int main() 
  uint32_t lats[3] = 0, 1, 2;
  uint32_t lons[3] = 9, 8, 7;

  array_t lat =  .data = lats, .len = 3 ;
  array_t lon =  .data = lons, .len = 3 ;

  array_t fixed = convert_vec(lat, lon);
  tuple_t *real = fixed.data;

  for (int i = 0; i < fixed.len; i++) 
    printf("%d, %d\n", real[i].a, real[i].b);
  

  return 0;

我们定义了两个structs——一个代表我们的元组,另一个代表一个数组,因为我们将来回传递它们。

我们将通过在 Rust 中定义 完全相同 结构来跟进这一点,并将它们定义为具有 完全相同 成员(类型、排序、名称)。重要的是,我们使用 #[repr(C)] 让 Rust 编译器知道不要对数据进行重新排序。

extern crate libc;

use std::slice;
use std::mem;

#[repr(C)]
pub struct Tuple 
    a: libc::uint32_t,
    b: libc::uint32_t,


#[repr(C)]
pub struct Array 
    data: *const libc::c_void,
    len: libc::size_t,


impl Array 
    unsafe fn as_u32_slice(&self) -> &[u32] 
        assert!(!self.data.is_null());
        slice::from_raw_parts(self.data as *const u32, self.len as usize)
    

    fn from_vec<T>(mut vec: Vec<T>) -> Array 
        // Important to make length and capacity match
        // A better solution is to track both length and capacity
        vec.shrink_to_fit();

        let array = Array  data: vec.as_ptr() as *const libc::c_void, len: vec.len() as libc::size_t ;

        // Whee! Leak the memory, and now the raw pointer (and
        // eventually C) is the owner.
        mem::forget(vec);

        array
    


#[no_mangle]
pub extern fn convert_vec(lon: Array, lat: Array) -> Array 
    let lon = unsafe  lon.as_u32_slice() ;
    let lat = unsafe  lat.as_u32_slice() ;

    let vec =
        lat.iter().zip(lon.iter())
        .map(|(&lat, &lon)| Tuple  a: lat, b: lon )
        .collect();

    Array::from_vec(vec)

我们必须绝不在 FFI 边界上接受或返回非repr(C) 类型,因此我们通过Array。请注意,有大量的unsafe 代码,因为我们必须将指向数据的未知指针 (c_void) 转换为特定类型。这就是 C 世界中泛型的代价。

现在让我们把目光转向 Python。基本上,我们只需要模仿 C 代码所做的:

import ctypes

class FFITuple(ctypes.Structure):
    _fields_ = [("a", ctypes.c_uint32),
                ("b", ctypes.c_uint32)]

class FFIArray(ctypes.Structure):
    _fields_ = [("data", ctypes.c_void_p),
                ("len", ctypes.c_size_t)]

    # Allow implicit conversions from a sequence of 32-bit unsigned
    # integers.
    @classmethod
    def from_param(cls, seq):
        return cls(seq)

    # Wrap sequence of values. You can specify another type besides a
    # 32-bit unsigned integer.
    def __init__(self, seq, data_type = ctypes.c_uint32):
        array_type = data_type * len(seq)
        raw_seq = array_type(*seq)
        self.data = ctypes.cast(raw_seq, ctypes.c_void_p)
        self.len = len(seq)

# A conversion function that cleans up the result value to make it
# nicer to consume.
def void_array_to_tuple_list(array, _func, _args):
    tuple_array = ctypes.cast(array.data, ctypes.POINTER(FFITuple))
    return [tuple_array[i] for i in range(0, array.len)]

lib = ctypes.cdll.LoadLibrary("./target/debug/libtupleffi.dylib")

lib.convert_vec.argtypes = (FFIArray, FFIArray)
lib.convert_vec.restype = FFIArray
lib.convert_vec.errcheck = void_array_to_tuple_list

for tupl in lib.convert_vec([1,2,3], [9,8,7]):
    print tupl.a, tupl.b

原谅我的初级 Python。 我相信有经验的 Pythonista 可以让这个看起来更漂亮!感谢@eryksun 提供了some nice advice 如何让调用方法的消费者方面更好更多 .

关于所有权和内存泄漏的一句话

在这个示例代码中,我们泄露了Vec 分配的内存。理论上,FFI 代码现在拥有内存,但实际上,它不能用它做任何有用的事情。要获得一个完全正确的示例,您需要添加另一个方法来接受从被调用者返回的指针,将其转换回 Vec,然后允许 Rust 删除该值。这是唯一安全的方法,因为 Rust 几乎可以保证使用与您的 FFI 语言使用的内存分配器不同的内存分配器。

不确定我是否应该返回一个引用,以及如果我这样做了,我将如何使用适当的生命周期说明符来注释函数

不,您不想(阅读:不能)返回引用。如果可以,那么项目的所有权将以函数调用结束,并且引用将指向任何内容。这就是为什么我们需要使用mem::forget 进行两步舞并返回一个原始指针。

【讨论】:

这非常彻底。非常感谢。 这里是from_paramerrcheck 的文档链接。 @eryksun 您有没有机会整理一个包含您的建议的基本示例? @eryksun 我已经更新了,再次感谢!我最不确定的部分是errcheck 函数;我写的方式有意义吗? 我会使用 return ctypes.cast(array.data, ctypes.POINTER(FFITuple * array.len))[0] 返回一个 FFITuple 数组。另外,我忘了在from_param 中你应该传递一个类的实例,例如return arg if isinstance(arg, cls) else cls(arg).

如何使用列表推导将元组的元组转换为一维列表? [复制]

】如何使用列表推导将元组的元组转换为一维列表?[复制]【英文标题】:HowdoIconvertatupleoftuplestoaone-dimensionallistusinglistcomprehension?[duplicate]【发布时间】:2011-03-1308:47:37【问题描述】:我有一个元组-例如:tupleOfTuples=((1,2),(3,4),(5,)... 查看详情

Typescript 将元组的类型元组转换为元组(扁平元组)

】Typescript将元组的类型元组转换为元组(扁平元组)【英文标题】:TypescriptconverttypeTupleoftupletoTuple(flattenTuple)【发布时间】:2020-04-1101:01:12【问题描述】:我有这个TypeT=Params<[Tuple1,Tuple2]>//eg[[string],[number]]如何制作(展平)T... 查看详情

将元组的字符串表示形式转换为真正的元组

】将元组的字符串表示形式转换为真正的元组【英文标题】:Convertastringrepresentationofatupletoarealtuple【发布时间】:2015-12-1922:11:26【问题描述】:如何将这个字符串"[type,a,to,room01023123,body,heywhat\'supmister,by,someone]"转换成这样... 查看详情

将元组的无序列表转换为 pandas DataFrame

】将元组的无序列表转换为pandasDataFrame【英文标题】:ConvertingunorderedlistoftuplestopandasDataFrame【发布时间】:2018-05-2920:57:57【问题描述】:我正在使用库usaddress从我拥有的一组文件中解析地址。我希望我的最终输出是一个数据框,... 查看详情

如何将元组列表转换为 pandas 数据框,以便每个元组的第一个值代表一列?

】如何将元组列表转换为pandas数据框,以便每个元组的第一个值代表一列?【英文标题】:HowcanItransformalistoftuplesintoapandasdataframesothatthefirstvalueofeachtuplerepresentsacolumn?【发布时间】:2020-04-1315:47:33【问题描述】:我想转换我的元... 查看详情

Spark 2.0:如何将元组的 RDD 转换为 DF [重复]

】Spark2.0:如何将元组的RDD转换为DF[重复]【英文标题】:Spark2.0:howtoconvertaRDDofTuplestoDF[duplicate]【发布时间】:2017-06-0103:12:16【问题描述】:我正在将我的一个项目从Spark1.6升级到Spark2.0.1。以下代码适用于Spark1.6,但不适用于2.0.1... 查看详情

如何创建将二叉树转换为元组的函数?

...2-0100:56:43【问题描述】:我遇到了这个问题,我的任务是将元组转换为二叉树,然后将二叉树转换回元组并返回树和元组。我能够将元组转换为树,但我未能创建一个函数来执行相反的操作。我只是一个尝试学习数据结构的初学... 查看详情

如何在python中将元组列表转换为元组的元组[重复]

】如何在python中将元组列表转换为元组的元组[重复]【英文标题】:Howtoconvertlistoftuplestotupleoftuplesinpython[duplicate]【发布时间】:2017-07-3001:50:40【问题描述】:我正在尝试将元组列表转换为元组元组,我做错了,请帮助解决此问题... 查看详情

如何在 Python 中将元组的元组转换为 pandas.DataFrame?

】如何在Python中将元组的元组转换为pandas.DataFrame?【英文标题】:Howtoconverttupleoftuplestopandas.DataFrameinPython?【发布时间】:2016-02-1512:00:00【问题描述】:没有冒犯,如果问题太基本。如果您需要更多信息,请告诉我。我正在寻找... 查看详情

将元组列表转换为字典

】将元组列表转换为字典【英文标题】:Convertingalistoftuplesintoadict【发布时间】:2010-09-2016:07:28【问题描述】:我有一个这样的元组列表:[(\'a\',1),(\'a\',2),(\'a\',3),(\'b\',1),(\'b\',2),(\'c\',1),]我想通过第一项来遍历这个键控,因此,... 查看详情

将元组的元素插入数据库

】将元组的元素插入数据库【英文标题】:Insertingtuple\'selementstodatabase【发布时间】:2011-12-2411:39:07【问题描述】:我有一个元组,我想存储它的元素,我试图将它插入如下,它给出了以下错误,我做错了什么?records_to_be_inserted... 查看详情

如何将元组类型转换为联合?

】如何将元组类型转换为联合?【英文标题】:Howtoconvertatupletypetoaunion?【发布时间】:2020-04-0200:15:31【问题描述】:如何将元组泛型类型映射到联合类型?typeNeededUnionType<T>=T[keyofT];//IncludesalltheArraypropertiesvaluesconstvalue:NeededUn... 查看详情

将元组列表转换为 Pandas 系列

】将元组列表转换为Pandas系列【英文标题】:ConvertingalistoftuplestoaPandasseries【发布时间】:2019-04-2104:00:17【问题描述】:我有一个要转换为系列的元组列表。returnarray2[(0,0.07142857142857142),(0,0.07142857142857142),(1,0.08333333333333333),(1,0.3333... 查看详情

TypeScript:将元组类型转换为对象

】TypeScript:将元组类型转换为对象【英文标题】:TypeScript:converttupletypetoobject【发布时间】:2021-11-0404:34:13【问题描述】:总结:我有一个这样的元组类型:[session:SessionAgent,streamID:string,isScreenShare:boolean,connectionID:string,videoProducer... 查看详情

如何将元组中的字典列表转换为表格数据/熊猫数据框?

】如何将元组中的字典列表转换为表格数据/熊猫数据框?【英文标题】:Howtoconvertalistofdictionariesinsideatupleintotabulardata/pandasDataFrame?【发布时间】:2017-07-1121:02:06【问题描述】:我目前有一个包含两个元素的元组列表,一个字符... 查看详情

Python将元组转换为值

】Python将元组转换为值【英文标题】:Pythontoconverttupletovalue【发布时间】:2018-05-3114:29:47【问题描述】:我正在尝试检索表中的行数与:importpostgresqldb=postgresql.open(...)res=db.query("selectcount(1)fromtestdata")print(res)>>>(10,)如何只打... 查看详情

将元组中的列表转换为numpy数组?

】将元组中的列表转换为numpy数组?【英文标题】:Convertlistintupletonumpyarray?【发布时间】:2011-01-2501:05:40【问题描述】:我有列表元组。这些列表之一是分数列表。我想将分数列表转换为numpy数组,以利用scipy提供的预构建统计... 查看详情

Python:将元组转换为二维数组

】Python:将元组转换为二维数组【英文标题】:Python:convertingtupleinto2Darray【发布时间】:2016-04-2219:44:07【问题描述】:我想像这样转换元组t=[(4,10),(9,7),(11,2),(2,2)]像这样的二维数组:a=[[4,10],[9,7],[11,2],[2,2]]我试过了a=[]foriint:a.append... 查看详情