SWIG:将 Python 字符串传递给 void 指针类型的参数
SWIG: Pass Python String to argument of type void pointer
我已经开始使用 SWIG,这样我就可以在 Python 中使用 C 库了。我有这段代码,我将 Python 字符串传递给需要 "void *"
的 C 函数
example.h:
char test(void *buf);
example.c:
char test(void *buf) {
char *c = (char*)buf;
return *c;
}
Python:
import example
buf = "hello"
example.test(buf)
当我 运行 它时,出现以下错误:
TypeError: in method 'test', argument 1 of type 'void *'
但是,当我将 "void*" 参数更改为 "char*" 时,它似乎起作用了。我有点困惑,因为我认为 "void*" 匹配任何类型的指针。无论如何,我四处寻找并找到了 ctypes 库并将其转换为 c_void_p (Python: converting strings for use with ctypes.c_void_p())。那似乎对我不起作用。
作为解决方法,我在我的 swig 文件中制作了一个包装器:
/* File: example.i */
%module example
%include typemaps.i
%{
#include "example.h"
%}
%include "example.h"
%inline %{
char test_wrapper(char *buf) {
void *voidBuf = (void*)buf;
return test(voidBuf);
}
%}
这似乎有效。但是,我想知道是否有人可以解释为什么 ctypes 方法不起作用。如果我对 ctypes 方法完全不满意,是否有比创建内联包装器更合适的方法?
谢谢大家!
您的内联包装器方法是解决不能直接使用的函数的一种合理的通用方法。通常,SWIG 会尝试在目标语言中模仿 C 或 C++ 的行为。在这种情况下,正如您所观察到的那样,它并没有完全做到这一点。我认为这样做的原因有两个:首先,在 SWIG 支持的某些语言(例如 Java)中,您将如何着手这样做并不明显。其次,即使你可以在 Python 中做一些通常适用于这种特定情况的事情 void*
对于 C 程序员来说也是相当模糊的,没有关于它是如何打算的进一步指导 interpreted/used . C 和 Python 之间也存在不匹配 - 字符串在 Python 中被引用计数且不可变,因此很容易误入 Python 行为中断的情况对 Python 程序员来说完全不明显。
使用 ctypes 执行转换并不是真正可行的解决方案,SWIG 和 ctypes 在包装事物和输入参数方面具有根本不同的方法。 (如果你很好奇,我只是在他们之间写了一个相当)
一般情况下的另一个选择是让用户 %include <cpointer.i>
为您生成一些 casting/converting 的代码,例如:
%pointer_cast(type1, type2, name)
但是在这种情况下,由于您使用的是 char*
,因此第一个注释文档中的注释适用:
Note: None of these macros can be used to safely work with strings
(char * or char **).
Note: When working with simple pointers, typemaps can often be used to
provide more seamless operation.
总的来说,我赞成第二个注释提出的观点,即通过多做一些工作(例如 %inline
提供过载)提供更直观的行为。因此,在您的特定情况下,我建议的唯一更改是使用 %rename(test) test_wrapper;
使其成为 Python 内的重载。 (如果您的目标是 C++ 而不是 C,那么您可以将其作为 C++ 中的重载)
我已经开始使用 SWIG,这样我就可以在 Python 中使用 C 库了。我有这段代码,我将 Python 字符串传递给需要 "void *"
的 C 函数example.h:
char test(void *buf);
example.c:
char test(void *buf) {
char *c = (char*)buf;
return *c;
}
Python:
import example
buf = "hello"
example.test(buf)
当我 运行 它时,出现以下错误:
TypeError: in method 'test', argument 1 of type 'void *'
但是,当我将 "void*" 参数更改为 "char*" 时,它似乎起作用了。我有点困惑,因为我认为 "void*" 匹配任何类型的指针。无论如何,我四处寻找并找到了 ctypes 库并将其转换为 c_void_p (Python: converting strings for use with ctypes.c_void_p())。那似乎对我不起作用。
作为解决方法,我在我的 swig 文件中制作了一个包装器:
/* File: example.i */
%module example
%include typemaps.i
%{
#include "example.h"
%}
%include "example.h"
%inline %{
char test_wrapper(char *buf) {
void *voidBuf = (void*)buf;
return test(voidBuf);
}
%}
这似乎有效。但是,我想知道是否有人可以解释为什么 ctypes 方法不起作用。如果我对 ctypes 方法完全不满意,是否有比创建内联包装器更合适的方法?
谢谢大家!
您的内联包装器方法是解决不能直接使用的函数的一种合理的通用方法。通常,SWIG 会尝试在目标语言中模仿 C 或 C++ 的行为。在这种情况下,正如您所观察到的那样,它并没有完全做到这一点。我认为这样做的原因有两个:首先,在 SWIG 支持的某些语言(例如 Java)中,您将如何着手这样做并不明显。其次,即使你可以在 Python 中做一些通常适用于这种特定情况的事情 void*
对于 C 程序员来说也是相当模糊的,没有关于它是如何打算的进一步指导 interpreted/used . C 和 Python 之间也存在不匹配 - 字符串在 Python 中被引用计数且不可变,因此很容易误入 Python 行为中断的情况对 Python 程序员来说完全不明显。
使用 ctypes 执行转换并不是真正可行的解决方案,SWIG 和 ctypes 在包装事物和输入参数方面具有根本不同的方法。 (如果你很好奇,我只是在他们之间写了一个相当
一般情况下的另一个选择是让用户 %include <cpointer.i>
为您生成一些 casting/converting 的代码,例如:
%pointer_cast(type1, type2, name)
但是在这种情况下,由于您使用的是 char*
,因此第一个注释文档中的注释适用:
Note: None of these macros can be used to safely work with strings (char * or char **).
Note: When working with simple pointers, typemaps can often be used to provide more seamless operation.
总的来说,我赞成第二个注释提出的观点,即通过多做一些工作(例如 %inline
提供过载)提供更直观的行为。因此,在您的特定情况下,我建议的唯一更改是使用 %rename(test) test_wrapper;
使其成为 Python 内的重载。 (如果您的目标是 C++ 而不是 C,那么您可以将其作为 C++ 中的重载)