Unverified Commit dac74ebd authored by Aaron Gokaslan's avatar Aaron Gokaslan Committed by GitHub
Browse files

fix(clang-tidy): performance fixes applied in tests and CI (#3051)

* Initial fixes

* Whoops

* Finish clang-tidy manual fixes

* Add two missing fixes

* Revert

* Update clang-tidy

* Try to fix unreachable code error

* Move nolint comment

* Apply missing fix

* Don't override clang-tidy config

* Does this fix clang-tidy?

* Make all clang-tidy errors visible

* Add comments about NOLINTs and remove a few

* Fix typo
parent 3b30b0a5
......@@ -18,6 +18,7 @@ modernize-use-noexcept,
modernize-use-emplace,
modernize-use-override,
modernize-use-using,
*performance*,
readability-container-size-empty,
readability-make-member-function-const,
readability-redundant-function-ptr-dereference,
......
......@@ -27,7 +27,7 @@ jobs:
clang-tidy:
name: Clang-Tidy
runs-on: ubuntu-latest
container: silkeh/clang:10
container: silkeh/clang:12
steps:
- uses: actions/checkout@v2
......@@ -37,10 +37,10 @@ jobs:
- name: Configure
run: >
cmake -S . -B build
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--warnings-as-errors=*"
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy)"
-DDOWNLOAD_EIGEN=ON
-DDOWNLOAD_CATCH=ON
-DCMAKE_CXX_STANDARD=17
- name: Build
run: cmake --build build -j 2
run: cmake --build build -j 2 -- --keep-going
......@@ -262,7 +262,7 @@ add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
return class_<detail::OstreamRedirect>(std::move(m), name.c_str(), module_local())
.def(init<bool, bool>(), arg("stdout") = true, arg("stderr") = true)
.def("__enter__", &detail::OstreamRedirect::enter)
.def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); });
.def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
}
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
......@@ -1629,12 +1629,13 @@ struct enum_base {
auto static_property = handle((PyObject *) get_internals().static_property_type);
m_base.attr("__repr__") = cpp_function(
[](object arg) -> str {
[](const object &arg) -> str {
handle type = type::handle_of(arg);
object type_name = type.attr("__name__");
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
}, name("__repr__"), is_method(m_base)
);
},
name("__repr__"),
is_method(m_base));
m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base)));
......@@ -1718,8 +1719,10 @@ struct enum_base {
PYBIND11_ENUM_OP_CONV("__ror__", a | b);
PYBIND11_ENUM_OP_CONV("__xor__", a ^ b);
PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b);
m_base.attr("__invert__") = cpp_function(
[](object arg) { return ~(int_(arg)); }, name("__invert__"), is_method(m_base));
m_base.attr("__invert__")
= cpp_function([](const object &arg) { return ~(int_(arg)); },
name("__invert__"),
is_method(m_base));
}
} else {
PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
......@@ -1740,10 +1743,10 @@ struct enum_base {
#undef PYBIND11_ENUM_OP_STRICT
m_base.attr("__getstate__") = cpp_function(
[](object arg) { return int_(arg); }, name("__getstate__"), is_method(m_base));
[](const object &arg) { return int_(arg); }, name("__getstate__"), is_method(m_base));
m_base.attr("__hash__") = cpp_function(
[](object arg) { return int_(arg); }, name("__hash__"), is_method(m_base));
[](const object &arg) { return int_(arg); }, name("__hash__"), is_method(m_base));
}
PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) {
......
#pragma once
#include <utility>
#include "pybind11_tests.h"
/// Simple class used to test py::local:
......@@ -54,7 +56,7 @@ py::class_<T> bind_local(Args && ...args) {
namespace pets {
class Pet {
public:
Pet(std::string name) : name_(name) {}
Pet(std::string name) : name_(std::move(name)) {}
std::string name_;
const std::string &name() const { return name_; }
};
......
......@@ -81,7 +81,7 @@ public:
}
/// Move constructor
ref(ref &&r) : m_ptr(r.m_ptr) {
ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
r.m_ptr = nullptr;
print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
......@@ -96,7 +96,7 @@ public:
}
/// Move another reference into the current one
ref& operator=(ref&& r) {
ref &operator=(ref &&r) noexcept {
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
if (*this == r)
......
......@@ -104,6 +104,8 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("return_self", [](LocalVec *v) { return v; });
m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); });
// Changing this broke things with pygrep. TODO fix
// NOLINTNEXTLINE
class Dog : public pets::Pet { public: Dog(std::string name) : Pet(name) {}; };
py::class_<pets::Pet>(m, "Pet", py::module_local())
.def("name", &pets::Pet::name);
......@@ -126,6 +128,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_missing_header_message
// The main module already includes stl.h, but we need to test the error message
// which appears when this header is missing.
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("missing_header_arg", [](std::vector<float>) { });
m.def("missing_header_return", []() { return std::vector<float>(); });
}
......@@ -27,7 +27,7 @@ TEST_SUBMODULE(buffers, m) {
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
}
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
Matrix(Matrix &&s) noexcept : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
print_move_created(this);
s.m_rows = 0;
s.m_cols = 0;
......@@ -49,7 +49,7 @@ TEST_SUBMODULE(buffers, m) {
return *this;
}
Matrix &operator=(Matrix &&s) {
Matrix &operator=(Matrix &&s) noexcept {
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
if (&s != this) {
delete[] m_data;
......@@ -79,6 +79,7 @@ TEST_SUBMODULE(buffers, m) {
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
.def(py::init<py::ssize_t, py::ssize_t>())
/// Construct from a buffer
// NOLINTNEXTLINE(performance-unnecessary-value-param)
.def(py::init([](py::buffer const b) {
py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
......@@ -89,31 +90,31 @@ TEST_SUBMODULE(buffers, m) {
return v;
}))
.def("rows", &Matrix::rows)
.def("cols", &Matrix::cols)
.def("rows", &Matrix::rows)
.def("cols", &Matrix::cols)
/// Bare bones interface
.def("__getitem__", [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error();
return m(i.first, i.second);
})
.def("__setitem__", [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error();
m(i.first, i.second) = v;
})
/// Provide buffer access
.def_buffer([](Matrix &m) -> py::buffer_info {
.def("__getitem__",
[](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error();
return m(i.first, i.second);
})
.def("__setitem__",
[](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error();
m(i.first, i.second) = v;
})
/// Provide buffer access
.def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info(
m.data(), /* Pointer to buffer */
{ m.rows(), m.cols() }, /* Buffer dimensions */
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
sizeof(float) }
);
})
;
});
// test_inherited_protocol
class SquareMatrix : public Matrix {
......@@ -208,7 +209,5 @@ TEST_SUBMODULE(buffers, m) {
})
;
m.def("get_buffer_info", [](py::buffer buffer) {
return buffer.request();
});
m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
}
......@@ -30,7 +30,11 @@ class type_caster<ConstRefCasted> {
// cast operator.
bool load(handle, bool) { return true; }
operator ConstRefCasted&&() { value = {1}; return std::move(value); }
operator ConstRefCasted &&() {
value = {1};
// NOLINTNEXTLINE(performance-move-const-arg)
return std::move(value);
}
operator ConstRefCasted&() { value = {2}; return value; }
operator ConstRefCasted*() { value = {3}; return &value; }
......@@ -101,6 +105,7 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_bytes_to_string
m.def("strlen", [](char *s) { return strlen(s); });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("string_length", [](std::string s) { return s.length(); });
#ifdef PYBIND11_HAS_U8STRING
......@@ -146,6 +151,7 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
// test_tuple
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("pair_passthrough", [](std::pair<bool, std::string> input) {
return std::make_pair(input.second, input.first);
}, "Return a pair in reversed order");
......@@ -177,10 +183,13 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_none_deferred
m.def("defer_none_cstring", [](char *) { return false; });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("defer_none_cstring", [](py::none) { return true; });
m.def("defer_none_custom", [](UserType *) { return false; });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("defer_none_custom", [](py::none) { return true; });
m.def("nodefer_none_void", [](void *) { return true; });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("nodefer_none_void", [](py::none) { return false; });
// test_void_caster
......@@ -231,6 +240,7 @@ TEST_SUBMODULE(builtin_casters, m) {
}, "copy"_a);
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("refwrap_call_iiw", [](IncType &w, py::function f) {
py::list l;
l.append(f(std::ref(w)));
......
......@@ -17,8 +17,8 @@ int dummy_function(int i) { return i + 1; }
TEST_SUBMODULE(callbacks, m) {
// test_callbacks, test_function_signatures
m.def("test_callback1", [](py::object func) { return func(); });
m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); });
m.def("test_callback1", [](const py::object &func) { return func(); });
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
m.def("test_callback3", [](const std::function<int(int)> &func) {
return "func(43) = " + std::to_string(func(43)); });
m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; });
......@@ -27,51 +27,48 @@ TEST_SUBMODULE(callbacks, m) {
});
// test_keyword_args_and_generalized_unpacking
m.def("test_tuple_unpacking", [](py::function f) {
m.def("test_tuple_unpacking", [](const py::function &f) {
auto t1 = py::make_tuple(2, 3);
auto t2 = py::make_tuple(5, 6);
return f("positional", 1, *t1, 4, *t2);
});
m.def("test_dict_unpacking", [](py::function f) {
m.def("test_dict_unpacking", [](const py::function &f) {
auto d1 = py::dict("key"_a="value", "a"_a=1);
auto d2 = py::dict();
auto d3 = py::dict("b"_a=2);
return f("positional", 1, **d1, **d2, **d3);
});
m.def("test_keyword_args", [](py::function f) {
return f("x"_a=10, "y"_a=20);
});
m.def("test_keyword_args", [](const py::function &f) { return f("x"_a = 10, "y"_a = 20); });
m.def("test_unpacking_and_keywords1", [](py::function f) {
m.def("test_unpacking_and_keywords1", [](const py::function &f) {
auto args = py::make_tuple(2);
auto kwargs = py::dict("d"_a=4);
return f(1, *args, "c"_a=3, **kwargs);
});
m.def("test_unpacking_and_keywords2", [](py::function f) {
m.def("test_unpacking_and_keywords2", [](const py::function &f) {
auto kwargs1 = py::dict("a"_a=1);
auto kwargs2 = py::dict("c"_a=3, "d"_a=4);
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
});
m.def("test_unpacking_error1", [](py::function f) {
m.def("test_unpacking_error1", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3);
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
});
m.def("test_unpacking_error2", [](py::function f) {
m.def("test_unpacking_error2", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3);
return f(**kwargs, "x"_a=1); // duplicate keyword after **
});
m.def("test_arg_conversion_error1", [](py::function f) {
f(234, UnregisteredType(), "kw"_a=567);
});
m.def("test_arg_conversion_error1",
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
m.def("test_arg_conversion_error2", [](py::function f) {
m.def("test_arg_conversion_error2", [](const py::function &f) {
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
});
......@@ -80,7 +77,7 @@ TEST_SUBMODULE(callbacks, m) {
Payload() { print_default_created(this); }
~Payload() { print_destroyed(this); }
Payload(const Payload &) { print_copy_created(this); }
Payload(Payload &&) { print_move_created(this); }
Payload(Payload &&) noexcept { print_move_created(this); }
};
// Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>);
......@@ -127,7 +124,8 @@ TEST_SUBMODULE(callbacks, m) {
virtual ~AbstractBase() {}; // NOLINT(modernize-use-equals-default)
virtual unsigned int func() = 0;
};
m.def("func_accepting_func_accepting_base", [](std::function<double(AbstractBase&)>) { });
m.def("func_accepting_func_accepting_base",
[](const std::function<double(AbstractBase &)> &) {});
struct MovableObject {
bool valid = true;
......@@ -135,8 +133,8 @@ TEST_SUBMODULE(callbacks, m) {
MovableObject() = default;
MovableObject(const MovableObject &) = default;
MovableObject &operator=(const MovableObject &) = default;
MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; }
MovableObject &operator=(MovableObject &&o) {
MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; }
MovableObject &operator=(MovableObject &&o) noexcept {
valid = o.valid;
o.valid = false;
return *this;
......@@ -145,7 +143,7 @@ TEST_SUBMODULE(callbacks, m) {
py::class_<MovableObject>(m, "MovableObject");
// test_movable_object
m.def("callback_with_movable", [](std::function<void(MovableObject &)> f) {
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
auto x = MovableObject();
f(x); // lvalue reference shouldn't move out object
return x.valid; // must still return `true`
......@@ -159,7 +157,7 @@ TEST_SUBMODULE(callbacks, m) {
// test async Python callbacks
using callback_f = std::function<void(int)>;
m.def("test_async_callback", [](callback_f f, py::list work) {
m.def("test_async_callback", [](const callback_f &f, const py::list &work) {
// make detached thread that calls `f` with piece of work after a little delay
auto start_f = [f](int j) {
auto invoke_f = [f, j] {
......@@ -175,7 +173,7 @@ TEST_SUBMODULE(callbacks, m) {
start_f(py::cast<int>(i));
});
m.def("callback_num_times", [](py::function f, std::size_t num) {
m.def("callback_num_times", [](const py::function &f, std::size_t num) {
for (std::size_t i = 0; i < num; i++) {
f();
}
......
......@@ -19,6 +19,8 @@
#include "local_bindings.h"
#include <pybind11/stl.h>
#include <utility>
#if defined(_MSC_VER)
# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
#endif
......@@ -129,6 +131,7 @@ TEST_SUBMODULE(class_, m) {
m.def("return_none", []() -> BaseClass* { return nullptr; });
// test_isinstance
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("check_instances", [](py::list l) {
return py::make_tuple(
py::isinstance<py::tuple>(l[0]),
......@@ -155,17 +158,13 @@ TEST_SUBMODULE(class_, m) {
return py::type::of<Invalid>();
});
m.def("get_type_of", [](py::object ob) {
return py::type::of(ob);
});
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
m.def("get_type_classic", [](py::handle h) {
return h.get_type();
});
m.def("as_type", [](py::object ob) {
return py::type(ob);
});
m.def("as_type", [](const py::object &ob) { return py::type(ob); });
// test_mismatched_holder
struct MismatchBase1 { };
......@@ -219,6 +218,7 @@ TEST_SUBMODULE(class_, m) {
py::implicitly_convertible<UserType, ConvertibleFromUserType>();
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("implicitly_convert_variable", [](py::object o) {
// `o` is `UserType` and `r` is a reference to a temporary created by implicit
// conversion. This is valid when called inside a bound function because the temp
......@@ -397,6 +397,7 @@ TEST_SUBMODULE(class_, m) {
struct StringWrapper { std::string str; };
m.def("test_error_after_conversions", [](int) {});
m.def("test_error_after_conversions",
// NOLINTNEXTLINE(performance-unnecessary-value-param)
[](StringWrapper) -> NotRegistered { return {}; });
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
py::implicitly_convertible<std::string, StringWrapper>();
......@@ -461,19 +462,20 @@ TEST_SUBMODULE(class_, m) {
struct OtherDuplicate {};
struct DuplicateNested {};
struct OtherDuplicateNested {};
m.def("register_duplicate_class_name", [](py::module_ m) {
m.def("register_duplicate_class_name", [](const py::module_ &m) {
py::class_<Duplicate>(m, "Duplicate");
py::class_<OtherDuplicate>(m, "Duplicate");
});
m.def("register_duplicate_class_type", [](py::module_ m) {
m.def("register_duplicate_class_type", [](const py::module_ &m) {
py::class_<OtherDuplicate>(m, "OtherDuplicate");
py::class_<OtherDuplicate>(m, "YetAnotherDuplicate");
});
m.def("register_duplicate_nested_class_name", [](py::object gt) {
m.def("register_duplicate_nested_class_name", [](const py::object &gt) {
py::class_<DuplicateNested>(gt, "DuplicateNested");
py::class_<OtherDuplicateNested>(gt, "DuplicateNested");
});
m.def("register_duplicate_nested_class_type", [](py::object gt) {
m.def("register_duplicate_nested_class_type", [](const py::object &gt) {
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
});
......
......@@ -34,7 +34,7 @@ py::bytes return_bytes() {
return std::string(data, 4);
}
std::string print_bytes(py::bytes bytes) {
std::string print_bytes(const py::bytes &bytes) {
std::string ret = "bytes[";
const auto value = static_cast<std::string>(bytes);
for (size_t i = 0; i < value.length(); ++i) {
......@@ -146,8 +146,13 @@ TEST_SUBMODULE(constants_and_functions, m) {
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
m.def("should_raise", [capture](int) { return capture.zeros[9] + 33; }, py::kw_only(), py::arg());
});
m.def("register_with_raising_repr", [](py::module_ m, py::object default_value) {
m.def("should_raise", [](int, int, py::object) { return 42; }, "some docstring",
py::arg_v("x", 42), py::arg_v("y", 42, "<the answer>"), py::arg_v("z", default_value));
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
m.def(
"should_raise",
[](int, int, const py::object &) { return 42; },
"some docstring",
py::arg_v("x", 42),
py::arg_v("y", 42, "<the answer>"),
py::arg_v("z", default_value));
});
}
......@@ -37,9 +37,16 @@ template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
class MoveOnlyInt {
public:
MoveOnlyInt() { print_default_created(this); }
MoveOnlyInt(int v) : value{std::move(v)} { print_created(this, value); }
MoveOnlyInt(MoveOnlyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); }
MoveOnlyInt &operator=(MoveOnlyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; }
MoveOnlyInt(int v) : value{v} { print_created(this, value); }
MoveOnlyInt(MoveOnlyInt &&m) noexcept {
print_move_created(this, m.value);
std::swap(value, m.value);
}
MoveOnlyInt &operator=(MoveOnlyInt &&m) noexcept {
print_move_assigned(this, m.value);
std::swap(value, m.value);
return *this;
}
MoveOnlyInt(const MoveOnlyInt &) = delete;
MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
~MoveOnlyInt() { print_destroyed(this); }
......@@ -49,9 +56,16 @@ public:
class MoveOrCopyInt {
public:
MoveOrCopyInt() { print_default_created(this); }
MoveOrCopyInt(int v) : value{std::move(v)} { print_created(this, value); }
MoveOrCopyInt(MoveOrCopyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); }
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; }
MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
print_move_created(this, m.value);
std::swap(value, m.value);
}
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) noexcept {
print_move_assigned(this, m.value);
std::swap(value, m.value);
return *this;
}
MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; }
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
~MoveOrCopyInt() { print_destroyed(this); }
......@@ -61,7 +75,7 @@ public:
class CopyOnlyInt {
public:
CopyOnlyInt() { print_default_created(this); }
CopyOnlyInt(int v) : value{std::move(v)} { print_created(this, value); }
CopyOnlyInt(int v) : value{v} { print_created(this, value); }
CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; }
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
~CopyOnlyInt() { print_destroyed(this); }
......@@ -111,6 +125,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
py::return_value_policy::move);
// test_move_and_copy_casts
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("move_and_copy_casts", [](py::object o) {
int r = 0;
r += py::cast<MoveOrCopyInt>(o).value; /* moves */
......@@ -126,7 +141,9 @@ TEST_SUBMODULE(copy_move_policies, m) {
// test_move_and_copy_loads
m.def("move_only", [](MoveOnlyInt m) { return m.value; });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; });
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
return p.first.value + p.second.value;
......
......@@ -67,9 +67,12 @@ public:
DestructionTester() { print_default_created(this); }
~DestructionTester() { print_destroyed(this); }
DestructionTester(const DestructionTester &) { print_copy_created(this); }
DestructionTester(DestructionTester &&) { print_move_created(this); }
DestructionTester(DestructionTester &&) noexcept { print_move_created(this); }
DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; }
DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; }
DestructionTester &operator=(DestructionTester &&) noexcept {
print_move_assigned(this);
return *this;
}
};
namespace pybind11 { namespace detail {
template <> struct type_caster<DestructionTester> {
......@@ -94,7 +97,11 @@ TEST_SUBMODULE(custom_type_casters, m) {
class ArgInspector {
public:
ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) {
std::string g(const ArgInspector1 &a,
const ArgInspector1 &b,
int c,
ArgInspector2 *d,
ArgAlwaysConverts) {
return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
}
static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
......@@ -106,8 +113,14 @@ TEST_SUBMODULE(custom_type_casters, m) {
.def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts())
;
m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; },
py::arg{}.noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts());
m.def(
"arg_inspect_func",
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
return a.arg + "\n" + b.arg;
},
py::arg{}.noconvert(false),
py::arg_v(nullptr, ArgInspector1()).noconvert(true),
py::arg() = ArgAlwaysConverts());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
......
......@@ -54,8 +54,7 @@ void reset_refs() {
}
// Returns element 2,1 from a matrix (used to test copy/nocopy)
double get_elem(Eigen::Ref<const Eigen::MatrixXd> m) { return m(2, 1); };
double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); };
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
// reference is referencing rows/columns correctly).
......@@ -94,14 +93,16 @@ TEST_SUBMODULE(eigen, m) {
m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; });
m.def("double_threec", [](py::EigenDRef<Eigen::Vector3f> x) { x *= 2; });
m.def("double_threer", [](py::EigenDRef<Eigen::RowVector3f> x) { x *= 2; });
m.def("double_mat_cm", [](Eigen::MatrixXf x) -> Eigen::MatrixXf { return 2.0f * x; });
m.def("double_mat_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; });
m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; });
m.def("double_mat_rm", [](const DenseMatrixR &x) -> DenseMatrixR { return 2.0f * x; });
// test_eigen_ref_to_python
// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("cholesky1", [](Eigen::Ref<MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
m.def("cholesky3", [](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("cholesky4", [](Eigen::Ref<const MatrixXdR> x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
// test_eigen_ref_mutators
......@@ -247,8 +248,11 @@ TEST_SUBMODULE(eigen, m) {
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });
m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; });
// test_mutator_descriptors
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("fixed_mutator_r", [](Eigen::Ref<FixedMatrixR>) {});
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("fixed_mutator_c", [](Eigen::Ref<FixedMatrixC>) {});
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("fixed_mutator_a", [](py::EigenDRef<FixedMatrixC>) {});
// test_dense
m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); });
......@@ -280,6 +284,7 @@ TEST_SUBMODULE(eigen, m) {
// that would allow copying (if types or strides don't match) for comparison:
m.def("get_elem", &get_elem);
// Now this alternative that calls the tells pybind to fail rather than copy:
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("get_elem_nocopy", [](Eigen::Ref<const Eigen::MatrixXd> m) -> double { return get_elem(m); },
py::arg{}.noconvert());
// Also test a row-major-only no-copy const ref:
......@@ -295,13 +300,16 @@ TEST_SUBMODULE(eigen, m) {
// test_issue1105
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense
// eigen Vector or RowVector, the argument would fail to load because the numpy copy would fail:
// numpy won't broadcast a Nx1 into a 1-dimensional vector.
// eigen Vector or RowVector, the argument would fail to load because the numpy copy would
// fail: numpy won't broadcast a Nx1 into a 1-dimensional vector. NOLINTNEXTLINE
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("iss1105_col", [](Eigen::VectorXd) { return true; });
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("iss1105_row", [](Eigen::RowVectorXd) { return true; });
// test_named_arguments
// Make sure named arguments are working properly:
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("matrix_multiply", [](const py::EigenDRef<const Eigen::MatrixXd> A, const py::EigenDRef<const Eigen::MatrixXd> B)
-> Eigen::MatrixXd {
if (A.cols() != B.rows()) throw std::domain_error("Nonconformable matrices!");
......@@ -318,6 +326,7 @@ TEST_SUBMODULE(eigen, m) {
// In case of a failure (the caster's temp array does not live long enough), creating
// a new array (np.ones(10)) increases the chances that the temp array will be garbage
// collected and/or that its memory will be overridden with different values.
// NOLINTNEXTLINE (performance-unnecessary-value-param)
m.def("get_elem_direct", [](Eigen::Ref<const Eigen::VectorXd> v) {
py::module_::import("numpy").attr("ones")(10);
return v(5);
......
......@@ -8,16 +8,17 @@
#include <catch.hpp>
#include <thread>
#include <fstream>
#include <functional>
#include <thread>
#include <utility>
namespace py = pybind11;
using namespace py::literals;
class Widget {
public:
Widget(std::string message) : message(message) { }
Widget(std::string message) : message(std::move(message)) {}
virtual ~Widget() = default;
std::string the_message() const { return message; }
......
......@@ -9,7 +9,9 @@
#include <pybind11/eval.h>
#include "pybind11_tests.h"
#include <utility>
TEST_SUBMODULE(eval_, m) {
// test_evals
......@@ -67,7 +69,7 @@ TEST_SUBMODULE(eval_, m) {
int val_out;
local["call_test2"] = py::cpp_function([&](int value) { val_out = value; });
auto result = py::eval_file(filename, global, local);
auto result = py::eval_file(std::move(filename), global, local);
return val_out == 43 && result.is_none();
});
......
......@@ -8,7 +8,9 @@
*/
#include "test_exceptions.h"
#include "pybind11_tests.h"
#include <utility>
// A type that should be raised as an exception in Python
class MyException : public std::exception {
......@@ -199,6 +201,8 @@ TEST_SUBMODULE(exceptions, m) {
throw py::error_already_set();
});
// Changing this broke things. Don't know why
// NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("python_call_in_destructor", [](py::dict d) {
try {
PythonCallInDestructor set_dict_in_destructor(d);
......@@ -210,21 +214,23 @@ TEST_SUBMODULE(exceptions, m) {
return false;
});
m.def("python_alreadyset_in_destructor", [](py::str s) {
m.def("python_alreadyset_in_destructor", [](const py::str &s) {
PythonAlreadySetInDestructor alreadyset_in_destructor(s);
return true;
});
// test_nested_throws
m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) {
try { f(*args); }
catch (py::error_already_set &ex) {
if (ex.matches(exc_type))
py::print(ex.what());
else
throw;
}
});
m.def("try_catch",
[m](const py::object &exc_type, const py::function &f, const py::args &args) {
try {
f(*args);
} catch (py::error_already_set &ex) {
if (ex.matches(exc_type))
py::print(ex.what());
else
throw;
}
});
// Test repr that cannot be displayed
m.def("simple_bool_passthrough", [](bool x) {return x;});
......
......@@ -8,10 +8,11 @@
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <cmath>
#include <new>
#include <utility>
// Classes for testing python construction via C++ factory function:
// Not publicly constructible, copyable, or movable:
......@@ -35,8 +36,15 @@ class TestFactory2 {
TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); }
TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); }
public:
TestFactory2(TestFactory2 &&m) { value = std::move(m.value); print_move_created(this); }
TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; }
TestFactory2(TestFactory2 &&m) noexcept {
value = std::move(m.value);
print_move_created(this);
}
TestFactory2 &operator=(TestFactory2 &&m) noexcept {
value = std::move(m.value);
print_move_assigned(this);
return *this;
}
std::string value;
~TestFactory2() { print_destroyed(this); }
};
......@@ -48,8 +56,15 @@ protected:
TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); }
public:
TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); }
TestFactory3(TestFactory3 &&m) { value = std::move(m.value); print_move_created(this); }
TestFactory3 &operator=(TestFactory3 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; }
TestFactory3(TestFactory3 &&m) noexcept {
value = std::move(m.value);
print_move_created(this);
}
TestFactory3 &operator=(TestFactory3 &&m) noexcept {
value = std::move(m.value);
print_move_assigned(this);
return *this;
}
std::string value;
virtual ~TestFactory3() { print_destroyed(this); }
};
......@@ -73,7 +88,11 @@ protected:
bool alias = false;
public:
TestFactory6(int i) : value{i} { print_created(this, i); }
TestFactory6(TestFactory6 &&f) { print_move_created(this); value = f.value; alias = f.alias; }
TestFactory6(TestFactory6 &&f) noexcept {
print_move_created(this);
value = f.value;
alias = f.alias;
}
TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; }
virtual ~TestFactory6() { print_destroyed(this); }
virtual int get() { return value; }
......@@ -85,7 +104,7 @@ public:
// when an alias is needed:
PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); }
PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); }
PyTF6(PyTF6 &&f) : TestFactory6(std::move(f)) { print_move_created(this); }
PyTF6(PyTF6 &&f) noexcept : TestFactory6(std::move(f)) { print_move_created(this); }
PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); }
PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); }
~PyTF6() override { print_destroyed(this); }
......@@ -98,7 +117,11 @@ protected:
bool alias = false;
public:
TestFactory7(int i) : value{i} { print_created(this, i); }
TestFactory7(TestFactory7 &&f) { print_move_created(this); value = f.value; alias = f.alias; }
TestFactory7(TestFactory7 &&f) noexcept {
print_move_created(this);
value = f.value;
alias = f.alias;
}
TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; }
virtual ~TestFactory7() { print_destroyed(this); }
virtual int get() { return value; }
......@@ -107,7 +130,7 @@ public:
class PyTF7 : public TestFactory7 {
public:
PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); }
PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); }
PyTF7(PyTF7 &&f) noexcept : TestFactory7(std::move(f)) { print_move_created(this); }
PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); }
~PyTF7() override { print_destroyed(this); }
int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
......@@ -122,7 +145,9 @@ public:
// Holder:
static std::unique_ptr<TestFactory1> construct1(int a) { return std::unique_ptr<TestFactory1>(new TestFactory1(a)); }
// pointer again
static TestFactory1 *construct1_string(std::string a) { return new TestFactory1(a); }
static TestFactory1 *construct1_string(std::string a) {
return new TestFactory1(std::move(a));
}
// Moveable type:
// pointer:
......@@ -130,7 +155,7 @@ public:
// holder:
static std::unique_ptr<TestFactory2> construct2(int a) { return std::unique_ptr<TestFactory2>(new TestFactory2(a)); }
// by value moving:
static TestFactory2 construct2(std::string a) { return TestFactory2(a); }
static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); }
// shared_ptr holder type:
// pointer:
......@@ -173,10 +198,11 @@ TEST_SUBMODULE(factory_constructors, m) {
;
py::class_<TestFactory2>(m, "TestFactory2")
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
.def(py::init([](unique_ptr_tag, std::string v) { return TestFactoryHelper::construct2(v); }))
.def(py::init([](unique_ptr_tag, std::string v) {
return TestFactoryHelper::construct2(std::move(v));
}))
.def(py::init([](move_tag) { return TestFactoryHelper::construct2(); }))
.def_readwrite("value", &TestFactory2::value)
;
.def_readwrite("value", &TestFactory2::value);
// Stateful & reused:
int c = 1;
......@@ -188,7 +214,9 @@ TEST_SUBMODULE(factory_constructors, m) {
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
ignoreOldStyleInitWarnings([&pyTestFactory3]() {
pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }); // placement-new ctor
pyTestFactory3.def("__init__", [](TestFactory3 &self, std::string v) {
new (&self) TestFactory3(std::move(v));
}); // placement-new ctor
});
pyTestFactory3
// factories returning a derived type:
......@@ -219,52 +247,54 @@ TEST_SUBMODULE(factory_constructors, m) {
py::class_<TestFactory6, PyTF6>(m, "TestFactory6")
.def(py::init([](base_tag, int i) { return TestFactory6(i); }))
.def(py::init([](alias_tag, int i) { return PyTF6(i); }))
.def(py::init([](alias_tag, std::string s) { return PyTF6(s); }))
.def(py::init([](alias_tag, std::string s) { return PyTF6(std::move(s)); }))
.def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); }))
.def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); }))
.def(py::init([](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); }))
.def(py::init(
[](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); }))
.def("get", &TestFactory6::get)
.def("has_alias", &TestFactory6::has_alias)
.def_static("get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference)
.def_static("get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference)
;
.def_static(
"get_cstats", &ConstructorStats::get<TestFactory6>, py::return_value_policy::reference)
.def_static(
"get_alias_cstats", &ConstructorStats::get<PyTF6>, py::return_value_policy::reference);
// test_init_factory_dual
// Separate alias constructor testing
py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7")
.def(py::init(
[](int i) { return TestFactory7(i); },
[](int i) { return PyTF7(i); }))
.def(py::init(
[](pointer_tag, int i) { return new TestFactory7(i); },
[](pointer_tag, int i) { return new PyTF7(i); }))
.def(py::init(
[](mixed_tag, int i) { return new TestFactory7(i); },
[](mixed_tag, int i) { return PyTF7(i); }))
.def(py::init(
[](mixed_tag, std::string s) { return TestFactory7((int) s.size()); },
[](mixed_tag, std::string s) { return new PyTF7((int) s.size()); }))
.def(py::init(
[](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
[](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); }))
.def(py::init(
[](alias_tag, pointer_tag, int i) { return new PyTF7(i); },
[](alias_tag, pointer_tag, int i) { return new PyTF7(10*i); }))
.def(py::init([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); }))
.def(py::init([](pointer_tag, int i) { return new TestFactory7(i); },
[](pointer_tag, int i) { return new PyTF7(i); }))
.def(py::init([](mixed_tag, int i) { return new TestFactory7(i); },
[](mixed_tag, int i) { return PyTF7(i); }))
.def(py::init([](mixed_tag, const std::string &s) { return TestFactory7((int) s.size()); },
[](mixed_tag, const std::string &s) { return new PyTF7((int) s.size()); }))
.def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory7(i); },
[](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); }))
.def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF7(i); },
[](alias_tag, pointer_tag, int i) { return new PyTF7(10 * i); }))
.def(py::init(
[](shared_ptr_tag, base_tag, int i) { return std::make_shared<TestFactory7>(i); },
[](shared_ptr_tag, base_tag, int i) { auto *p = new PyTF7(i); return std::shared_ptr<TestFactory7>(p); }))
.def(py::init(
[](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); },
[](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared<TestFactory7>(i); })) // <-- invalid alias factory
[](shared_ptr_tag, base_tag, int i) {
auto *p = new PyTF7(i);
return std::shared_ptr<TestFactory7>(p);
}))
.def(py::init([](shared_ptr_tag,
invalid_base_tag,
int i) { return std::make_shared<TestFactory7>(i); },
[](shared_ptr_tag, invalid_base_tag, int i) {
return std::make_shared<TestFactory7>(i);
})) // <-- invalid alias factory
.def("get", &TestFactory7::get)
.def("has_alias", &TestFactory7::has_alias)
.def_static("get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference)
.def_static("get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference)
;
.def_static(
"get_cstats", &ConstructorStats::get<TestFactory7>, py::return_value_policy::reference)
.def_static(
"get_alias_cstats", &ConstructorStats::get<PyTF7>, py::return_value_policy::reference);
// test_placement_new_alternative
// Class with a custom new operator but *without* a placement new operator (issue #948)
......@@ -331,12 +361,10 @@ TEST_SUBMODULE(factory_constructors, m) {
pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
// Regular again: requires yet another preallocation
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); });
pyNoisyAlloc.def(
"__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); });
});
// static_assert testing (the following def's should all fail with appropriate compilation errors):
#if 0
struct BadF1Base {};
......
......@@ -35,20 +35,15 @@ TEST_SUBMODULE(gil_scoped, m) {
.def("virtual_func", &VirtClass::virtual_func)
.def("pure_virtual_func", &VirtClass::pure_virtual_func);
m.def("test_callback_py_obj",
[](py::object func) { func(); });
m.def("test_callback_std_func",
[](const std::function<void()> &func) { func(); });
m.def("test_callback_virtual_func",
[](VirtClass &virt) { virt.virtual_func(); });
m.def("test_callback_pure_virtual_func",
[](VirtClass &virt) { virt.pure_virtual_func(); });
m.def("test_cross_module_gil",
[]() {
auto cm = py::module_::import("cross_module_gil_utils");
auto gil_acquire = reinterpret_cast<void (*)()>(
PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
py::gil_scoped_release gil_release;
gil_acquire();
});
m.def("test_callback_py_obj", [](py::object &func) { func(); });
m.def("test_callback_std_func", [](const std::function<void()> &func) { func(); });
m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_func(); });
m.def("test_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); });
m.def("test_cross_module_gil", []() {
auto cm = py::module_::import("cross_module_gil_utils");
auto gil_acquire
= reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
py::gil_scoped_release gil_release;
gil_acquire();
});
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment