2017-10-19 31 views
1

我已經按照教程https://docs.python.org/2.7/extending/newtypes.html#the-basics在C中創建了自定義Python類型。在我的C中,我收到了一個指向結構體的指針,我希望能夠從Python獲取和設置結構體中的值,而不需要獲取它的副本。即如何將指針存儲在自定義嵌入式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 */ 
}; 
+0

很難從代碼片段弄清楚。什麼是'x'(來自'myObject.x()') - 它看起來像一個方法。還有什麼是'myStruct * s'?看起來你直到最後沒有閱讀_Python_頁面(是的,它很長)。請發佈完整的代碼,並解釋您的問題。順便說一句:_C_模塊編譯?如果是,是否可以在_Python_中導入? – CristiFati

+0

我確實要實現'x'作爲對象的一種方法,這樣做不是我所遇到的問題。假設結構是 typedef struct {x,y; unsigned char a,r,g,b; } 我寫的對象確實編譯了,我可以在Python中創建這些對象並與之交互。我知道如何爲C中的Python對象執行方法。我遇到的問題是如何將該指針放入KeyObject結構中。一旦我創建了KeyObject,我就無法看到如何獲得這個結構。 –

+0

_Python_不知道指針。您應該能夠從_Python_訪問(r/w)內部結構體字段,但它只是一個數字。我在這裏看到一些過度嵌套(如果我正確的話)。一種方法是直接在'KeyObject'中移動'myStruct'字段。 – CristiFati

回答

0

下面是一個例子(cbk。Ç),即可以作爲這個任務的主鏈:

#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 - 相當於__new__的Python) - 初始化它們 - 加入
    • 析構函數(Key_dealloc - 即不正好相反 - - 在等效__del__的Python)加入,以及,以避免內存泄漏(這只是以前的子彈後果)
  • 訪問InnerStructx構件通過Key_getXKey_setX功能(注意,它們在Key_getsets引用)來完成:
    • ,內x構件,將由key_instance.x被訪問,因爲它是一個Key實例屬性
      • 這樣更有意義比使用的吸氣劑(get_x())和一個設置器(​​69),更的Python IC
      • 然而,如果吸氣劑/調節器的方式是優選的,Key_getXKey_setX簽名應該略作修改(I想去掉最後一個參數會做),他們應該在Key_methods被referenceded - 應指定KeyTypetp_methods(在上述網頁還介紹)
    • 當添加新成員InnerStruct,只爲x完成的東西需要被複制和適應(當然,如果有將是顯得過於相似的功能,代碼需要重構 - 但這是外電流範圍)
  • 最後一部分是相當標準的Python擴展模塊代碼

EDIT0:1 ST評論後,好像問題是棘手比它似乎。不知道我是否還是錯了,因爲這看起來不是什麼大不了的事情。改變是(據我所知),inner指針應該來自別的地方(另一個庫(.dll。)),而不是在構造函數中創建。改變爲例如模仿新(希望預期的)行爲:

  • 由於InnerStruct指針由外部庫返回(把它稱爲external.dll),該結構定義在頭文件被移動屬於該庫 - 稱之爲external.h(如下圖),這是由cbk.c
  • 包括會更有意義庫出口的一些數據通過一個函數(也通過庫輸出):getExternalPtr這可能會有爭議 - 目前它只有(虛擬):dummyArg0
  • 由於getExternalPtr內分配內存,將是有意義的具有解除分配(delExternalPtr)相應的功能,以避免內存泄漏和未定義的行爲(例如如果內存分配在一個地方,則釋放到另一個地方,並且這兩個地方應該得到不同的運行時間(C runtimes)。通過getExternalPtr返回的任何指針應該傳給delExternalPtr恰好一次
  • 在2以上的功能現在是從Key_newKey_dealloc調用。如果這仍然不是OK,並且需要經過創作修改(儘管它可能是一些種族問題會出現)的對象,成員可以像做:((KeyObject*)keyInstancePyObjectPtr)->inner = getExternalPtr(0);只有一個陷阱
    • keyInstancePyObjectPtr(這是一個通用的PyObject*)應Key_Type類型。 Key_CheckExact宏觀正是這麼做的檢查
  • 現在,模塊依賴(是鏈接於)在外部 LIB(不知道的東西實際上是如何),但可以這麼改到動態( DLL)裝載(經由[man]: DLOPEN(3)/[man]: DLSYM(3)[MSDN]: LoadLibrary function)/ [MSDN]: GetProcAddress function

    外部庫代碼:

    • external.h

      #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 
      
    • external.c

      #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); 
      } 
      


測試程序(ckb_test。PY):

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" 

輸出

c:\Work\Dev\StackOverflow\q46833364>set PATH=%PATH%;.\external\Win32-Release 

c:\Work\Dev\StackOverflow\q46833364>set PYTHONPATH=%PYTHONPATH%;.\ckb\Win32-Release 

c:\Work\Dev\StackOverflow\q46833364\>"c:\Install\x86\HPE\OPSWpython\2.7.10__00\python.exe" ckb_test.py 

Module: <module 'ckb' from 'c:\Work\Dev\StackOverflow\q46833364\ckb\Win32-Release\ckb.pyd'> 
Dir: ['Key', '__doc__', '__file__', '__name__', '__package__'] 

Class: <type 'ckb.Key'> 
Dir: ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'x'] 

Instance: <ckb.Key object at 0x027A7050> 
Dir: ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'x'] 

Key.x (initial): 1618 
Key.x (modified): 123 
Traceback (most recent call last): 
    File "..\ckb_test.py", line 20, in <module> 
    key.x = 1.0 
TypeError: 'x' value must be an int 

End 
+0

這是我用自己提供的資源爲自己做了多少。我顯然沒有說清楚。我遇到的問題是,指向結構的指針由外部庫提供,因此無法在Key_new函數中分配。我在C中有一個關鍵對象的實例,它的類型是'PyObject *',因爲這是C API用來與python對象交互的東西,我的問題是如何從外部分配內部變量對象被構造。這種C,Python交互沒有記錄在教程頁面上。 –

+0

然後不會將'PyObject'強制轉換爲'Key'並將外部結構指針賦值給'inner'成員工作? (例如'((Key *)keyInstancePyObject) - > inner = externalInnerStructPointer')工作嗎?是否需要使用該外部指針(或者您可以創建另一個外部指針並使用外部指針的值填充其成員)?恐怕我仍然錯過了一些東西,或者它可能是一個_XY_問題。 – CristiFati

+0

Aaaah是的,這確實是我需要的。我沒有意識到你可以將'PyObject'強制轉換爲'Key'對象。 –

相關問題