2016-03-10 12:24:10 +00:00
|
|
|
/*
|
2016-08-12 11:50:00 +00:00
|
|
|
tests/test_issues.cpp -- collection of testcases for miscellaneous issues
|
2016-03-10 12:24:10 +00:00
|
|
|
|
2016-04-17 18:21:41 +00:00
|
|
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
2016-03-10 12:24:10 +00:00
|
|
|
|
|
|
|
All rights reserved. Use of this source code is governed by a
|
|
|
|
BSD-style license that can be found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2016-08-12 11:50:00 +00:00
|
|
|
#include "pybind11_tests.h"
|
|
|
|
#include "constructor_stats.h"
|
2016-04-20 15:00:57 +00:00
|
|
|
#include <pybind11/stl.h>
|
2016-08-09 21:57:59 +00:00
|
|
|
#include <pybind11/operators.h>
|
2016-03-10 12:24:10 +00:00
|
|
|
|
2016-04-30 21:02:39 +00:00
|
|
|
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
|
|
|
|
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
#define TRACKERS(CLASS) CLASS() { print_default_created(this); } ~CLASS() { print_destroyed(this); }
|
|
|
|
struct NestABase { int value = -2; TRACKERS(NestABase) };
|
|
|
|
struct NestA : NestABase { int value = 3; NestA& operator+=(int i) { value += i; return *this; } TRACKERS(NestA) };
|
|
|
|
struct NestB { NestA a; int value = 4; NestB& operator-=(int i) { value -= i; return *this; } TRACKERS(NestB) };
|
|
|
|
struct NestC { NestB b; int value = 5; NestC& operator*=(int i) { value *= i; return *this; } TRACKERS(NestC) };
|
|
|
|
|
2016-03-10 12:24:10 +00:00
|
|
|
void init_issues(py::module &m) {
|
|
|
|
py::module m2 = m.def_submodule("issues");
|
|
|
|
|
2016-05-01 08:39:45 +00:00
|
|
|
#if !defined(_MSC_VER)
|
|
|
|
// Visual Studio 2015 currently cannot compile this test
|
|
|
|
// (see the comment in type_caster_base::make_copy_constructor)
|
|
|
|
// #70 compilation issue if operator new is not public
|
|
|
|
class NonConstructible { private: void *operator new(size_t bytes) throw(); };
|
|
|
|
py::class_<NonConstructible>(m, "Foo");
|
2016-05-01 12:42:20 +00:00
|
|
|
m2.def("getstmt", []() -> NonConstructible * { return nullptr; },
|
2016-05-01 08:39:45 +00:00
|
|
|
py::return_value_policy::reference);
|
|
|
|
#endif
|
|
|
|
|
2016-03-10 12:24:10 +00:00
|
|
|
// #137: const char* isn't handled properly
|
2016-08-12 20:28:31 +00:00
|
|
|
m2.def("print_cchar", [](const char *s) { return std::string(s); });
|
2016-03-26 22:04:10 +00:00
|
|
|
|
|
|
|
// #150: char bindings broken
|
2016-08-12 20:28:31 +00:00
|
|
|
m2.def("print_char", [](char c) { return std::string(1, c); });
|
2016-04-11 16:13:08 +00:00
|
|
|
|
|
|
|
// #159: virtual function dispatch has problems with similar-named functions
|
2016-08-12 20:28:31 +00:00
|
|
|
struct Base { virtual std::string dispatch() const {
|
2016-04-30 21:02:39 +00:00
|
|
|
/* for some reason MSVC2015 can't compile this if the function is pure virtual */
|
2016-08-12 20:28:31 +00:00
|
|
|
return {};
|
2016-04-30 21:02:39 +00:00
|
|
|
}; };
|
2016-04-30 20:44:00 +00:00
|
|
|
|
|
|
|
struct DispatchIssue : Base {
|
2016-08-12 20:28:31 +00:00
|
|
|
virtual std::string dispatch() const {
|
|
|
|
PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */);
|
2016-04-30 20:44:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-26 11:19:27 +00:00
|
|
|
py::class_<Base, std::unique_ptr<Base>, DispatchIssue>(m2, "DispatchIssue")
|
2016-04-20 15:00:57 +00:00
|
|
|
.def(py::init<>())
|
2016-04-11 16:13:08 +00:00
|
|
|
.def("dispatch", &Base::dispatch);
|
|
|
|
|
2016-08-12 20:28:31 +00:00
|
|
|
m2.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
|
2016-04-30 20:44:00 +00:00
|
|
|
|
|
|
|
struct Placeholder { int i; Placeholder(int i) : i(i) { } };
|
2016-04-20 15:00:57 +00:00
|
|
|
|
|
|
|
py::class_<Placeholder>(m2, "Placeholder")
|
2016-04-21 10:21:14 +00:00
|
|
|
.def(py::init<int>())
|
2016-04-20 15:00:57 +00:00
|
|
|
.def("__repr__", [](const Placeholder &p) { return "Placeholder[" + std::to_string(p.i) + "]"; });
|
|
|
|
|
|
|
|
// #171: Can't return reference wrappers (or STL datastructures containing them)
|
2016-04-21 10:21:14 +00:00
|
|
|
m2.def("return_vec_of_reference_wrapper", [](std::reference_wrapper<Placeholder> p4){
|
2016-04-20 15:00:57 +00:00
|
|
|
Placeholder *p1 = new Placeholder{1};
|
|
|
|
Placeholder *p2 = new Placeholder{2};
|
2016-04-21 10:21:14 +00:00
|
|
|
Placeholder *p3 = new Placeholder{3};
|
2016-04-20 15:00:57 +00:00
|
|
|
std::vector<std::reference_wrapper<Placeholder>> v;
|
|
|
|
v.push_back(std::ref(*p1));
|
|
|
|
v.push_back(std::ref(*p2));
|
|
|
|
v.push_back(std::ref(*p3));
|
2016-04-21 10:21:14 +00:00
|
|
|
v.push_back(p4);
|
2016-04-20 15:00:57 +00:00
|
|
|
return v;
|
|
|
|
});
|
2016-04-27 12:33:52 +00:00
|
|
|
|
|
|
|
// #181: iterator passthrough did not compile
|
|
|
|
m2.def("iterator_passthrough", [](py::iterator s) -> py::iterator {
|
|
|
|
return py::make_iterator(std::begin(s), std::end(s));
|
|
|
|
});
|
2016-04-30 21:02:39 +00:00
|
|
|
|
|
|
|
// #187: issue involving std::shared_ptr<> return value policy & garbage collection
|
|
|
|
struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ };
|
|
|
|
struct ElementA : ElementBase {
|
|
|
|
ElementA(int v) : v(v) { }
|
|
|
|
int value() { return v; }
|
|
|
|
int v;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ElementList {
|
|
|
|
void add(std::shared_ptr<ElementBase> e) { l.push_back(e); }
|
|
|
|
std::vector<std::shared_ptr<ElementBase>> l;
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<ElementBase, std::shared_ptr<ElementBase>> (m2, "ElementBase");
|
|
|
|
|
|
|
|
py::class_<ElementA, std::shared_ptr<ElementA>>(m2, "ElementA", py::base<ElementBase>())
|
|
|
|
.def(py::init<int>())
|
|
|
|
.def("value", &ElementA::value);
|
|
|
|
|
|
|
|
py::class_<ElementList, std::shared_ptr<ElementList>>(m2, "ElementList")
|
|
|
|
.def(py::init<>())
|
|
|
|
.def("add", &ElementList::add)
|
|
|
|
.def("get", [](ElementList &el){
|
|
|
|
py::list list;
|
|
|
|
for (auto &e : el.l)
|
|
|
|
list.append(py::cast(e));
|
|
|
|
return list;
|
|
|
|
});
|
2016-05-01 12:42:20 +00:00
|
|
|
|
|
|
|
// (no id): should not be able to pass 'None' to a reference argument
|
2016-08-19 11:45:36 +00:00
|
|
|
m2.def("get_element", [](ElementA &el) { return el.value(); });
|
2016-05-17 13:35:29 +00:00
|
|
|
|
|
|
|
// (no id): don't cast doubles to ints
|
|
|
|
m2.def("expect_float", [](float f) { return f; });
|
|
|
|
m2.def("expect_int", [](int i) { return i; });
|
2016-05-26 11:19:27 +00:00
|
|
|
|
|
|
|
// (no id): don't invoke Python dispatch code when instantiating C++
|
|
|
|
// classes that were not extended on the Python side
|
|
|
|
struct A {
|
|
|
|
virtual ~A() {}
|
|
|
|
virtual void f() { std::cout << "A.f()" << std::endl; }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PyA : A {
|
|
|
|
PyA() { std::cout << "PyA.PyA()" << std::endl; }
|
|
|
|
|
|
|
|
void f() override {
|
|
|
|
std::cout << "PyA.f()" << std::endl;
|
|
|
|
PYBIND11_OVERLOAD(void, A, f);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto call_f = [](A *a) { a->f(); };
|
|
|
|
|
2016-07-01 12:54:24 +00:00
|
|
|
pybind11::class_<A, std::unique_ptr<A>, PyA>(m2, "A")
|
|
|
|
.def(py::init<>())
|
|
|
|
.def("f", &A::f);
|
2016-05-26 11:19:27 +00:00
|
|
|
|
2016-07-01 12:54:24 +00:00
|
|
|
m2.def("call_f", call_f);
|
2016-05-31 07:53:28 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
py::class_<Placeholder>(m2, "Placeholder");
|
|
|
|
throw std::logic_error("Expected an exception!");
|
2016-06-01 21:03:10 +00:00
|
|
|
} catch (std::runtime_error &) {
|
2016-05-31 07:53:28 +00:00
|
|
|
/* All good */
|
|
|
|
}
|
2016-07-17 21:43:00 +00:00
|
|
|
|
|
|
|
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
|
|
|
|
class StrIssue {
|
|
|
|
public:
|
|
|
|
StrIssue(int i) : val{i} {}
|
|
|
|
StrIssue() : StrIssue(-1) {}
|
|
|
|
int value() const { return val; }
|
|
|
|
private:
|
|
|
|
int val;
|
|
|
|
};
|
|
|
|
py::class_<StrIssue> si(m2, "StrIssue");
|
|
|
|
si .def(py::init<int>())
|
|
|
|
.def(py::init<>())
|
2016-08-19 11:45:36 +00:00
|
|
|
.def("__str__", [](const StrIssue &si) { return "StrIssue[" + std::to_string(si.value()) + "]"; })
|
2016-07-17 21:43:00 +00:00
|
|
|
;
|
|
|
|
|
2016-08-09 21:57:59 +00:00
|
|
|
// Issue #328: first member in a class can't be used in operators
|
2016-08-10 16:08:04 +00:00
|
|
|
py::class_<NestABase>(m2, "NestABase").def(py::init<>()).def_readwrite("value", &NestABase::value);
|
|
|
|
py::class_<NestA>(m2, "NestA").def(py::init<>()).def(py::self += int())
|
|
|
|
.def("as_base", [](NestA &a) -> NestABase& { return (NestABase&) a; }, py::return_value_policy::reference_internal);
|
2016-08-09 21:57:59 +00:00
|
|
|
py::class_<NestB>(m2, "NestB").def(py::init<>()).def(py::self -= int()).def_readwrite("a", &NestB::a);
|
|
|
|
py::class_<NestC>(m2, "NestC").def(py::init<>()).def(py::self *= int()).def_readwrite("b", &NestC::b);
|
2016-08-12 20:28:31 +00:00
|
|
|
m2.def("get_NestA", [](const NestA &a) { return a.value; });
|
|
|
|
m2.def("get_NestB", [](const NestB &b) { return b.value; });
|
|
|
|
m2.def("get_NestC", [](const NestC &c) { return c.value; });
|
2016-05-01 12:42:20 +00:00
|
|
|
}
|