Move tests from test_issues.cpp/py into appropriate files

This commit is contained in:
Dean Moldovan 2017-06-07 16:52:50 +02:00
parent 44e9a4e6cf
commit bdfb50f384
21 changed files with 510 additions and 632 deletions

View File

@ -40,7 +40,6 @@ set(PYBIND11_TEST_FILES
test_eval.cpp test_eval.cpp
test_exceptions.cpp test_exceptions.cpp
test_inheritance.cpp test_inheritance.cpp
test_issues.cpp
test_kwargs_and_defaults.cpp test_kwargs_and_defaults.cpp
test_methods_and_attributes.cpp test_methods_and_attributes.cpp
test_modules.cpp test_modules.cpp
@ -59,7 +58,7 @@ set(PYBIND11_TEST_FILES
) )
# Invoking cmake with something like: # Invoking cmake with something like:
# cmake -DPYBIND11_TEST_OVERRIDE="test_issues.cpp;test_picking.cpp" .. # cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_picking.cpp" ..
# lets you override the tests that get compiled and run. You can restore to all tests with: # lets you override the tests that get compiled and run. You can restore to all tests with:
# cmake -DPYBIND11_TEST_OVERRIDE= .. # cmake -DPYBIND11_TEST_OVERRIDE= ..
if (PYBIND11_TEST_OVERRIDE) if (PYBIND11_TEST_OVERRIDE)

View File

@ -187,4 +187,23 @@ test_initializer copy_move_policies([](py::module &m) {
static PrivateOpNew x{}; static PrivateOpNew x{};
return x; return x;
}, py::return_value_policy::reference); }, py::return_value_policy::reference);
// #389: rvp::move should fall-through to copy on non-movable objects
struct MoveIssue1 {
int v;
MoveIssue1(int v) : v{v} {}
MoveIssue1(const MoveIssue1 &c) = default;
MoveIssue1(MoveIssue1 &&) = delete;
};
struct MoveIssue2 {
int v;
MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default;
};
py::class_<MoveIssue1>(m, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
m.def("get_moveissue1", [](int i) { return new MoveIssue1(i); }, py::return_value_policy::move);
m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
}); });

View File

@ -109,3 +109,13 @@ def test_private_op_new():
assert "the object is neither movable nor copyable" in str(excinfo.value) assert "the object is neither movable nor copyable" in str(excinfo.value)
assert m.private_op_new_reference().value == 1 assert m.private_op_new_reference().value == 1
def test_move_fallback():
"""#389: rvp::move should fall-through to copy on non-movable objects"""
from pybind11_tests import get_moveissue1, get_moveissue2
m2 = get_moveissue2(2)
assert m2.value == 2
m1 = get_moveissue1(1)
assert m1.value == 1

View File

@ -120,4 +120,24 @@ test_initializer inheritance([](py::module &m) {
py::class_<MismatchBase2>(m, "MismatchBase2"); py::class_<MismatchBase2>(m, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(m, "MismatchDerived2"); py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(m, "MismatchDerived2");
}); });
// #511: problem with inheritance + overwritten def_static
struct MyBase {
static std::unique_ptr<MyBase> make() {
return std::unique_ptr<MyBase>(new MyBase());
}
};
struct MyDerived : MyBase {
static std::unique_ptr<MyDerived> make() {
return std::unique_ptr<MyDerived>(new MyDerived());
}
};
py::class_<MyBase>(m, "MyBase")
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
}); });

View File

@ -76,3 +76,16 @@ def test_holder():
assert str(excinfo.value) == ("generic_type: type \"MismatchDerived2\" has a " assert str(excinfo.value) == ("generic_type: type \"MismatchDerived2\" has a "
"non-default holder type while its base " "non-default holder type while its base "
"\"MismatchBase2\" does not") "\"MismatchBase2\" does not")
def test_inheritance_override_def_static():
"""#511: problem with inheritance + overwritten def_static"""
from pybind11_tests import MyBase, MyDerived
b = MyBase.make()
d1 = MyDerived.make2()
d2 = MyDerived.make()
assert isinstance(b, MyBase)
assert isinstance(d1, MyDerived)
assert isinstance(d2, MyDerived)

View File

