提问人:Kuglatec 提问时间:10/27/2023 更新时间:10/30/2023 访问量:81
ctypes python/C 中的结构数组
Array of Structures in ctypes python/C
问:
运行我的代码后,我只能访问我的数组的第一个结构,该结构在 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 定义为顶部定义的文件类的结构数组
答:
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
非常感谢你
评论