正如標題所暗示的那樣,我正在嘗試創建一堆屬性,但代碼變得重復且混亂。我想使用closure引數使代碼更緊湊。
根據C API 參考,閉包是一個函式指標,為 getter/setter 提供附加資訊。我還沒有找到它在使用中的例子。
這是我目前使用它的方式:
static void closure_1() {};
static void closure_2() {};
...
static PyObject *
FOO_getter(FOO* self, void *closure) {
if (closure == &closure_1) {
return self->bar_1;
} else if (closure == &closure_2) {
return self->bar_2;
}
}
static int
FOO_setter(FOO* self, PyObject *value, void *closure) {
if (closure == &closure_1) {
if (somehow value is invalid) {
PyErr_SetString(PyExc_ValueError, "invalid value for bar_1.");
return -1;
}
} else if (closure == closure_2) {
if (somehow value is invalid) {
PyErr_SetString(PyExc_ValueError, "invalid value for bar_2.");
return -1;
}
}
return 0;
}
static PyGetSetDef FOO_getsetters[] = {
{"bar_1", (getter) FOO_getter, (setter) FOO_setter, "bar_1 attribute", closure_1},
{"bar_2", (getter) FOO_getter, (setter) FOO_setter, "bar_2 attribute", closure_2},
{NULL} /* Sentinel */
};
...
它以我想要的方式作業,但它看起來更像是一個黑客而不是“pythonic”。有沒有更好的方法來處理這個問題?例如,以某種方式呼叫閉包。
uj5u.com熱心網友回復:
我猜這個“閉包”用于將額外的背景關系傳遞給Foo_getter. 它應該是簡化訪問Foo. 檔案很可能是錯誤的。它應該是“可選指標”,而不是“可選函式指標”。
考慮傳遞成員的偏移量。可以使用 中定義的標準offsetof宏輕松獲得結構成員的偏移量stddef.h。它是一個適合void*輸入的小無符號整數。
static PyGetSetDef FOO_getsetters[] = {
{"bar_1", (getter) FOO_getter, (setter) FOO_setter, "bar_1 attribute", (void*)offsetof(FOO, bar_1)},
{"bar_2", (getter) FOO_getter, (setter) FOO_setter, "bar_2 attribute", (void*)offsetof(FOO, bar_2)},
{NULL} /* Sentinel */
};
現在吸氣劑可能是:
static PyObject *
FOO_getter(FOO* self, void *closure) {
// pointer to location where the FOO's member is stored
char *memb_ptr = (char*)self (size_t)closure;
// cast to `PyObject**` because `mem_ptr` points to location where a pointer to `PyObject` is stored
return *(PyObject**)mem_ptr;
}
對 setter 使用類似的架構。
uj5u.com熱心網友回復:
盡管有檔案,但我假設closure可以是您想要的任何指標。那么傳遞一個“物件”怎么樣,因為 C 不支持閉包(在運行時從字面上生成函式的不足)。
在物件中,我們可以將成員的偏移量存盤在 中FOO,以及指向特定于屬性的驗證器的指標。
typedef int (*Validator)(FOO *, const struct Attribute *, void *);
typedef struct Attribute {
const char *name;
size_t offset;
Validator validator;
} Attribute;
static PyObject **resolve_offset(FOO *self, const Attribute *attr) {
return (PyObject **)( ( (char *)self ) attr->offset );
}
static PyObject *FOO_getter(FOO *self, void *_attr) {
const Attribute *attr = (const Attribute *)_attr;
return *resolve_offset(self, attr);
}
static int FOO_setter(FOO *self, PyObject *val, void *_attr) {
const Attribute *attr = (const Attribute *)_attr;
if (attr->validator(self, attr, val)) {
*resolve_offset(self, attr) = val;
return 0;
} else {
// Building the string to include attr->name is left to you.
PyErr_SetString(PyExc_ValueError, "invalid value.");
return -1;
}
}
static int FOO_bar_1_validator(FOO *self, const Attribute *attr, void *val) { ... }
static int FOO_bar_2_validator(FOO *self, const Attribute *attr, void *val) { ... }
#define ATTRIBUTE(name) \
static Attribute FOO_ ## name ## attr = { \
#name, \
offsetof(FOO, name), \
FOO_ ## name ## _validator \
};
ATTRIBUTE(bar_1);
ATTRIBUTE(bar_2);
#define PY_ATTR_DEF(name) { \
#name, \
(getter)FOO_getter, \
(setter)FOO_setter, \
#name " attribute", \
&(FOO_ ## name ## attr) \
}
static PyGetSetDef FOO_getsetters[] = {
PY_ATTR_DEF(bar_1),
PY_ATTR_DEF(bar_2),
{ NULL }
};
我最初寫道:
resolve_offset肯定依賴于未定義的行為,但它應該可以正常作業。另一種方法是在我們的屬性物件中使用三個函式(get、validate、set)而不是一個函式,但這違背了問題的重點。
但是@tstanisl 指出它看起來不是 UB。驚人的!
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/359883.html