@ -1,377 +0,0 @@
/*
tests/test_issues.cpp -- collection of testcases for miscellaneous issues
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h>
#include <pybind11/operators.h>
#include <pybind11/complex.h>
#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) };
/// #393
class OpTest1 {};
class OpTest2 {};
OpTest1 operator+(const OpTest1 &, const OpTest1 &) {
py::print("Add OpTest1 with OpTest1");
return OpTest1();
}
OpTest2 operator+(const OpTest2 &, const OpTest2 &) {
py::print("Add OpTest2 with OpTest2");
return OpTest2();
}
OpTest2 operator+(const OpTest2 &, const OpTest1 &) {
py::print("Add OpTest2 with OpTest1");
return OpTest2();
}
// #461
class Dupe1 {
public:
Dupe1(int v) : v_{v} {}
int get_value() const { return v_; }
private:
int v_;
};
class Dupe2 {};
class Dupe3 {};
class DupeException : public std::runtime_error {};
// #478
template <typename T> class custom_unique_ptr {
public:
custom_unique_ptr() { print_default_created(this); }
custom_unique_ptr(T *ptr) : _ptr{ptr} { print_created(this, ptr); }
custom_unique_ptr(custom_unique_ptr<T> &&move) : _ptr{move._ptr} { move._ptr = nullptr; print_move_created(this); }
custom_unique_ptr &operator=(custom_unique_ptr<T> &&move) { print_move_assigned(this); if (_ptr) destruct_ptr(); _ptr = move._ptr; move._ptr = nullptr; return *this; }
custom_unique_ptr(const custom_unique_ptr<T> &) = delete;
void operator=(const custom_unique_ptr<T> &copy) = delete;
~custom_unique_ptr() { print_destroyed(this); if (_ptr) destruct_ptr(); }
private:
T *_ptr = nullptr;
void destruct_ptr() { delete _ptr; }
};
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
/// Issue #528: templated constructor
struct TplConstrClass {
template <typename T> TplConstrClass(const T &arg) : str{arg} {}
std::string str;
bool operator==(const TplConstrClass &t) const { return t.str == str; }
};
namespace std {
template <> struct hash<TplConstrClass> { size_t operator()(const TplConstrClass &t) const { return std::hash<std::string>()(t.str); } };
}
void init_issues(py::module &m) {
py::module m2 = m.def_submodule("issues");
// #137: const char* isn't handled properly
m2.def("print_cchar", [](const char *s) { return std::string(s); });
// #150: char bindings broken
m2.def("print_char", [](char c) { return std::string(1, c); });
// #159: virtual function dispatch has problems with similar-named functions
struct Base { virtual std::string dispatch() const {
/* for some reason MSVC2015 can't compile this if the function is pure virtual */
return {};
}; };
struct DispatchIssue : Base {
virtual std::string dispatch() const {
PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */);
}
};
py::class_<Base, DispatchIssue>(m2, "DispatchIssue")
.def(py::init<>())
.def("dispatch", &Base::dispatch);
m2.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
struct Placeholder { int i; Placeholder(int i) : i(i) { } };
py::class_<Placeholder>(m2, "Placeholder")
.def(py::init<int>())
.def("__repr__", [](const Placeholder &p) { return "Placeholder[" + std::to_string(p.i) + "]"; });
// #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));
});
// #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, ElementBase, std::shared_ptr<ElementA>>(m2, "ElementA")
.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;
});
// (no id): should not be able to pass 'None' to a reference argument
m2.def("get_element", [](ElementA &el) { return el.value(); });
// (no id): don't cast doubles to ints
m2.def("expect_float", [](float f) { return f; });
m2.def("expect_int", [](int i) { return i; });
try {
py::class_<Placeholder>(m2, "Placeholder");
throw std::logic_error("Expected an exception!");
} catch (std::runtime_error &) {
/* All good */
}
// 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<>())
.def("__str__", [](const StrIssue &si) { return "StrIssue[" + std::to_string(si.value()) + "]"; })
;
// Issue #328: first member in a class can't be used in operators
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);
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);
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; });
// Issue 389: r_v_p::move should fall-through to copy on non-movable objects
class MoveIssue1 {
public:
MoveIssue1(int v) : v{v} {}
MoveIssue1(const MoveIssue1 &c) { v = c.v; }
MoveIssue1(MoveIssue1 &&) = delete;
int v;
};
class MoveIssue2 {
public:
MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default;
int v;
};
py::class_<MoveIssue1>(m2, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
py::class_<MoveIssue2>(m2, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
m2.def("get_moveissue1", [](int i) -> MoveIssue1 * { return new MoveIssue1(i); }, py::return_value_policy::move);
m2.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
// Issues 392/397: overridding reference-returning functions
class OverrideTest {
public:
struct A { std::string value = "hi"; };
std::string v;
A a;
explicit OverrideTest(const std::string &v) : v{v} {}
virtual std::string str_value() { return v; }
virtual std::string &str_ref() { return v; }
virtual A A_value() { return a; }
virtual A &A_ref() { return a; }
};
class PyOverrideTest : public OverrideTest {
public:
using OverrideTest::OverrideTest;
std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); }
// Not allowed (uncommenting should hit a static_assert failure): we can't get a reference
// to a python numeric value, since we only copy values in the numeric type caster:
// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); }
// But we can work around it like this:
private:
std::string _tmp;
std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); }
public:
std::string &str_ref() override { return _tmp = str_ref_helper(); }
A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); }
A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); }
};
py::class_<OverrideTest::A>(m2, "OverrideTest_A")
.def_readwrite("value", &OverrideTest::A::value);
py::class_<OverrideTest, PyOverrideTest>(m2, "OverrideTest")
.def(py::init<const std::string &>())
.def("str_value", &OverrideTest::str_value)
// .def("str_ref", &OverrideTest::str_ref)
.def("A_value", &OverrideTest::A_value)
.def("A_ref", &OverrideTest::A_ref);
/// Issue 393: need to return NotSupported to ensure correct arithmetic operator behavior
py::class_<OpTest1>(m2, "OpTest1")
.def(py::init<>())
.def(py::self + py::self);
py::class_<OpTest2>(m2, "OpTest2")
.def(py::init<>())
.def(py::self + py::self)
.def("__add__", [](const OpTest2& c2, const OpTest1& c1) { return c2 + c1; })
.def("__radd__", [](const OpTest2& c2, const OpTest1& c1) { return c2 + c1; });
// Issue 388: Can't make iterators via make_iterator() with different r/v policies
static std::vector<int> list = { 1, 2, 3 };
m2.def("make_iterator_1", []() { return py::make_iterator<py::return_value_policy::copy>(list); });
m2.def("make_iterator_2", []() { return py::make_iterator<py::return_value_policy::automatic>(list); });
static std::vector<std::string> nothrows;
// Issue 461: registering two things with the same name:
py::class_<Dupe1>(m2, "Dupe1")
.def("get_value", &Dupe1::get_value)
;
m2.def("dupe1_factory", [](int v) { return new Dupe1(v); });
py::class_<Dupe2>(m2, "Dupe2");
py::exception<DupeException>(m2, "DupeException");
try {
m2.def("Dupe1", [](int v) { return new Dupe1(v); });
nothrows.emplace_back("Dupe1");
}
catch (std::runtime_error &) {}
try {
py::class_<Dupe3>(m2, "dupe1_factory");
nothrows.emplace_back("dupe1_factory");
}
catch (std::runtime_error &) {}
try {
py::exception<Dupe3>(m2, "Dupe2");
nothrows.emplace_back("Dupe2");
}
catch (std::runtime_error &) {}
try {
m2.def("DupeException", []() { return 30; });
nothrows.emplace_back("DupeException1");
}
catch (std::runtime_error &) {}
try {
py::class_<DupeException>(m2, "DupeException");
nothrows.emplace_back("DupeException2");
}
catch (std::runtime_error &) {}
m2.def("dupe_exception_failures", []() {
py::list l;
for (auto &e : nothrows) l.append(py::cast(e));
return l;
});
/// Issue #471: shared pointer instance not dellocated
class SharedChild : public std::enable_shared_from_this<SharedChild> {
public:
SharedChild() { print_created(this); }
~SharedChild() { print_destroyed(this); }
};
class SharedParent {
public:
SharedParent() : child(std::make_shared<SharedChild>()) { }
const SharedChild &get_child() const { return *child; }
private:
std::shared_ptr<SharedChild> child;
};
py::class_<SharedChild, std::shared_ptr<SharedChild>>(m, "SharedChild");
py::class_<SharedParent, std::shared_ptr<SharedParent>>(m, "SharedParent")
.def(py::init<>())
.def("get_child", &SharedParent::get_child, py::return_value_policy::reference);
/// Issue/PR #478: unique ptrs constructed and freed without destruction
class SpecialHolderObj {
public:
int val = 0;
SpecialHolderObj *ch = nullptr;
SpecialHolderObj(int v, bool make_child = true) : val{v}, ch{make_child ? new SpecialHolderObj(val+1, false) : nullptr}
{ print_created(this, val); }
~SpecialHolderObj() { delete ch; print_destroyed(this); }
SpecialHolderObj *child() { return ch; }
};
py::class_<SpecialHolderObj, custom_unique_ptr<SpecialHolderObj>>(m, "SpecialHolderObj")
.def(py::init<int>())
.def("child", &SpecialHolderObj::child, pybind11::return_value_policy::reference_internal)
.def_readwrite("val", &SpecialHolderObj::val)
.def_static("holder_cstats", &ConstructorStats::get<custom_unique_ptr<SpecialHolderObj>>,
py::return_value_policy::reference);
/// Issue #484: number conversion generates unhandled exceptions
m2.def("test_complex", [](float x) { py::print("{}"_s.format(x)); });
m2.def("test_complex", [](std::complex<float> x) { py::print("({}, {})"_s.format(x.real(), x.imag())); });
/// Issue #511: problem with inheritance + overwritten def_static
struct MyBase {
static std::unique_ptr<MyBase> make() {
return std::unique_ptr<MyBase>(new MyBase());
}
};
struct MyDerived : MyBase {
static std::unique_ptr<MyDerived> make() {
return std::unique_ptr<MyDerived>(new MyDerived());
}
};
py::class_<MyBase>(m2, "MyBase")
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m2, "MyDerived")
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
py::dict d;
std::string bar = "bar";
d["str"] = bar;
d["num"] = 3.7;
/// Issue #528: templated constructor
m2.def("tpl_constr_vector", [](std::vector<TplConstrClass> &) {});
m2.def("tpl_constr_map", [](std::unordered_map<TplConstrClass, TplConstrClass> &) {});
m2.def("tpl_constr_set", [](std::unordered_set<TplConstrClass> &) {});
#if defined(PYBIND11_HAS_OPTIONAL)
m2.def("tpl_constr_optional", [](std::optional<TplConstrClass> &) {});
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
m2.def("tpl_constr_optional", [](std::experimental::optional<TplConstrClass> &) {});
#endif
}
// MSVC workaround: trying to use a lambda here crashes MSVC
test_initializer issues(&init_issues);

