如何将指针存储在自定义嵌入式Python对象中

时间:2017-10-19 15:19:47

标签: python c python-2.7 pointers

我已根据教程https://docs.python.org/2.7/extending/newtypes.html#the-basics在C中创建了一个自定义Python类型。在我的C中,我收到一个指向结构的指针,我希望能够从Python中获取并设置结构中的值,而无需复制它。即。

a = myObject.x() # gets the x value in the struct.

myObject.x(255) # sets the x value in the struct.

但是我看不到如何将指针存储在python对象中。

我当前的对象定义目前只是python网站的基本对象实现。

typedef struct {
    PyObject_HEAD
    myStruct *s;
} KeyObject;

static PyTypeObject KeyType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "ckb.Key",             /* tp_name */
    sizeof(KeyObject), /* tp_basicsize */
    0,                         /* tp_itemsize */
    0,                         /* tp_dealloc */
    0,                         /* tp_print */
    0,                         /* tp_getattr */
    0,                         /* tp_setattr */
    0,                         /* tp_compare */
    0,                         /* tp_repr */
    0,                         /* tp_as_number */
    0,                         /* tp_as_sequence */
    0,                         /* tp_as_mapping */
    0,                         /* tp_hash */
    0,                         /* tp_call */
    0,                         /* tp_str */
    0,                         /* tp_getattro */
    0,                         /* tp_setattro */
    0,                         /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,        /* tp_flags */
    "Key objects",           /* tp_doc */
};

static PyMethodDef key_methods[] = {
    {NULL}  /* Sentinel */
};

1 个答案:

答案 0 :(得分:0)

以下是一个示例( cbk.c ),它可以作为此任务的主干:

#include "external.h"
#include "Python.h"

#define MOD_NAME "ckb"
#define KEY_CLASS_NAME "Key"


/*
typedef struct InnerStruct_tag {
    int x;
} InnerStruct;
//*/

typedef struct KeyObject_tag {
    PyObject_HEAD
    InnerStruct *inner;
} KeyObject;


static PyObject *Key_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
    KeyObject *self;
    self = (KeyObject*)type->tp_alloc(type, 0);
    if (self != NULL) {
        //self->inner = (InnerStruct*)calloc(1, sizeof(Key));
        self->inner = getExternalPtr(1234);  // Don't allocate here, get the pointer from external lib
        if (self->inner == NULL) {
            Py_DECREF(self);
            return NULL;
        }
    }
    return (PyObject*)self;
}

static void Key_dealloc(KeyObject *self) {
    //free(self->inner);
    delExternalPtr(self->inner);  // Use the external dellocation function (optional)
    Py_TYPE(self)->tp_free((PyObject*)self);
}


static PyObject *Key_getX(KeyObject *self, void *closure) {
    return PyInt_FromLong(self->inner->x);
}

static int Key_setX(KeyObject *self, PyObject *value, void *closure) {
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "Cannot delete 'x'");
        return -1;
    }
    if (!PyInt_Check(value)) {
        PyErr_SetString(PyExc_TypeError, "'x' value must be an int");
        return -1;
    }
    self->inner->x = ((PyIntObject*)value)->ob_ival;
    return 0;
}

static PyGetSetDef Key_getsets[] = {
    {"x", (getter)Key_getX, (setter)Key_setX, "x", NULL},
    {NULL}  // Sentinel
};


static PyTypeObject Key_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    MOD_NAME"."KEY_CLASS_NAME, /* tp_name */
    sizeof(KeyObject),         /* tp_basicsize */
    0,                         /* tp_itemsize */
    (destructor)Key_dealloc,   /* tp_dealloc */
    0,                         /* tp_print */
    0,                         /* tp_getattr */
    0,                         /* tp_setattr */
    0,                         /* tp_compare */
    0,                         /* tp_repr */
    0,                         /* tp_as_number */
    0,                         /* tp_as_sequence */
    0,                         /* tp_as_mapping */
    0,                         /* tp_hash */
    0,                         /* tp_call */
    0,                         /* tp_str */
    0,                         /* tp_getattro */
    0,                         /* tp_setattro */
    0,                         /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT |
        Py_TPFLAGS_BASETYPE,   /* tp_flags */
    KEY_CLASS_NAME" objects",  /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    0,                         /* tp_methods */
    0,                         /* tp_members */
    Key_getsets,               /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    Key_new,                   /* tp_new */
};

#define Key_CheckExact(op) ((op)->ob_type == &Key_Type)


static PyMethodDef module_methods[] = {
    {NULL}  // Sentinel
};

PyMODINIT_FUNC initckb(void) {
    PyObject* m;
    if (PyType_Ready(&Key_Type) < 0)
        return;
    m = Py_InitModule3(MOD_NAME, module_methods,
        MOD_NAME": Example module that creates an extension type ("KEY_CLASS_NAME").");
    Py_INCREF(&Key_Type);
    PyModule_AddObject(m, KEY_CLASS_NAME, (PyObject*)&Key_Type);
}

