mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-27 07:32:02 +00:00
Merge branch 'pybind:master' into master
This commit is contained in:
commit
611c5aecc2
@ -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
|
||||
============================
|
||||
|
||||
|
@ -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>);
|
||||
|
@ -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) {
|
||||
|
@ -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_)();
|
||||
|
@ -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) {
|
||||
|
7
setup.py
7
setup.py
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user