ctypes python/C 中的结构数组

Array of Structures in ctypes python/C

提问人:Kuglatec 提问时间:10/27/2023 更新时间:10/30/2023 访问量:81

问:

运行我的代码后,我只能访问我的数组的第一个结构,该结构在 c 中定义 在外部文件中定义的函数。 我认为,错误出在这一行:其中仅将 restype 定义为“文件”而不是文件数组。 我无法找到这个语法,所以我向你们寻求帮助。 提前非常感谢。 这是我的python源代码:engineLIB.get_files.restype = ctypes.POINTER(file)

import ctypes

engineLIB = ctypes.CDLL('./engine.so')

class file(ctypes.Structure):
    _fields_ = [
        ("name", ctypes.c_char * 250),
        ("path", ctypes.c_char * 250),
        ("fileCnt", ctypes.c_int),
    ]

engineLIB.get_files.restype = ctypes.POINTER(file)
engineLIB.get_files.argtype = ctypes.c_char_p

path = input("Input path: ")
files = engineLIB.get_files(path.encode("utf-8"))

for i in range(2):
    print(files[i].name)
    print(files[i].path)


我尝试将 restype 定义为顶部定义的文件类的结构数组

python 数组 c struct ctypes

评论

0赞 CristiFati 10/27/2023
stackoverflow.com/a/57299823/4788546 包含一个示例(不是针对结构,而是相同的原理)。

答:

0赞 CristiFati 10/29/2023 #1

正如我的评论中所述,看起来像是 [SO] 的副本:使用 ctypes 传递和获取 C 函数数组的问题(@CristiFati的答案)(尽管该数组用于双 s 而不是结构,并且还包含(额外)双指针**) 的东西),所以一定要阅读它 1st

我还想强调 [SO]:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati的答案)(你有一个错别字argtypes)。

我准备了一个例子,我猜你想做什么。

dll00.c

#include <stdlib.h>
#include <string.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  include <dirent.h>
#  define DLL00_EXPORT_API
#endif

#define MAXPATH 250


typedef struct {
    char name[MAXPATH];
    char path[MAXPATH];
    int cnt;
} File, *PFile;


#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API PFile getFiles(const char *path, int *pCount);
DLL00_EXPORT_API void dealloc(PFile ptr);

#if defined(__cplusplus)
}
#endif


PFile getFiles(const char *path, int *pCount)
{
    PFile ret = NULL;
    int idx = 0;
#if !defined(_WIN32)
    DIR *pDir = NULL;
    struct dirent *pEntry = NULL;
    if ((!pCount) || (!path)) {
        return ret;
    }
    if (!(pDir = opendir(path))) {
        *pCount = -1;
        return ret;
    }
    *pCount = 0;
    while ((pEntry = readdir(pDir))) {
        ++(*pCount);
    }
    closedir(pDir);

    if (!(pDir = opendir(path))) {
        *pCount = -1;
        return ret;
    }
    *pCount += idx;
    ret = calloc(*pCount, sizeof(File));
    while ((pEntry = readdir(pDir))) {
        strcpy(ret[idx].name, pEntry->d_name);
        strcpy(ret[idx].path, path);
        ret[idx].cnt = idx;
        ++idx;
    }
    closedir(pDir);
#endif
    return ret;
}


void dealloc(PFile ptr)
{
    free(ptr);
}

code00.py

#!/usr/bin/env python

import ctypes as cts
import sys


DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")

MAXPATH = 250  # @TODO - cfati: definitions must match the C ones

class File(cts.Structure):
    _fields_ = (
        ("name", cts.c_char * MAXPATH),
        ("path", cts.c_char * MAXPATH),
        ("cnt", cts.c_int),
    )

FilePtr = cts.POINTER(File)


def main(*argv):
    dll = cts.CDLL(DLL_NAME)
    getFiles = dll.getFiles
    getFiles.argtypes = (cts.c_char_p, cts.POINTER(cts.c_int))
    getFiles.restype = FilePtr
    dealloc = dll.dealloc
    dealloc.argtypes = (FilePtr,)
    dealloc.restype = None


    #path = input("Input path: ")
    path = "."
    count = cts.c_int(0)
    files = getFiles(path.encode(), cts.byref(count))
    print("Path [{:s}] has {:d} entries:".format(path, count.value))
    for idx in range(count.value):
        file = files[idx]
        print("\nName: [{:s}]\nPath: [{:s}]\nCnt: {:d}\n".format(file.name.decode(), file.path.decode(), file.cnt))
    dealloc(files)


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

注意

  • 当以指针的形式(缺少大小信息)返回数组(因为这是函数返回的内容:结构数组)时,有一些方法可以向调用者发出大小信号。我选择了最直接的一个:函数的附加(输出)参数

    • 另一种方法是在数组的末尾有一个额外的条目,其中包含一些字段的哨兵值(有点像 NUL 用于标记 char* 的末尾),但我不喜欢它
  • C 代码效率低下,它会遍历目录两次(一次用于获取文件计数,第二次用于实际获取数据)。还有其他选择(在需要时增加数组大小),但代码会变得非常复杂,并且超出了问题范围

  • 多一些错误处理不会有什么坏处

  • 尽管超出了问题的范围,但 getFiles 不会在 Win 上返回任何内容

输出

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q077368808]> . ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
code00.py  dll00.c
[064bit prompt]> 
[064bit prompt]> gcc -fPIC -shared -o dll00.so dll00.c
[064bit prompt]> ls
code00.py  dll00.c  dll00.so
[064bit prompt]> 
[064bit prompt]> python ./code00.py 
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] 064bit on linux

Path [.] has 5 entries:

Name: [.]
Path: [.]
Cnt: 0

Name: [..]
Path: [.]
Cnt: 1

Name: [code00.py]
Path: [.]
Cnt: 2

Name: [dll00.c]
Path: [.]
Cnt: 3

Name: [dll00.so]
Path: [.]
Cnt: 4

Done.

评论

1赞 Kuglatec 10/30/2023
非常感谢你