备注

  • 为清晰起见,已重命名(重新组织)现有结构名称/成员
  • 内部结构只有一个成员(x),足以成为一个点
  • 所有内容都依赖[Python]: Defining New Types页面上的 (问题中也提到了这一点)
  • 由于包装器对象(Key)包含指针(inner),因此为了避免每次访问时都检查NULL
    • 添加了一个构造函数(Key_new - 相当于 Python中的__new__ ) - 初始化它们 -
    • 析构函数(Key_dealloc - Python 中的等效__del__ - 完全相反 - 也被添加,以避免内存泄漏(这只是以前的子弹后果)
  • InnerStruct x成员访问权限是通过Key_getXKey_setX函数完成的(请注意Key_getsets中引用它们):
    • 来自 Python ,内部x成员将由key_instance.x访问,因为它是Key实例属性
      • 这种方式比使用getter(get_x())和setter(set_x(value))更有意义,而且更多是 Python ic
      • 如果首选getter / setter方式,Key_getXKey_setX签名应略微修改(我认为删除最后一个参数会这样做),并且应该在{{1}中引用它们} - 应该将Key_methods指定为 tp_methods (也在上面的网页中描述)
    • KeyType添加新成员时,只需要复制和调整InnerStruct所需的内容(当然,如果有类似的函数,代码应该重构 - 但是这样超出当前范围)
  • 最后一部分是非常标准的 Python 扩展模块代码

EDIT0 :在1 st 评论之后,似乎问题比看起来更棘手。不确定我是否仍然弄错了,因为它似乎没什么大不了的。 (据我所知),更改是x指针应来自其他位置(另一个库( .dll )),而不是在构造函数中创建。更改为示例以模仿新的(并且希望预期的)行为:

  • 由于外部库(称为 external.dll )返回inner指针,因此结构定义在属于该库的头文件中移动 - 称为 external.h (下),包含在 cbk.c
  • 有意义的是,库通过函数导出一些数据(也由库导出):InnerStruct可能需要参数 - 目前它只有(虚拟)一个:getExternalPtr
  • 由于dummyArg0内部分配内存,因此有一个相应的函数可以解除分配它(getExternalPtr),以避免内存泄漏和未定义的行为(例如如果在一个地方分配了内存,在另一个地方分配了内存,并且这两个地方是由不同的 C 运行时分配的。 delExternalPtr返回的任何指针都应该传递给getExternalPtr 一次
  • 现在将从delExternalPtrKey_new调用上述2个函数。如果这仍然不是 OK ,并且在创建后需要修改对象(虽然可能会出现一些种族问题),可以设置成员喜欢:Key_dealloc只有一次捕获
    • ((KeyObject*)keyInstancePyObjectPtr)->inner = getExternalPtr(0);(通用keyInstancePyObjectPtr)应为PyObject*类型。 Key_Type宏完全检查
  • 现在,模块依赖于链接外部 lib(不确定实际情况如何),但可以更改为< em>动态SO(DLL)加载(通过[man]: DLOPEN(3) / [man]: DLSYM(3)[MSDN]: LoadLibrary function)/ [MSDN]: GetProcAddress function

    外部库代码:

    • external.h

      Key_CheckExact
    • external.c

      #if defined (WIN32)
      #  if defined (EXTERNAL_DYNAMIC)
      #    if defined EXTERNAL_EXPORTS
      #      define EXTERNAL_EXPORT __declspec(dllexport)
      #    else
      #      define EXTERNAL_EXPORT __declspec(dllimport)
      #    endif
      #  else
      #    define EXTERNAL_EXPORT
      #  endif
      #else
      #  define EXTERNAL_EXPORT
      #endif
      
      
      typedef struct InnerStruct_tag {
          int x;
      } InnerStruct;
      
      
      #if defined (__cplusplus)
      extern "C" {
      #endif
      
      EXTERNAL_EXPORT InnerStruct *getExternalPtr(int dummyArg0);
      EXTERNAL_EXPORT void delExternalPtr(InnerStruct *ptr);
      
      #if defined (__cplusplus)
      }
      #endif
      


测试程序( ckb_test.py ):

#include "external.h"
#include <stdlib.h>


InnerStruct *getExternalPtr(int dummyArg0) {
    InnerStruct *ret = (InnerStruct*)malloc(sizeof(InnerStruct));
    if (ret != NULL)
        ret->x = 1618;
    return ret;
}

void delExternalPtr(InnerStruct *ptr) {
    free(ptr);
}

<强>输出

import traceback
import ckb

print "\nModule:", ckb
print "Dir:", dir(ckb)

print "\nClass:", ckb.Key
print "Dir:", dir(ckb.Key)

key = ckb.Key()
print "\nInstance:", key
print "Dir:", dir(key)

print "\nKey.x (initial):", key.x
key.x = 123
print "Key.x (modified):", key.x

try:
    key.x = 1.0
except:
    traceback.print_exc()

del(key)
print "\nEnd"