【问题标题】:Python lists and ctypesPython 列表和 ctypes
【发布时间】:2016-02-06 19:42:35
【问题描述】:

您好,我正在尝试调用具有以下签名的 c++ 函数

change_mountain_heights(mnt *mountains[],float32 heights[],int8 num_elements)

这个想法来自 python 我可以有一个山脉及其高度的列表,我可以调用 c++ 函数来设置它们的高度。 C++ 函数需要 2 个数组。一个包含山名的数组和另一个包含山高的数组。

所以在 python 中我有 2 个列表

  • mountains=[乞力马扎罗山,mount_saina,mount_elgon]
  • mnt_heights=[100.1,200,3331.56]
  • element_count=len(山)

我使用ctypes并调用c函数如下,

def change_heights(mountain_list,height_list,element_count):
    mountain_array=(ctypes.c_char_p * len(mountain_list))(*mountain_list)
    height_array=(ctypes.c_float * len(height_list))(*height_list)
    element_count_param=ctypes.c_int8(element_count)
    change_mountain_heights.argtypes=(ctypes.c_char_p,ctypes.c_float,ctypes.c_int8)
    _success=change_mountain_heights(mountain_array,height_array,element_count_param)

但它似乎不起作用,我不能 100% 确定我是否正确地创建了数组,因为大小 确保山字符串正确分配给数组, 同样对于高度。我做错了什么,还是应该在别处寻找问题的根源?

更新

我认为其中一个问题源于

    change_mountain_heights.argtypes=(ctypes.c_char_p,ctypes.c_float,ctypes.c_int8)

现在山脉列表是一个字符串数组,或者在 python 中是一个字符串列表,我如何将 argtypes 设置为字符串列表或至少是一个字符串数组。

我是否正确假设我在 argtypes 中使用的 ctypes.c_char_p 不允许我将字符串数组传递给函数?我应该在 argtypes 中有什么表明我将通过

  1. 一个包含字符串的数组
  2. 一个包含浮点数的数组

我在 windows 上使用 python 2.7.10 问候

【问题讨论】:

  • 你能定义“它似乎不起作用”吗?您至少可以调用该函数吗?由于该函数是一个 c++ 函数,如果它没有声明为 extern "C",它可能会被导出为一个修饰函数名。 mnt 只是 typedef 上的 char 吗?
  • 我可以调用该函数,实际上它并没有崩溃,但是当我查看新的山高时,它们保持不变。我单步执行了代码,成功返回 1,失败返回 0(我快速阅读了一些文档),它总是返回 0。我想我没有正确的 argtypes。但我不确定如何将数组声明为 argtypes 的一部分。
  • 函数如何知道两个数组中有多少元素(因为它们不是 size 参数)?这是否意味着moutains 数组中的最后一个元素是NULL
  • 抱歉没觉得size参数很重要,更新问题

标签: c++ python-2.7 ctypes


【解决方案1】:

这是一个 C 语言的简单测试(每个高度加 100.0):

TMPDLL_API /* export */
int WINAPIV change_mountain_heights(char* mountains[], float heights[], int8_t num_elements)
{
    if (num_elements <= 0)
        return 0;

    if (!(mountains && heights))
        return 0;

    for (int8_t index = 0; index < num_elements; ++index) {
        char* mountain = mountains[index];
        printf("mountain: %s ; height: %f\n", mountain, heights[index]);

        // add 100.0
        heights[index] += 100.0f;
    }

    return 1;
}

python代码:

import ctypes
import os

current_dir = os.path.dirname(os.path.realpath(__file__))
dll_path = os.path.join(current_dir, "tmpDll.dll")

_tmp_dll = ctypes.CDLL(dll_path)

# int change_mountain_heights(char* mountains[], float heights[], int8_t num_elements)

_tmp_dll.change_mountain_heights.restype = ctypes.c_int  # default, not required
_tmp_dll.change_mountain_heights.argtypes = (
    ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_float),
    ctypes.c_int8)


def change_mountain_heights(mountains, mountain_heights):
    if len(mountains) != len(mountain_heights):
        raise ValueError()

    # pointer to strings
    string_pointers = (ctypes.c_char_p * len(mountains))(*mountains)
    # floats
    floats = (ctypes.c_float * len(mountain_heights))(*mountain_heights)
    # pointer to floats
    pfloat = ctypes.cast(floats, ctypes.POINTER(ctypes.c_float))

    # call C function
    result = _tmp_dll.change_mountain_heights(string_pointers, pfloat,
                                              len(mountain_heights))
    print("result: {}".format(result))
    if result == 1:
        # change passed list with new results
        del mountain_heights[:]
        mountain_heights.extend(list(floats))

    return result


def main():
    mountains = ["foo", "bar", "baz"]
    mountain_heights = [100.0, 200.0, 300.0]

    print("Before: {}".format(mountain_heights))

    change_mountain_heights(mountains, mountain_heights)

    print("After: {}".format(mountain_heights))

if __name__ == "__main__":
    main()

输出:

Before: [100.0, 200.0, 300.0]
result: 1
After: [200.0, 300.0, 400.0]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-29
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多