Merge branch 'pybind:master' into master

This commit is contained in:
Steve R. Sun 2022-02-01 10:14:31 +08:00 committed by GitHub
commit 611c5aecc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 137 additions and 11 deletions

View File

@ -1161,6 +1161,58 @@ error:
.. versionadded:: 2.6
Binding classes with template parameters
========================================
pybind11 can also wrap classes that have template parameters. Consider these classes:
.. code-block:: cpp
struct Cat {};
struct Dog {};
template <typename PetType>
struct Cage {
Cage(PetType& pet);
PetType& get();
};
C++ templates may only be instantiated at compile time, so pybind11 can only
wrap instantiated templated classes. You cannot wrap a non-instantiated template:
.. code-block:: cpp
// BROKEN (this will not compile)
py::class_<Cage>(m, "Cage");
.def("get", &Cage::get);
You must explicitly specify each template/type combination that you want to
wrap separately.
.. code-block:: cpp
// ok
py::class_<Cage<Cat>>(m, "CatCage")
.def("get", &Cage<Cat>::get);
// ok
py::class_<Cage<Dog>>(m, "DogCage")
.def("get", &Cage<Dog>::get);
If your class methods have template parameters you can wrap those as well,
but once again each instantiation must be explicitly specified:
.. code-block:: cpp
typename <typename T>
struct MyClass {
template <typename V>
T fn(V v);
};
py::class<MyClass<int>>(m, "MyClassT")
.def("fn", &MyClass<int>::fn<std::string>);
Custom automatic downcasters
============================

View File

@ -578,3 +578,38 @@ prefers earlier-defined overloads to later-defined ones.
.. versionadded:: 2.6
The ``py::prepend()`` tag.
Binding functions with template parameters
==========================================
You can bind functions that have template parameters. Here's a function:
.. code-block:: cpp
template <typename T>
void set(T t);
C++ templates cannot be instantiated at runtime, so you cannot bind the
non-instantiated function:
.. code-block:: cpp
// BROKEN (this will not compile)
m.def("set", &set);
You must bind each instantiated function template separately. You may bind
each instantiation with the same name, which will be treated the same as
an overloaded function:
.. code-block:: cpp
m.def("set", &set<int>);
m.def("set", &set<std::string>);
Sometimes it's more clear to bind them with separate names, which is also
an option:
.. code-block:: cpp
m.def("setInt", &set<int>);
m.def("setString", &set<std::string>);

View File

@ -200,7 +200,9 @@ struct type_info {
void *get_buffer_data = nullptr;
void *(*module_local_load)(PyObject *, const type_info *) = nullptr;
/* A simple type never occurs as a (direct or indirect) parent
* of a class that makes use of multiple inheritance */
* of a class that makes use of multiple inheritance.
* A type can be simple even if it has non-simple ancestors as long as it has no descendants.
*/
bool simple_type : 1;
/* True if there is no multiple inheritance in this type's inheritance tree */
bool simple_ancestors : 1;
@ -322,7 +324,7 @@ inline bool raise_err(PyObject *exc_type, const char *msg) {
#endif
PyErr_SetString(exc_type, msg);
return false;
};
}
inline void translate_exception(std::exception_ptr p) {
if (!p) {

View File

@ -177,10 +177,10 @@ struct npy_api {
}
bool PyArray_Check_(PyObject *obj) const {
return (bool) PyObject_TypeCheck(obj, PyArray_Type_);
return PyObject_TypeCheck(obj, PyArray_Type_) != 0;
}
bool PyArrayDescr_Check_(PyObject *obj) const {
return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_);
return PyObject_TypeCheck(obj, PyArrayDescr_Type_) != 0;
}
unsigned int (*PyArray_GetNDArrayCFeatureVersion_)();

View File

@ -988,6 +988,13 @@ protected:
}
append_note_if_missing_header_is_suspected(msg);
#if PY_VERSION_HEX >= 0x03030000
// Attach additional error info to the exception if supported
if (PyErr_Occurred()) {
raise_from(PyExc_TypeError, msg.c_str());
return nullptr;
}
#endif
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
}
@ -1206,13 +1213,14 @@ protected:
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
mark_parents_nonsimple(tinfo->type);
tinfo->simple_ancestors = false;
tinfo->simple_type = false;
}
else if (rec.bases.size() == 1) {
auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr());
tinfo->simple_ancestors = parent_tinfo->simple_ancestors;
// a child of a non-simple type can never be a simple type
tinfo->simple_type = parent_tinfo->simple_type;
auto *parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr());
assert(parent_tinfo != nullptr);
bool parent_simple_ancestors = parent_tinfo->simple_ancestors;
tinfo->simple_ancestors = parent_simple_ancestors;
// The parent can no longer be a simple type if it has MI and has a child
parent_tinfo->simple_type = parent_tinfo->simple_type && parent_simple_ancestors;
}
if (rec.module_local) {

View File

@ -146,6 +146,13 @@ with remove_output("pybind11/include", "pybind11/share"):
"-DBUILD_TESTING=OFF",
"-DPYBIND11_NOPYTHON=ON",
]
if "CMAKE_ARGS" in os.environ:
fcommand = [
c
for c in os.environ["CMAKE_ARGS"].split()
if "DCMAKE_INSTALL_PREFIX" not in c
]
cmd += fcommand
cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
subprocess.check_call(cmd, **cmake_opts)
subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)

View File

@ -235,7 +235,7 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// - functions are get_{base}_{var}, return {var}
struct MVB {
MVB() = default;
MVB(const MVB&) = default;
MVB(const MVB &) = default;
virtual ~MVB() = default;
int b = 1;

View File

@ -472,3 +472,25 @@ def test_pr3635_diamond_f():
assert o.get_f_e() == 5
assert o.get_f_f() == 6
def test_python_inherit_from_mi():
"""Tests extending a Python class from a single inheritor of a MI class"""
class PyMVF(m.MVF):
g = 7
def get_g_g(self):
return self.g
o = PyMVF()
assert o.b == 1
assert o.c == 2
assert o.d0 == 3
assert o.d1 == 4
assert o.e == 5
assert o.f == 6
assert o.g == 7
assert o.get_g_g() == 7

View File

@ -110,7 +110,7 @@ if(NOT DEFINED PYTHON_MODULE_EXTENSION)
execute_process(
COMMAND
"${${_Python}_EXECUTABLE}" "-c"
"from distutils import sysconfig as s;print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))"
"import sys, importlib; s = importlib.import_module('distutils.sysconfig' if sys.version_info < (3, 10) else 'sysconfig'); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))"
OUTPUT_VARIABLE _PYTHON_MODULE_EXTENSION
ERROR_VARIABLE _PYTHON_MODULE_EXTENSION_ERR
OUTPUT_STRIP_TRAILING_WHITESPACE)