草庐IT

python - 构建 ctypes 类的简洁方式

coder 2023-05-23 原文

我已经定义了一个 ctypes 类和一个相关的便利函数,如下所示:

class BNG_FFITuple(Structure):
    _fields_ = [("a", c_uint32),
                ("b", c_uint32)]


class BNG_FFIArray(Structure):
    _fields_ = [("data", c_void_p),
                ("len", c_size_t)]

    # Allow implicit conversions from a sequence of 32-bit unsigned ints
    @classmethod
    def from_param(cls, seq):
        return seq if isinstance(seq, cls) else cls(seq)

    def __init__(self, seq, data_type = c_float):
        array_type = data_type * len(seq)
        raw_seq = array_type(*seq)
        self.data = cast(raw_seq, c_void_p)
        self.len = len(seq)


def bng_void_array_to_tuple_list(array, _func, _args):
    res = cast(array.data, POINTER(BNG_FFITuple * array.len))[0]
    return res

convert = lib.convert_to_bng
convert.argtypes = (BNG_FFIArray, BNG_FFIArray)
convert.restype = BNG_FFIArray
convert.errcheck = bng_void_array_to_tuple_list
drop_array = lib.drop_array 
drop_array.argtypes = (POINTER(BNG_FFIArray),)

然后我定义一个简单的便利函数:

def f(a, b):
    return [(i.a, i.b) for i in iter(convert(a, b))]

大部分都可以完美运行,但我有两个问题:

  • 不够灵活;我希望能够使用 c_float 而不是 c_uint32 实例化 BNG_FFITuple (所以字段是 c_float ),反之亦然,所以 BNG_FFIArray data_typec_uint32。不过,我不清楚如何做到这一点。
  • 我想释放现在由 Python 拥有的内存,方法是将 POINTER(BNG_FFIArray) 发送回我的 dylib(请参阅 drop_array - 我已经已经在我的 dylib 中定义了一个函数),但我不确定应该在什么时候调用它。

有没有办法以一种更整洁、更 Python 的方式封装所有这些,也更安全?我担心如果没有以稳健的方式定义内存清理(在 __exit__?__del__?)出现任何问题都会导致内存未释放

最佳答案

由于您对 rust 方面有一定的控制权,因此最简单的做法是在调用之前从 Python 预先分配结果数组,并将所有内容传递到单个结构中。

下面的代码假定了这种修改,但也指定了如果你不能这样做,你将在哪里进行释放。

请注意,如果您进行这种封装,则不需要为库函数指定参数和结果处理等内容,因为您只是从一个地方调用实际函数,并且始终使用完全相同的相同类型的参数。

我不知道 rust(甚至我的 C 有点生锈),但下面的代码假设您重新定义 rust 以匹配类似这样的内容:

typedef struct FFIParams {
    int32 source_ints;
    int32 len;
    void * a;
    void * b;
    void * result;
} FFIParams;

void convert_to_bng(FFIParams *p) {
}

这里是 Python。最后一点——由于参数结构的重用,这不是线程安全的。如果需要,这很容易解决。

from ctypes import c_uint32, c_float, c_size_t, c_void_p
from ctypes import Structure, POINTER, pointer, cast
from itertools import izip, islice

_test_standalone = __name__ == '__main__'

if _test_standalone:
    class lib(object):
        @staticmethod
        def convert_to_bng(ptr_params):
            params = ptr_params.contents
            source_ints = params.source_ints
            types = c_uint32, c_float
            if not source_ints:
                types = reversed(types)
            length = params.len
            src_type, dst_type = types
            src_type = POINTER(length * src_type)
            dst_type = POINTER(length * 2 * dst_type)
            a = cast(params.a, src_type).contents
            b = cast(params.b, src_type).contents
            result = cast(params.result, dst_type).contents

            # Assumes we are converting int to float or back...
            func = float if source_ints else int
            result[0::2] = map(func, a)
            result[1::2] = map(func, b)

class _BNG_FFIParams(Structure):
    _fields_ = [("source_ints", c_uint32),
                ("len", c_size_t),
                ("a", c_void_p),
                ("b", c_void_p),
                ("result", c_void_p)]

class _BNG_FFI(object):

    int_type = c_uint32
    float_type = c_float
    _array_type = type(10 * int_type)

    # This assumes we want the result to be opposite type.
    # Maybe I misunderstood this -- easily fixable if so.
    _result_type = {int_type: float_type, float_type: int_type}

    def __init__(self):
        my_params = _BNG_FFIParams()
        self._params = my_params
        self._pointer = POINTER(_BNG_FFIParams)(my_params)
        self._converter = lib.convert_to_bng


    def _getarray(self, seq, data_type):
        # Optimization for pre-allocated correct array type
        if type(type(seq)) == self._array_type and seq._type_ is data_type:
            print("Optimized!")
            return seq
        return (data_type * len(seq))(*seq)

    def __call__(self, a, b, data_type=float_type):
        length = len(a)
        if length != len(b):
            raise ValueError("Input lengths must be same")

        a, b = (self._getarray(x, data_type) for x in (a, b))

        # This has the salutary side-effect of insuring we were
        # passed a valid type
        result = (length * 2 * self._result_type[data_type])()

        params = self._params
        params.source_ints = data_type is self.int_type
        params.len = length
        params.a = cast(pointer(a), c_void_p)
        params.b = cast(pointer(b), c_void_p)
        params.result = cast(pointer(result), c_void_p)
        self._converter(self._pointer)

        evens = islice(result, 0, None, 2)
        odds = islice(result, 1, None, 2)
        result = list(izip(evens, odds))

        # If you have to have the converter allocate memory,
        # deallocate it here...

        return result

convert = _BNG_FFI()

if _test_standalone:
    print(convert([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], c_float))
    print(convert([1, 2, 3], [4, 5, 6], c_uint32))
    print(convert([1, 2, 3], (c_uint32 * 3)(4, 5, 6), c_uint32))

关于python - 构建 ctypes 类的简洁方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31316766/

有关python - 构建 ctypes 类的简洁方式的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

    question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

  5. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  6. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  7. 没有类的 Ruby 方法? - 2

    大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow

  8. ruby - Rails 关联 - 同一个类的多个 has_one 关系 - 2

    我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下

  9. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  10. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

随机推荐