View File

@ -1,243 +0,0 @@
import pytest
from pybind11_tests import ConstructorStats
def test_regressions():
from pybind11_tests.issues import print_cchar, print_char
# #137: const char* isn't handled properly
assert print_cchar("const char *") == "const char *"
# #150: char bindings broken
assert print_char("c") == "c"
def test_dispatch_issue(msg):
"""#159: virtual function dispatch has problems with similar-named functions"""
from pybind11_tests.issues import DispatchIssue, dispatch_issue_go
class PyClass1(DispatchIssue):
def dispatch(self):
return "Yay.."
class PyClass2(DispatchIssue):
def dispatch(self):
with pytest.raises(RuntimeError) as excinfo:
super(PyClass2, self).dispatch()
assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
p = PyClass1()
return dispatch_issue_go(p)
b = PyClass2()
assert dispatch_issue_go(b) == "Yay.."
def test_iterator_passthrough():
"""#181: iterator passthrough did not compile"""
from pybind11_tests.issues import iterator_passthrough
assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15]
def test_shared_ptr_gc():
"""// #187: issue involving std::shared_ptr<> return value policy & garbage collection"""
from pybind11_tests.issues import ElementList, ElementA
el = ElementList()
for i in range(10):
el.add(ElementA(i))
pytest.gc_collect()
for i, v in enumerate(el.get()):
assert i == v.value()
def test_no_id(msg):
from pybind11_tests.issues import get_element, expect_float, expect_int
with pytest.raises(TypeError) as excinfo:
get_element(None)
assert msg(excinfo.value) == """
get_element(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.issues.ElementA) -> int
Invoked with: None
"""
with pytest.raises(TypeError) as excinfo:
expect_int(5.2)
assert msg(excinfo.value) == """
expect_int(): incompatible function arguments. The following argument types are supported:
1. (arg0: int) -> int
Invoked with: 5.2
"""
assert expect_float(12) == 12
def test_str_issue(msg):
"""Issue #283: __str__ called on uninitialized instance when constructor arguments invalid"""
from pybind11_tests.issues import StrIssue
assert str(StrIssue(3)) == "StrIssue[3]"
with pytest.raises(TypeError) as excinfo:
str(StrIssue("no", "such", "constructor"))
assert msg(excinfo.value) == """
__init__(): incompatible constructor arguments. The following argument types are supported:
1. m.issues.StrIssue(arg0: int)
2. m.issues.StrIssue()
Invoked with: 'no', 'such', 'constructor'
"""
def test_nested():
""" #328: first member in a class can't be used in operators"""
from pybind11_tests.issues import NestA, NestB, NestC, get_NestA, get_NestB, get_NestC
a = NestA()
b = NestB()
c = NestC()
a += 10
assert get_NestA(a) == 13
b.a += 100
assert get_NestA(b.a) == 103
c.b.a += 1000
assert get_NestA(c.b.a) == 1003
b -= 1
assert get_NestB(b) == 3
c.b -= 3
assert get_NestB(c.b) == 1
c *= 7
assert get_NestC(c) == 35
abase = a.as_base()
assert abase.value == -2
a.as_base().value += 44
assert abase.value == 42
assert c.b.a.as_base().value == -2
c.b.a.as_base().value += 44
assert c.b.a.as_base().value == 42
del c
pytest.gc_collect()
del a # Should't delete while abase is still alive
pytest.gc_collect()
assert abase.value == 42
del abase, b
pytest.gc_collect()
def test_move_fallback():
from pybind11_tests.issues import get_moveissue1, get_moveissue2
m2 = get_moveissue2(2)
assert m2.value == 2
m1 = get_moveissue1(1)
assert m1.value == 1
def test_override_ref():
from pybind11_tests.issues import OverrideTest
o = OverrideTest("asdf")
# Not allowed (see associated .cpp comment)
# i = o.str_ref()
# assert o.str_ref() == "asdf"
assert o.str_value() == "asdf"
assert o.A_value().value == "hi"
a = o.A_ref()
assert a.value == "hi"
a.value = "bye"
assert a.value == "bye"
def test_operators_notimplemented(capture):
from pybind11_tests.issues import OpTest1, OpTest2
with capture:
c1, c2 = OpTest1(), OpTest2()
c1 + c1
c2 + c2
c2 + c1
c1 + c2
assert capture == """
Add OpTest1 with OpTest1
Add OpTest2 with OpTest2
Add OpTest2 with OpTest1
Add OpTest2 with OpTest1
"""
def test_iterator_rvpolicy():
""" Issue 388: Can't make iterators via make_iterator() with different r/v policies """
from pybind11_tests.issues import make_iterator_1
from pybind11_tests.issues import make_iterator_2
assert list(make_iterator_1()) == [1, 2, 3]
assert list(make_iterator_2()) == [1, 2, 3]
assert not isinstance(make_iterator_1(), type(make_iterator_2()))
def test_dupe_assignment():
""" Issue 461: overwriting a class with a function """
from pybind11_tests.issues import dupe_exception_failures
assert dupe_exception_failures() == []
def test_enable_shared_from_this_with_reference_rvp():
""" Issue #471: shared pointer instance not dellocated """
from pybind11_tests import SharedParent, SharedChild
parent = SharedParent()
child = parent.get_child()
cstats = ConstructorStats.get(SharedChild)
assert cstats.alive() == 1
del child, parent
assert cstats.alive() == 0
def test_non_destructed_holders():
""" Issue #478: unique ptrs constructed and freed without destruction """
from pybind11_tests import SpecialHolderObj
a = SpecialHolderObj(123)
b = a.child()
assert a.val == 123
assert b.val == 124
cstats = SpecialHolderObj.holder_cstats()
assert cstats.alive() == 1
del b
assert cstats.alive() == 1
del a
assert cstats.alive() == 0
def test_complex_cast(capture):
""" Issue #484: number conversion generates unhandled exceptions """
from pybind11_tests.issues import test_complex
with capture:
test_complex(1)
test_complex(2j)
assert capture == """
1.0
(0.0, 2.0)
"""
def test_inheritance_override_def_static():
from pybind11_tests.issues import MyBase, MyDerived
b = MyBase.make()
d1 = MyDerived.make2()
d2 = MyDerived.make()
assert isinstance(b, MyBase)
assert isinstance(d1, MyDerived)
assert isinstance(d2, MyDerived)

