ctypes python/C中的结构数组

fcwjkofz  于 2023-11-16  发布在  Python
关注(0)|答案(1)|浏览(124)

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

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定义为在顶部定义的file类的结构体数组

62lalag4

62lalag41#

正如我在评论中所说的,看起来像是[SO]: Problems with passing and getting arrays for a C function using ctypes (@CristiFati's answer)的副本(尽管那个是用于 doubles而不是结构,并且还包含(额外的)双指针(*)东西),所以请确保首先阅读它。
我还想强调一下[SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)(你有一个 typoargtypes)。
我准备了一个例子,我猜你是想。

  • 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.

相关问题