自引用类:来自C接口的具体python类

我正在尝试设计一个可以在
Python中轻松扩展的C接口(使用
ctypes).我在C中使用了
natural idiom

struct format {
    int (*can_open)(const char *filename);
    struct format * (*open)(const char *filename);
    void (*delete)(struct format *self);
    int (*read)(struct format *self, char *buf, size_t len);
};

如果我想直接从C扩展此接口,它可以很好地工作:

struct derived /* concrete implementation */
{
    struct format base;
};

但我真正想做的是使用ctypes从Python实现这个接口.这是我到目前为止:

CANOPENFUNC   = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p)
#OPENFUNC     = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p)
#OPENFUNC     = ctypes.CFUNCTYPE(ctypes.POINTER( python_format ), ctypes.c_char_p)
#DELETEFUNC   = ctypes.CFUNCTYPE(None, ctypes.c_void_p)
#READFUNC     = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p)

def py_canopen_func( string ):
    print "py_canopen_func", string
    return 1

canopen_func   = CANOPENFUNC(py_canopen_func)
#open_func     = OPENFUNC(  py_open_func)
#delete_func   = DELETEFUNC(py_canopen_func)
#read_func     = READFUNC(py_canopen_func)

class python_format(ctypes.Structure):
  _fields_ = (
    ('can_open',  CANOPENFUNC),
    ('open',      OPENFUNC),
    ('delete',    DELETEFUNC),
    ('read',      READFUNC),
  )
  def __init__(self):
    self.can_open = canopen_func
    OPENFUNC    = ctypes.CFUNCTYPE(ctypes.POINTER(python_format), ctypes.c_char_p)
    def py_open_func2( string ):
      print "py_open_func2", string
      return ctypes.byref(self)
    self.open   = OPENFUNC( py_open_func2 )
    #self.delete = delete_func
    #self.read = read_func

我真的很难在这里为OPENFUNC定义原型.从技术上讲,它应该是:

OPENFUNC    = ctypes.CFUNCTYPE(ctypes.POINTER(python_format), ctypes.c_char_p)

但是我需要首先定义python_format,然后需要定义OPENFUNC.

加分点:实际的功能实现是什么?例如:

def func( str ): return None

要么

def func( str ): i = python_format(); return ctypes.pointer(i)

两个都给了我:

class python_format(ctypes.Structure):
  pass
OPENFUNC = ctypes.CFUNCTYPE(ctypes.POINTER( python_format ), ctypes.c_char_p)
OPENFUNC( func )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: invalid result type for callback function

这与其他issue有关吗?如果是这样,我应该更改我的初始C设计,因为我将无法从回调中返回指向python_format实例的指针?

最佳答案 在ctypes.Structure的文档中,它解释了如何执行此操作:

It is possible to define the _fields_ class variable after the class
statement that defines the Structure subclass, this allows to create
data types that directly or indirectly reference themselves

这意味着您可以添加:

class python_format(ctypes.Structure):  # forward declaration
    pass

然后在定义OPENFUNC(和其他函数类型)之后:

OPENFUNC = ctypes.CFUNCTYPE(ctypes.POINTER( python_format ), ctypes.c_char_p)
DELETEFUNC = etc...

然后能够如此定义python_format._fields_:

python_format._fields_ = (
    ('can_open',  CANOPENFUNC),
    ('open',      OPENFUNC),
    ('delete',    DELETEFUNC),
    ('read',      READFUNC),
  )

这是一个基于您的代码的更完整的示例:

import ctypes

class python_format(ctypes.Structure):  # forward declaration
    pass

CANOPENFUNC = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_char_p)
OPENFUNC = ctypes.PYFUNCTYPE(ctypes.c_int,
                                 ctypes.POINTER(python_format),
                                 ctypes.c_char_p)
DELETEFUNC = ctypes.PYFUNCTYPE(None, ctypes.c_void_p)
READFUNC = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_void_p)

def py_canopen_func(self, string):
    print "py_canopen_func", string
    return 1

def py_open_func(self, string):
    print "py_open_func2", string
    # Return types from callbacks cannot be anything other than simple
    # datatypes (c_int, c_float, ..., c_void_p). For other datatypes
    # (STRUCTURE, POINTER, ...), ctypes returns the following error
    # "Invalid result type for callback function"
    # see http://bugs.python.org/issue5710
    return 1  # can't return ctypes.byref(self)

canopen_func = CANOPENFUNC(py_canopen_func)
open_func = OPENFUNC(py_open_func)
#delete_func = DELETEFUNC(py_canopen_func)
#read_func = READFUNC(py_canopen_func)

class python_format(ctypes.Structure):
    python_format._fields_ = (
        ('can_open', CANOPENFUNC),
        ('open', OPENFUNC),
        ('delete', DELETEFUNC),
        ('read', READFUNC),
      )

    def __init__(self):
        self.can_open = canopen_func
        self.open = open_func
        #self.delete = delete_func
        #self.read = read_func

pf = python_format()
点赞