View File

@ -170,6 +170,13 @@ int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
int none4(std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; } int none4(std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; }
int none5(std::shared_ptr<NoneTester> obj) { return obj ? obj->answer : -1; } int none5(std::shared_ptr<NoneTester> obj) { return obj ? obj->answer : -1; }
struct StrIssue {
int val = -1;
StrIssue() = default;
StrIssue(int i) : val{i} {}
};
test_initializer methods_and_attributes([](py::module &m) { test_initializer methods_and_attributes([](py::module &m) {
py::class_<ExampleMandA> emna(m, "ExampleMandA"); py::class_<ExampleMandA> emna(m, "ExampleMandA");
emna.def(py::init<>()) emna.def(py::init<>())
@ -315,6 +322,8 @@ test_initializer methods_and_attributes([](py::module &m) {
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i"));
m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert());
/// Issue/PR #648: bad arg default debugging output /// Issue/PR #648: bad arg default debugging output
#if !defined(NDEBUG) #if !defined(NDEBUG)
@ -344,4 +353,11 @@ test_initializer methods_and_attributes([](py::module &m) {
m.def("ok_none4", &none4, py::arg().none(true)); m.def("ok_none4", &none4, py::arg().none(true));
m.def("ok_none5", &none5); m.def("ok_none5", &none5);
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
py::class_<StrIssue>(m, "StrIssue")
.def(py::init<int>())
.def(py::init<>())
.def("__str__", [](const StrIssue &si) {
return "StrIssue[" + std::to_string(si.val) + "]"; }
);
}); });

View File

@ -304,9 +304,9 @@ def test_cyclic_gc():
def test_noconvert_args(msg): def test_noconvert_args(msg):
from pybind11_tests import ArgInspector, arg_inspect_func, floats_only, floats_preferred import pybind11_tests as m
a = ArgInspector() a = m.ArgInspector()
assert msg(a.f("hi")) == """ assert msg(a.f("hi")) == """
loading ArgInspector1 argument WITH conversion allowed. Argument value = hi loading ArgInspector1 argument WITH conversion allowed. Argument value = hi
""" """
@ -330,15 +330,15 @@ def test_noconvert_args(msg):
""" """
assert (a.h("arg 1") == assert (a.h("arg 1") ==
"loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1") "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1")
assert msg(arg_inspect_func("A1", "A2")) == """ assert msg(m.arg_inspect_func("A1", "A2")) == """
loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 loading ArgInspector2 argument WITH conversion allowed. Argument value = A1
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2
""" """
assert floats_preferred(4) == 2.0 assert m.floats_preferred(4) == 2.0
assert floats_only(4.0) == 2.0 assert m.floats_only(4.0) == 2.0
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
floats_only(4) m.floats_only(4)
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
floats_only(): incompatible function arguments. The following argument types are supported: floats_only(): incompatible function arguments. The following argument types are supported:
1. (f: float) -> float 1. (f: float) -> float
@ -346,6 +346,27 @@ def test_noconvert_args(msg):
Invoked with: 4 Invoked with: 4
""" """
assert m.ints_preferred(4) == 2
assert m.ints_preferred(True) == 0
with pytest.raises(TypeError) as excinfo:
m.ints_preferred(4.0)
assert msg(excinfo.value) == """
ints_preferred(): incompatible function arguments. The following argument types are supported:
1. (i: int) -> int
Invoked with: 4.0
""" # noqa: E501 line too long
assert m.ints_only(4) == 2
with pytest.raises(TypeError) as excinfo:
m.ints_only(4.0)
assert msg(excinfo.value) == """
ints_only(): incompatible function arguments. The following argument types are supported:
1. (i: int) -> int
Invoked with: 4.0
"""
def test_bad_arg_default(msg): def test_bad_arg_default(msg):
from pybind11_tests import debug_enabled, bad_arg_def_named, bad_arg_def_unnamed from pybind11_tests import debug_enabled, bad_arg_def_named, bad_arg_def_unnamed
@ -371,7 +392,7 @@ def test_bad_arg_default(msg):
) )
def test_accepts_none(): def test_accepts_none(msg):
from pybind11_tests import (NoneTester, from pybind11_tests import (NoneTester,
no_none1, no_none2, no_none3, no_none4, no_none5, no_none1, no_none2, no_none3, no_none4, no_none5,
ok_none1, ok_none2, ok_none3, ok_none4, ok_none5) ok_none1, ok_none2, ok_none3, ok_none4, ok_none5)
@ -407,9 +428,32 @@ def test_accepts_none():
# The first one still raises because you can't pass None as a lvalue reference arg: # The first one still raises because you can't pass None as a lvalue reference arg:
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
assert ok_none1(None) == -1 assert ok_none1(None) == -1
assert "incompatible function arguments" in str(excinfo.value) assert msg(excinfo.value) == """
ok_none1(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.NoneTester) -> int
Invoked with: None
"""
# The rest take the argument as pointer or holder, and accept None: # The rest take the argument as pointer or holder, and accept None:
assert ok_none2(None) == -1 assert ok_none2(None) == -1
assert ok_none3(None) == -1 assert ok_none3(None) == -1
assert ok_none4(None) == -1 assert ok_none4(None) == -1
assert ok_none5(None) == -1 assert ok_none5(None) == -1
def test_str_issue(msg):
"""#283: __str__ called on uninitialized instance when constructor arguments invalid"""
from pybind11_tests import StrIssue
assert str(StrIssue(3)) == "StrIssue[3]"
with pytest.raises(TypeError) as excinfo:
str(StrIssue("no", "such", "constructor"))
assert msg(excinfo.value) == """
__init__(): incompatible constructor arguments. The following argument types are supported:
1. m.StrIssue(arg0: int)
2. m.StrIssue()
Invoked with: 'no', 'such', 'constructor'
"""

View File

@ -55,4 +55,47 @@ test_initializer modules([](py::module &m) {
.def_readwrite("a2", &B::a2); .def_readwrite("a2", &B::a2);
m.attr("OD") = py::module::import("collections").attr("OrderedDict"); m.attr("OD") = py::module::import("collections").attr("OrderedDict");
// Registering two things with the same name
m.def("duplicate_registration", []() {
class Dupe1 { };
class Dupe2 { };
class Dupe3 { };
class DupeException { };
auto dm = py::module("dummy");
auto failures = py::list();
py::class_<Dupe1>(dm, "Dupe1");
py::class_<Dupe2>(dm, "Dupe2");
dm.def("dupe1_factory", []() { return Dupe1(); });
py::exception<DupeException>(dm, "DupeException");
try {
py::class_<Dupe1>(dm, "Dupe1");
failures.append("Dupe1 class");
} catch (std::runtime_error &) {}
try {
dm.def("Dupe1", []() { return Dupe1(); });
failures.append("Dupe1 function");
} catch (std::runtime_error &) {}
try {
py::class_<Dupe3>(dm, "dupe1_factory");
failures.append("dupe1_factory");
} catch (std::runtime_error &) {}
try {
py::exception<Dupe3>(dm, "Dupe2");
failures.append("Dupe2");
} catch (std::runtime_error &) {}
try {
dm.def("DupeException", []() { return 30; });
failures.append("DupeException1");
} catch (std::runtime_error &) {}
try {
py::class_<DupeException>(dm, "DupeException");
failures.append("DupeException2");
} catch (std::runtime_error &) {}
return failures;
});
}); });

