ctypes:如何处理包含结构的结构?

时间:2019-10-04 09:00:51

标签: python c ctypes

我在C语言中有一个简单的API:

void init( t_main_struct* p_main_struct );
void calc( t_main_struct* p_main_struct, float* p_result );

我用作:

t_main_struct my_main_struct;
float my_result[3];

init(&my_main_struct);
calc(&my_main_struct, my_result);

我想使用Python ctypes包装以上API。

问题是,t_main_struct非常复杂:包含其他结构,这些结构包含其他结构,这些结构包含其他结构,等等。

在Python方面,我实际上不需要访问任何my_main_struct内部构件,这是一个黑匣子。我只需要知道存储在calc()中的my_result结果。

是否可以通过某种方式简化ctypes的{​​{1}}实施?我是否必须指定所有结构成员,包括子结构?或者我可以以某种方式传递足够的内存而无需指定所有结构细节?

1 个答案:

答案 0 :(得分:0)

列出[Python 3.Docs]: ctypes - A foreign function library for Python

如果您不关心结构的定义,并且由于两个函数都将指针作为参数,那么( Python )您唯一需要做的就是拥有足够大的缓冲区来容纳它(您可以使用 void 指针来引用 struct 实例)。
请记住,如果结构(递归)包含指向其他结构的指针,则也需要对这些结构进行分配(但我想这必须在 init中发生)。

dll00.c

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif


typedef struct _ComplexStruct {
    float f0;
    int i0;
    // The other members
} ComplexStruct;


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

DLL00_EXPORT_API void init(ComplexStruct *pStruct);
DLL00_EXPORT_API void calc(ComplexStruct *pStruct, float *pResult);

#if defined(__cplusplus)
}
#endif


DLL00_EXPORT_API void init(ComplexStruct *pStruct) {
    if (pStruct) {
        pStruct->f0 = -3.5;
        pStruct->i0 = 5;
    }
}


DLL00_EXPORT_API void calc(ComplexStruct *pStruct, float *pResult) {
    if ((pStruct) && (pResult)) {
        (*pResult) = pStruct->f0 * pStruct->i0;
    }
}

code00.py

#!/usr/bin/env python3

import sys
import ctypes


DLL_NAME = "./dll00.dll"


def main():
    dll00 = ctypes.CDLL(DLL_NAME)

    init = dll00.init
    init.argtypes = [ctypes.c_void_p]
    init.restype = None

    calc = dll00.calc
    calc.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_float)]
    calc.restype = None

    buf = ctypes.create_string_buffer(512)  # Create a buffer large enough to hold the structure

    init(buf)
    res = ctypes.c_float()
    calc(buf, ctypes.byref(res))

    print("Result: {0:.3f}".format(res.value))


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

输出

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058233135]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.16
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

[prompt]> dir /b
code00.py
dll00.c

[prompt]> cl /nologo /DDLL dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
   Creating library dll00.lib and object dll00.exp

[prompt]> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32

Result: -17.500

Done.