在方法'my_print_hexbytes'中,类型'uint32_t *'的参数1

问题描述 投票:0回答:3

我正在编写SWIG的Python C扩展,但在将二进制缓冲区传递给C api函数时会感到沮丧。这是我的例子:

utils.c

#include "utils.h"
void my_print_hexbytes(uint32_t *bytes, uint32_t bytes_len)
{
  uint32_t i;
  for(i = 0; i < bytes_len; i++)
    printf("%02x", bytes[i]);
  printf("\n");
}

utils.h

#include "commons.h"
#ifndef XXXX_UTILS_H
#define XXXX_UTILS_H
void my_print_hexbytes(uint32_t *bytes, uint32_t bytes_len);
#endif /* XXXX_UTILS_H */

commons.h

#ifndef XXXX_COMMONS_H
#define XXXX_COMMONS_H
....
....
#include <stdint.h>
....
....
#endif

xxxx_for_py.i

%module xxxx_for_py
%{
#define SWIG_FILE_WITH_INIT
#include "commons.h"
#include "utils.h"
%}
%include "stdint.i"
void my_print_hexbytes(uint32_t *bytes, uint32_t bytes_len);

第一次尝试

编译出_xxxx_for_py.so之后,我尝试在IPython中测试它:

In [1]: import xxxx_for_py

In [2]: from ctypes import *

In [3]: a_t = c_uint * 2

In [4]: a = a_t(0x1, 0x2)

In [5]: xxxx_for_py.my_print_hexbytes(a, 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-3c023fcf8b04> in <module>()
----> 1 xxxx_for_py.my_print_hexbytes(a, 2)

TypeError: in method 'my_print_hexbytes', argument 1 of type 'uint32_t *'

那个时候我不知道如何处理这个案子。我尝试将a改为bytearray,但它没有用。

不确定是否有人可以帮我解决这个问题。谢谢!

第二次尝试

感谢@Petesh评论。我试图通过将a投射到POINTER(c_uint)但仍然无法正常工作:(

In [5]: xxxx_for_py.my_print_hexbytes(cast(a, POINTER(c_uint)), 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-2abb9dae484d> in <module>()
----> 1 xxxx_for_py.my_print_hexbytes(cast(a, POINTER(c_uint)), 2)

TypeError: in method 'my_print_hexbytes', argument 1 of type 'uint32_t *'

第三次尝试

Unbounded Array section的SWIG文档中引用,我将carray.i添加到xxxx_for_py.i

xxxx_for_py.i

....
....stdint.i"
%include "carrays.i"
%array_class(uint32_t, uint32Array)
....

重新编译并加载.so后,仍然会出现相同的错误

In [1]: import xxxx_for_py

In [2]: a = xxxx_for_py.uint32Array(2)

In [3]: a[0] = 0x0

In [4]: a[1] = 0x1

In [5]: xxxx_for_py.my_print_hexbytes(a, 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-3c023fcf8b04> in <module>()
----> 1 xxxx_for_py.my_print_hexbytes(a, 2)

TypeError: in method 'my_print_hexbytes', argument 1 of type 'uint32_t *'

第四次尝试

一个星期后,当时,用户Nethanel张贴了answer。虽然它仍然没有用,但它可以帮助我找到一些有趣的东西。

python c swig
3个回答
1
投票

经过简单的测试,我自己找到了解决方案。有关详细信息,请参阅此文章http://au9ustine.github.io/wiki/crypto/cuda/swig.html#typeerror-in-method-xxxx-argument-y-of-type-zzzz (2017-01-13更新:此页面已损坏,我将在此处移动该页面内容)

一个简短的解剖学

在将二进制缓冲区传递给普通的C模块(即由gcc编译的库而没有额外的依赖性)时,继续尝试测试Python代码。内部可能看起来像:

integer list -> array/struct -> raw string -> C function parameter

来自create_string_buffer的一些描述。通过create_string_buffer,Python可以提供这样的功能,为当前变量动态分配内存片段以供使用。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

size_t
print_int_buf(void *src, size_t len)
{
    size_t i = 0;
    uint32_t *int_ptr = (uint32_t *)src;
    for (i = 0; i < len; i++) {
        printf("%p: %x\n", int_ptr + i, int_ptr[i]);
    }
    return i;
}

如果编译为名为my_ext_lib.so的共享库,则可以在Python中加载代码

import ctypes
from ctypes import *
import array

my_ext = ctypes.CDLL('./my_ext_lib.so')
buf = create_string_buffer(array.array('I', range(0x10)).tostring())
my_ext_lib.print_int_buf(buf, 0x10)

它确实有效。

虽然ctype和SWIG是Python和C之间互操作的完全不同的方法,但我在这里使用ctype代码来详细说明将参数传递给已编译模块的基本机制及其工作方式(包括Python模块和普通C共享库模块)。

临时解决方案

由于我的视图中的功能界面(或方法签名)存在显着差异,为什么不将其更改为void *而不是uint32_t *? (虽然这不是最好的方式,但它仍然可以作为收容的临时解决方案)

我用uint32_t *替换void *用于方法my_print_hexbytes,并且附加依赖性cdata.i(我不确定它是否有效,但为了安全起见它已被添加)。因此,下面列出了更改,最终显示了预期结果。

utils.c

#include "utils.h"
...
void 
my_print_hexbytes(void *bytes, size_t bytes_len)
{
  size_t i = 0;
  uint32_t *int_buf_ptr = (uint32_t *)bytes;
  for(i = 0; i < bytes_len; i++)
    printf("%02x", int_buf_ptr[i]);
  printf("\n");
}

标头utils.h

#include "commons.h"
#ifndef XXXX_UTILS_H
#define XXXX_UTILS_H
...
void my_print_hexbytes(void *bytes, size_t bytes_len);
#endif /* XXXX_UTILS_H */

xxxx_for_py.i

%module xxxx_for_py
%{
#define SWIG_FILE_WITH_INIT
#include "commons.h"
#include "utils.h"
%}
%include "stdint.i"
%include "carrays.i"
%include "cdata.i"
%array_class(uint32_t, uint32Array)
...
void my_print_hexbytes(void *bytes, size_t bytes_len);

在Python中调用方法,它将是:

In [1]: import xxxx_for_py

In [2]: a = xxxx_for_py.uint32Array(0x10)

In [3]: for i in range(0x10):
   ...:     a[i] = i
   ...:

In [4]: xxxx_for_py.my_print_hexbytes(a, 0x10)
000102030405060708090a0b0c0d0e0f

0
投票

发现你的问题,因为同样的问题。

不幸的是,似乎SWIG不支持高级类型:在http://www.swig.org/Doc3.0/Library.html#Library_carrays中,在对array_class宏进行描述之后,它写成:“当使用这个宏时,类型被限制为一个简单的类型名,如int或float。”

我也试过使用“unsigned int”失败了。

最后我的解决方法是使用C ++向量而不是uint32_t指针,这里是swig文件的相关部分:

%include "stdint.i"
%include "std_vector.i"

namespace std {
   %template(vectoruint32) vector<uint32_t>;
};

h文件的一部分:

#include <vector>

static uint32_t foo(std::vector<uint32_t> v) {return v[1] - v[0];}

蟒蛇:

import example
l = [1, 2, 3, 4]
example.foo(l)

希望这可以帮助


0
投票

我有一个非常简单的案例,但仍然有这个错误。这个问题的解决方法有效:Swig python - c++ how to use type int8_t

我在.i文件中的模块def之后直接添加了%include "stdint.i"

© www.soinside.com 2019 - 2024. All rights reserved.