View File

@ -61,3 +61,10 @@ def test_pydoc():
assert pybind11_tests.__doc__ == "pybind11 test module" assert pybind11_tests.__doc__ == "pybind11 test module"
assert pydoc.text.docmodule(pybind11_tests) assert pydoc.text.docmodule(pybind11_tests)
def test_duplicate_registration():
"""Registering two things with the same name"""
from pybind11_tests import duplicate_registration
assert duplicate_registration() == []

View File

@ -56,7 +56,38 @@ private:
float x, y; float x, y;
}; };
test_initializer operator_overloading([](py::module &m) { class C1 { };
class C2 { };
int operator+(const C1 &, const C1 &) { return 11; }
int operator+(const C2 &, const C2 &) { return 22; }
int operator+(const C2 &, const C1 &) { return 21; }
int operator+(const C1 &, const C2 &) { return 12; }
struct NestABase {
int value = -2;
};
struct NestA : NestABase {
int value = 3;
NestA& operator+=(int i) { value += i; return *this; }
};
struct NestB {
NestA a;
int value = 4;
NestB& operator-=(int i) { value -= i; return *this; }
};
struct NestC {
NestB b;
int value = 5;
NestC& operator*=(int i) { value *= i; return *this; }
};
test_initializer operator_overloading([](py::module &pm) {
auto m = pm.def_submodule("operators");
py::class_<Vector2>(m, "Vector2") py::class_<Vector2>(m, "Vector2")
.def(py::init<float, float>()) .def(py::init<float, float>())
.def(py::self + py::self) .def(py::self + py::self)
@ -81,4 +112,41 @@ test_initializer operator_overloading([](py::module &m) {
; ;
m.attr("Vector") = m.attr("Vector2"); m.attr("Vector") = m.attr("Vector2");
// #393: need to return NotSupported to ensure correct arithmetic operator behavior
py::class_<C1>(m, "C1")
.def(py::init<>())
.def(py::self + py::self);
py::class_<C2>(m, "C2")
.def(py::init<>())
.def(py::self + py::self)
.def("__add__", [](const C2& c2, const C1& c1) { return c2 + c1; })
.def("__radd__", [](const C2& c2, const C1& c1) { return c1 + c2; });
// #328: first member in a class can't be used in operators
py::class_<NestABase>(m, "NestABase")
.def(py::init<>())
.def_readwrite("value", &NestABase::value);
py::class_<NestA>(m, "NestA")
.def(py::init<>())
.def(py::self += int())
.def("as_base", [](NestA &a) -> NestABase& {
return (NestABase&) a;
}, py::return_value_policy::reference_internal);
py::class_<NestB>(m, "NestB")
.def(py::init<>())
.def(py::self -= int())
.def_readwrite("a", &NestB::a);
py::class_<NestC>(m, "NestC")
.def(py::init<>())
.def(py::self *= int())
.def_readwrite("b", &NestC::b);
m.def("get_NestA", [](const NestA &a) { return a.value; });
m.def("get_NestB", [](const NestB &b) { return b.value; });
m.def("get_NestC", [](const NestC &c) { return c.value; });
}); });

View File

@ -1,5 +1,9 @@
import pytest
from pybind11_tests import ConstructorStats
def test_operator_overloading(): def test_operator_overloading():
from pybind11_tests import Vector2, Vector, ConstructorStats from pybind11_tests.operators import Vector2, Vector
v1 = Vector2(1, 2) v1 = Vector2(1, 2)
v2 = Vector(3, -1) v2 = Vector(3, -1)
@ -51,3 +55,53 @@ def test_operator_overloading():
assert cstats.move_constructions >= 10 assert cstats.move_constructions >= 10
assert cstats.copy_assignments == 0 assert cstats.copy_assignments == 0
assert cstats.move_assignments == 0 assert cstats.move_assignments == 0
def test_operators_notimplemented():
"""#393: need to return NotSupported to ensure correct arithmetic operator behavior"""
from pybind11_tests.operators import C1, C2
c1, c2 = C1(), C2()
assert c1 + c1 == 11
assert c2 + c2 == 22
assert c2 + c1 == 21
assert c1 + c2 == 12
def test_nested():
"""#328: first member in a class can't be used in operators"""
from pybind11_tests.operators import NestA, NestB, NestC, get_NestA, get_NestB, get_NestC
a = NestA()
b = NestB()
c = NestC()
a += 10
assert get_NestA(a) == 13
b.a += 100
assert get_NestA(b.a) == 103
c.b.a += 1000
assert get_NestA(c.b.a) == 1003
b -= 1
assert get_NestB(b) == 3
c.b -= 3
assert get_NestB(c.b) == 1
c *= 7
assert get_NestC(c) == 35
abase = a.as_base()
assert abase.value == -2
a.as_base().value += 44
assert abase.value == 42
assert c.b.a.as_base().value == -2
c.b.a.as_base().value += 44
assert c.b.a.as_base().value == 42
del c
pytest.gc_collect()
del a # Should't delete while abase is still alive
pytest.gc_collect()
assert abase.value == 42
del abase, b
pytest.gc_collect()

View File

@ -11,6 +11,7 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <pybind11/complex.h>
#ifdef _WIN32 #ifdef _WIN32
# include <io.h> # include <io.h>
@ -215,6 +216,17 @@ std::vector<std::reference_wrapper<IncrIntWrapper>> incr_int_wrappers() {
return r; return r;
}; };
/// Issue #528: templated constructor
struct TplCtorClass {
template <typename T> TplCtorClass(const T &) { }
bool operator==(const TplCtorClass &) const { return true; }
};
namespace std {
template <>
struct hash<TplCtorClass> { size_t operator()(const TplCtorClass &) const { return 0; } };
}
test_initializer python_types([](py::module &m) { test_initializer python_types([](py::module &m) {
/* No constructor is explicitly defined below. An exception is raised when /* No constructor is explicitly defined below. An exception is raised when
trying to construct it directly from Python */ trying to construct it directly from Python */
@ -501,6 +513,8 @@ test_initializer python_types([](py::module &m) {
); );
}); });
m.def("string_roundtrip", [](const char *s) { return s; });
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/; char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/;
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00;
@ -661,6 +675,19 @@ test_initializer python_types([](py::module &m) {
return l; return l;
}); });
/// Issue #484: number conversion generates unhandled exceptions
m.def("test_complex", [](float x) { return "{}"_s.format(x); });
m.def("test_complex", [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
/// Issue #528: templated constructor
m.def("tpl_ctor_vector", [](std::vector<TplCtorClass> &) {});
m.def("tpl_ctor_map", [](std::unordered_map<TplCtorClass, TplCtorClass> &) {});
m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
#if defined(PYBIND11_HAS_OPTIONAL)
m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {});
#endif
}); });
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -452,6 +452,12 @@ def test_implicit_casting():
assert z['l'] == [3, 6, 9, 12, 15] assert z['l'] == [3, 6, 9, 12, 15]
def test_simple_string():
from pybind11_tests import string_roundtrip
assert string_roundtrip("const char *") == "const char *"
def test_unicode_conversion(): def test_unicode_conversion():
"""Tests unicode conversion and error reporting.""" """Tests unicode conversion and error reporting."""
import pybind11_tests import pybind11_tests
@ -699,3 +705,11 @@ def test_reference_wrapper():
assert refwrap_iiw(IncrIntWrapper(5)) == 5 assert refwrap_iiw(IncrIntWrapper(5)) == 5
assert refwrap_call_iiw(IncrIntWrapper(10), refwrap_iiw) == [10, 10, 10, 10] assert refwrap_call_iiw(IncrIntWrapper(10), refwrap_iiw) == [10, 10, 10, 10]
def test_complex_cast():
"""#484: number conversion generates unhandled exceptions"""
from pybind11_tests import test_complex
assert test_complex(1) == "1.0"
assert test_complex(2j) == "(0.0, 2.0)"

