You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
9.0 KiB

  1. /*
  2. tests/test_callbacks.cpp -- callbacks
  3. Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
  4. All rights reserved. Use of this source code is governed by a
  5. BSD-style license that can be found in the LICENSE file.
  6. */
  7. #include <pybind11/functional.h>
  8. #include "constructor_stats.h"
  9. #include "pybind11_tests.h"
  10. #include <thread>
  11. int dummy_function(int i) { return i + 1; }
  12. TEST_SUBMODULE(callbacks, m) {
  13. // test_callbacks, test_function_signatures
  14. m.def("test_callback1", [](const py::object &func) { return func(); });
  15. m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
  16. m.def("test_callback3", [](const std::function<int(int)> &func) {
  17. return "func(43) = " + std::to_string(func(43));
  18. });
  19. m.def("test_callback4",
  20. []() -> std::function<int(int)> { return [](int i) { return i + 1; }; });
  21. m.def("test_callback5",
  22. []() { return py::cpp_function([](int i) { return i + 1; }, py::arg("number")); });
  23. // test_keyword_args_and_generalized_unpacking
  24. m.def("test_tuple_unpacking", [](const py::function &f) {
  25. auto t1 = py::make_tuple(2, 3);
  26. auto t2 = py::make_tuple(5, 6);
  27. return f("positional", 1, *t1, 4, *t2);
  28. });
  29. m.def("test_dict_unpacking", [](const py::function &f) {
  30. auto d1 = py::dict("key"_a = "value", "a"_a = 1);
  31. auto d2 = py::dict();
  32. auto d3 = py::dict("b"_a = 2);
  33. return f("positional", 1, **d1, **d2, **d3);
  34. });
  35. m.def("test_keyword_args", [](const py::function &f) { return f("x"_a = 10, "y"_a = 20); });
  36. m.def("test_unpacking_and_keywords1", [](const py::function &f) {
  37. auto args = py::make_tuple(2);
  38. auto kwargs = py::dict("d"_a = 4);
  39. return f(1, *args, "c"_a = 3, **kwargs);
  40. });
  41. m.def("test_unpacking_and_keywords2", [](const py::function &f) {
  42. auto kwargs1 = py::dict("a"_a = 1);
  43. auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4);
  44. return f("positional",
  45. *py::make_tuple(1),
  46. 2,
  47. *py::make_tuple(3, 4),
  48. 5,
  49. "key"_a = "value",
  50. **kwargs1,
  51. "b"_a = 2,
  52. **kwargs2,
  53. "e"_a = 5);
  54. });
  55. m.def("test_unpacking_error1", [](const py::function &f) {
  56. auto kwargs = py::dict("x"_a = 3);
  57. return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword
  58. });
  59. m.def("test_unpacking_error2", [](const py::function &f) {
  60. auto kwargs = py::dict("x"_a = 3);
  61. return f(**kwargs, "x"_a = 1); // duplicate keyword after **
  62. });
  63. m.def("test_arg_conversion_error1",
  64. [](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
  65. m.def("test_arg_conversion_error2", [](const py::function &f) {
  66. f(234, "expected_name"_a = UnregisteredType(), "kw"_a = 567);
  67. });
  68. // test_lambda_closure_cleanup
  69. struct Payload {
  70. Payload() { print_default_created(this); }
  71. ~Payload() { print_destroyed(this); }
  72. Payload(const Payload &) { print_copy_created(this); }
  73. Payload(Payload &&) noexcept { print_move_created(this); }
  74. };
  75. // Export the payload constructor statistics for testing purposes:
  76. m.def("payload_cstats", &ConstructorStats::get<Payload>);
  77. m.def("test_lambda_closure_cleanup", []() -> std::function<void()> {
  78. Payload p;
  79. // In this situation, `Func` in the implementation of
  80. // `cpp_function::initialize` is NOT trivially destructible.
  81. return [p]() {
  82. /* p should be cleaned up when the returned function is garbage collected */
  83. (void) p;
  84. };
  85. });
  86. class CppCallable {
  87. public:
  88. CppCallable() { track_default_created(this); }
  89. ~CppCallable() { track_destroyed(this); }
  90. CppCallable(const CppCallable &) { track_copy_created(this); }
  91. CppCallable(CppCallable &&) noexcept { track_move_created(this); }
  92. void operator()() {}
  93. };
  94. m.def("test_cpp_callable_cleanup", []() {
  95. // Related issue: https://github.com/pybind/pybind11/issues/3228
  96. // Related PR: https://github.com/pybind/pybind11/pull/3229
  97. py::list alive_counts;
  98. ConstructorStats &stat = ConstructorStats::get<CppCallable>();
  99. alive_counts.append(stat.alive());
  100. {
  101. CppCallable cpp_callable;
  102. alive_counts.append(stat.alive());
  103. {
  104. // In this situation, `Func` in the implementation of
  105. // `cpp_function::initialize` IS trivially destructible,
  106. // only `capture` is not.
  107. py::cpp_function py_func(cpp_callable);
  108. py::detail::silence_unused_warnings(py_func);
  109. alive_counts.append(stat.alive());
  110. }
  111. alive_counts.append(stat.alive());
  112. {
  113. py::cpp_function py_func(std::move(cpp_callable));
  114. py::detail::silence_unused_warnings(py_func);
  115. alive_counts.append(stat.alive());
  116. }
  117. alive_counts.append(stat.alive());
  118. }
  119. alive_counts.append(stat.alive());
  120. return alive_counts;
  121. });
  122. // test_cpp_function_roundtrip
  123. /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
  124. m.def("dummy_function", &dummy_function);
  125. m.def("dummy_function_overloaded", [](int i, int j) { return i + j; });
  126. m.def("dummy_function_overloaded", &dummy_function);
  127. m.def("dummy_function2", [](int i, int j) { return i + j; });
  128. m.def(
  129. "roundtrip",
  130. [](std::function<int(int)> f, bool expect_none = false) {
  131. if (expect_none && f) {
  132. throw std::runtime_error("Expected None to be converted to empty std::function");
  133. }
  134. return f;
  135. },
  136. py::arg("f"),
  137. py::arg("expect_none") = false);
  138. m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string {
  139. using fn_type = int (*)(int);
  140. const auto *result = f.target<fn_type>();
  141. if (!result) {
  142. auto r = f(1);
  143. return "can't convert to function pointer: eval(1) = " + std::to_string(r);
  144. }
  145. if (*result == dummy_function) {
  146. auto r = (*result)(1);
  147. return "matches dummy_function: eval(1) = " + std::to_string(r);
  148. }
  149. return "argument does NOT match dummy_function. This should never happen!";
  150. });
  151. class AbstractBase {
  152. public:
  153. // [workaround(intel)] = default does not work here
  154. // Defaulting this destructor results in linking errors with the Intel compiler
  155. // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
  156. virtual ~AbstractBase() {} // NOLINT(modernize-use-equals-default)
  157. virtual unsigned int func() = 0;
  158. };
  159. m.def("func_accepting_func_accepting_base",
  160. [](const std::function<double(AbstractBase &)> &) {});
  161. struct MovableObject {
  162. bool valid = true;
  163. MovableObject() = default;
  164. MovableObject(const MovableObject &) = default;
  165. MovableObject &operator=(const MovableObject &) = default;
  166. MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; }
  167. MovableObject &operator=(MovableObject &&o) noexcept {
  168. valid = o.valid;
  169. o.valid = false;
  170. return *this;
  171. }
  172. };
  173. py::class_<MovableObject>(m, "MovableObject");
  174. // test_movable_object
  175. m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
  176. auto x = MovableObject();
  177. f(x); // lvalue reference shouldn't move out object
  178. return x.valid; // must still return `true`
  179. });
  180. // test_bound_method_callback
  181. struct CppBoundMethodTest {};
  182. py::class_<CppBoundMethodTest>(m, "CppBoundMethodTest")
  183. .def(py::init<>())
  184. .def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; });
  185. // This checks that builtin functions can be passed as callbacks
  186. // rather than throwing RuntimeError due to trying to extract as capsule
  187. m.def("test_sum_builtin",
  188. [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
  189. return sum_builtin(i);
  190. });
  191. // test async Python callbacks
  192. using callback_f = std::function<void(int)>;
  193. m.def("test_async_callback", [](const callback_f &f, const py::list &work) {
  194. // make detached thread that calls `f` with piece of work after a little delay
  195. auto start_f = [f](int j) {
  196. auto invoke_f = [f, j] {
  197. std::this_thread::sleep_for(std::chrono::milliseconds(50));
  198. f(j);
  199. };
  200. auto t = std::thread(std::move(invoke_f));
  201. t.detach();
  202. };
  203. // spawn worker threads
  204. for (auto i : work) {
  205. start_f(py::cast<int>(i));
  206. }
  207. });
  208. m.def("callback_num_times", [](const py::function &f, std::size_t num) {
  209. for (std::size_t i = 0; i < num; i++) {
  210. f();
  211. }
  212. });
  213. }