pybind11使用教程筆記__6_ functions--1. return_balue_policy


閱讀這部分時請確定已經對於基本的函數binding和class binding 有了基礎的瞭解。

Return value policies

python和C++對於管理內存和對象的生存時間非常不同。這樣當在C++和python之間創建binding的時候,如果function返回的類型是a non-trivial type. 只通過類型信息,python無法確定是否管理返回的value並釋放其資源,無法確定其是否在C++側進行處理。因此, pybind11提供了若干return value policy annotations來傳遞給module::def() and class_::def(),默認的是return_value_policy::automatic

Return value policies are tricky, and it’s very important to get them right.
舉簡例說明什麼會出錯:

/* Function declaration */
Data *get_data() { return _data; /* (pointer to a static data structure) */ }
...

/* Binding code */
m.def("get_data", &get_data); // <-- KABOOM, will cause crash when called from Python

當python 端執行 get_data() 時, the return value (a native C++ type) 必須包裹並將其轉換爲python可用類型。這個例子中,默認的 return value policy (return_value_policy::automatic)導致pybind11 to assume ownership of the static _data instance。

如上述由於pybind11認爲擁有C++實例的所屬權,當python的垃圾回收器最終刪除python warper,pybind11會嘗試通過 delete() 刪除C++實例。這個時候程序會崩潰,though errors could also be more subtle and involve silent data corruption.

上例中,應指定return_value_policy::reference,這樣global data instance 只是被引用了,並不涉及所有權的轉移。

m.def("get_data", &get_data, return_value_policy::reference);

另一方面,上面的引用類型不適合許多其他場景,如果忽略ownership可能會導致內存泄漏。作爲一個使用pybind11的開發者,熟悉瞭解不同的return value policies很重要,應該知道什麼情況下使用哪一種return value policy。

return value policy 的簡介:

Return value policy Description
return_value_policy::take_ownership Reference an existing object (i.e. do not create a new copy) and take ownership. Python will call the destructor and delete operator when the object’s reference count reaches zero. Undefined behavior ensues when the C++ side does the same, or when the data was not dynamically allocated.
return_value_policy::copy Create a new copy of the returned object, which will be owned by Python. This policy is comparably safe because the lifetimes of the two instances are decoupled.
return_value_policy::move Use std::move to move the return value contents into a new instance that will be owned by Python. This policy is comparably safe because the lifetimes of the two instances (move source and destination) are decoupled.
return_value_policy::reference Reference an existing object, but do not take ownership. The C++ side is responsible for managing the object’s lifetime and deallocating it when it is no longer used. Warning: undefined behavior will ensue when the C++ side deletes an object that is still referenced and used by Python.
return_value_policy::reference_internal Indicates that the lifetime of the return value is tied to the lifetime of a parent object, namely the implicit this, or self argument of the called method or property. Internally, this policy works just like return_value_policy::reference but additionally applies a keep_alive<0, 1> call policy (described in the next section) that prevents the parent object from being garbage collected as long as the return value is referenced by Python. This is the default policy for property getters created via def_property, def_readwrite, etc.
return_value_policy::automatic Default policy. This policy falls back to the policy return_value_policy::take_ownership when the return value is a pointer. Otherwise, it uses return_value_policy::move or return_value_policy::copy for rvalue and lvalue references, respectively. See above for a description of what all of these different policies do.
return_value_policy::automatic_reference As above, but use policy return_value_policy::reference when the return value is a pointer. This is the default conversion policy for function arguments when calling Python functions manually from C++ code (i.e. via handle::operator()). You probably won’t need to use this.

Return value policies 應用於 properties:

class_<MyClass>(m, "MyClass")
    .def_property("data", &MyClass::getData, &MyClass::setData,
                  py::return_value_policy::copy);

上面的代碼將py::return_value_policy::copy同時應用於getter和setter函數,然而setter函數並不關心return value policy,將return value policy 應用於getter函數可以使用cpp_function constructor:

class_<MyClass>(m, "MyClass")
    .def_property("data"
        py::cpp_function(&MyClass::getData, py::return_value_policy::copy),
        py::cpp_function(&MyClass::setData)
    );

Warning:

無效的return value policy 會引起訪問access uninitialized memory or free data structures multiple times, 這樣的問題會導致難於debug,hard-to-debug non-determinism and segmentation faults。因此非常有必要熟悉瞭解不同的return value policy

Note:

  • 上述的return value policy 只應用於pybind11沒有見過的數據類型,policy會定義返回類型的生存時間和所有權等關鍵信息。When pybind11 knows the instance already (as identified by its type and address in memory), it will return the existing Python object wrapper rather than creating a new copy.

  • 下一節會講述關於函數的調用規則call policy,作爲return value policy的補充。call policy 會涉及reference relationships that can involve both return values and parameters of functions.

  • 除了使用複雜的policy和 lifetime management logic,還可以使用智能指針 smart pointers。smatrt pointers 也可以反應一個object是否從python或C++被引用,這個可以消除因爲各種inconsistency導致的程序崩潰和未定義行爲。對於返回 smart pointer的函數,沒有必要指定return value policy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章