View File

@ -351,4 +351,14 @@ test_initializer sequences_and_iterators([](py::module &pm) {
m.def("tuple_iterator", [](py::tuple x) { return test_random_access_iterator(x); }); m.def("tuple_iterator", [](py::tuple x) { return test_random_access_iterator(x); });
m.def("list_iterator", [](py::list x) { return test_random_access_iterator(x); }); m.def("list_iterator", [](py::list x) { return test_random_access_iterator(x); });
m.def("sequence_iterator", [](py::sequence x) { return test_random_access_iterator(x); }); m.def("sequence_iterator", [](py::sequence x) { return test_random_access_iterator(x); });
// #181: iterator passthrough did not compile
m.def("iterator_passthrough", [](py::iterator s) -> py::iterator {
return py::make_iterator(std::begin(s), std::end(s));
});
// #388: Can't make iterators via make_iterator() with different r/v policies
static std::vector<int> list = { 1, 2, 3 };
m.def("make_iterator_1", []() { return py::make_iterator<py::return_value_policy::copy>(list); });
m.def("make_iterator_2", []() { return py::make_iterator<py::return_value_policy::automatic>(list); });
}); });

View File

@ -147,3 +147,20 @@ def test_python_iterator_in_cpp():
assert all(m.tuple_iterator(tuple(r))) assert all(m.tuple_iterator(tuple(r)))
assert all(m.list_iterator(list(r))) assert all(m.list_iterator(list(r)))
assert all(m.sequence_iterator(r)) assert all(m.sequence_iterator(r))
def test_iterator_passthrough():
"""#181: iterator passthrough did not compile"""
from pybind11_tests.sequences_and_iterators import iterator_passthrough
assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15]
def test_iterator_rvp():
"""#388: Can't make iterators via make_iterator() with different r/v policies """
import pybind11_tests.sequences_and_iterators as m
assert list(m.make_iterator_1()) == [1, 2, 3]
assert list(m.make_iterator_2()) == [1, 2, 3]
assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2()))

View File

@ -259,6 +259,18 @@ public:
PYBIND11_DECLARE_HOLDER_TYPE(T, CustomUniquePtr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, CustomUniquePtr<T>);
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;
};
test_initializer smart_ptr_and_references([](py::module &pm) { test_initializer smart_ptr_and_references([](py::module &pm) {
auto m = pm.def_submodule("smart_ptr"); auto m = pm.def_submodule("smart_ptr");
@ -309,4 +321,21 @@ test_initializer smart_ptr_and_references([](py::module &pm) {
py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder") py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder")
.def(py::init<>()) .def(py::init<>())
.def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {}); .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {});
// #187: issue involving std::shared_ptr<> return value policy & garbage collection
py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase");
py::class_<ElementA, ElementBase, std::shared_ptr<ElementA>>(m, "ElementA")
.def(py::init<int>())
.def("value", &ElementA::value);
py::class_<ElementList, std::shared_ptr<ElementList>>(m, "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;
});
}); });

View File

@ -234,3 +234,15 @@ def test_smart_ptr_from_default():
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
HeldByDefaultHolder.load_shared_ptr(instance) HeldByDefaultHolder.load_shared_ptr(instance)
assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo) assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo)
def test_shared_ptr_gc():
"""#187: issue involving std::shared_ptr<> return value policy & garbage collection"""
from pybind11_tests.smart_ptr import ElementList, ElementA
el = ElementList()
for i in range(10):
el.add(ElementA(i))
pytest.gc_collect()
for i, v in enumerate(el.get()):
assert i == v.value()

View File

@ -311,6 +311,16 @@ void initialize_inherited_virtuals(py::module &m) {
}; };
struct Base {
/* for some reason MSVC2015 can't compile this if the function is pure virtual */
virtual std::string dispatch() const { return {}; };
};
struct DispatchIssue : Base {
virtual std::string dispatch() const {
PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */);
}
};
test_initializer virtual_functions([](py::module &m) { test_initializer virtual_functions([](py::module &m) {
py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt") py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt")
@ -341,4 +351,51 @@ test_initializer virtual_functions([](py::module &m) {
m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>); m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
initialize_inherited_virtuals(m); initialize_inherited_virtuals(m);
// #159: virtual function dispatch has problems with similar-named functions
py::class_<Base, DispatchIssue>(m, "DispatchIssue")
.def(py::init<>())
.def("dispatch", &Base::dispatch);
m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
// #392/397: overridding reference-returning functions
class OverrideTest {
public:
struct A { std::string value = "hi"; };
std::string v;
A a;
explicit OverrideTest(const std::string &v) : v{v} {}
virtual std::string str_value() { return v; }
virtual std::string &str_ref() { return v; }
virtual A A_value() { return a; }
virtual A &A_ref() { return a; }
};
class PyOverrideTest : public OverrideTest {
public:
using OverrideTest::OverrideTest;
std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); }
// Not allowed (uncommenting should hit a static_assert failure): we can't get a reference
// to a python numeric value, since we only copy values in the numeric type caster:
// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); }
// But we can work around it like this:
private:
std::string _tmp;
std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); }
public:
std::string &str_ref() override { return _tmp = str_ref_helper(); }
A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); }
A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); }
};
py::class_<OverrideTest::A>(m, "OverrideTest_A")
.def_readwrite("value", &OverrideTest::A::value);
py::class_<OverrideTest, PyOverrideTest>(m, "OverrideTest")
.def(py::init<const std::string &>())
.def("str_value", &OverrideTest::str_value)
// .def("str_ref", &OverrideTest::str_ref)
.def("A_value", &OverrideTest::A_value)
.def("A_ref", &OverrideTest::A_ref);
}); });

View File

@ -257,3 +257,42 @@ def test_move_support():
assert mv_stats.copy_constructions == 1 assert mv_stats.copy_constructions == 1
assert nc_stats.move_constructions >= 0 assert nc_stats.move_constructions >= 0
assert mv_stats.move_constructions >= 0 assert mv_stats.move_constructions >= 0
def test_dispatch_issue(msg):
"""#159: virtual function dispatch has problems with similar-named functions"""
from pybind11_tests import DispatchIssue, dispatch_issue_go
class PyClass1(DispatchIssue):
def dispatch(self):
return "Yay.."
class PyClass2(DispatchIssue):
def dispatch(self):
with pytest.raises(RuntimeError) as excinfo:
super(PyClass2, self).dispatch()
assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
p = PyClass1()
return dispatch_issue_go(p)
b = PyClass2()
assert dispatch_issue_go(b) == "Yay.."
def test_override_ref():
"""#392/397: overridding reference-returning functions"""
from pybind11_tests import OverrideTest
o = OverrideTest("asdf")
# Not allowed (see associated .cpp comment)
# i = o.str_ref()
# assert o.str_ref() == "asdf"
assert o.str_value() == "asdf"
assert o.A_value().value == "hi"
a = o.A_ref()
assert a.value == "hi"
a.value = "bye"
assert a.value == "bye"