pybind11/tests/test_virtual_functions.py

257 lines
8.1 KiB
Python
Raw Normal View History

import pytest
import pybind11_tests
from pybind11_tests import ConstructorStats
def test_override(capture, msg):
from pybind11_tests import (ExampleVirt, runExampleVirt, runExampleVirtVirtual,
runExampleVirtBool)
class ExtendedExampleVirt(ExampleVirt):
def __init__(self, state):
super(ExtendedExampleVirt, self).__init__(state + 1)
self.data = "Hello world"
def run(self, value):
print('ExtendedExampleVirt::run(%i), calling parent..' % value)
return super(ExtendedExampleVirt, self).run(value + 1)
def run_bool(self):
print('ExtendedExampleVirt::run_bool()')
return False
def get_string1(self):
return "override1"
def pure_virtual(self):
print('ExtendedExampleVirt::pure_virtual(): %s' % self.data)
class ExtendedExampleVirt2(ExtendedExampleVirt):
def __init__(self, state):
super(ExtendedExampleVirt2, self).__init__(state + 1)
def get_string2(self):
return "override2"
ex12 = ExampleVirt(10)
with capture:
assert runExampleVirt(ex12, 20) == 30
assert capture == """
Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
""" # noqa: E501 line too long
with pytest.raises(RuntimeError) as excinfo:
runExampleVirtVirtual(ex12)
assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
ex12p = ExtendedExampleVirt(10)
with capture:
assert runExampleVirt(ex12p, 20) == 32
assert capture == """
ExtendedExampleVirt::run(20), calling parent..
Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
""" # noqa: E501 line too long
with capture:
assert runExampleVirtBool(ex12p) is False
assert capture == "ExtendedExampleVirt::run_bool()"
with capture:
runExampleVirtVirtual(ex12p)
assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"
ex12p2 = ExtendedExampleVirt2(15)
with capture:
assert runExampleVirt(ex12p2, 50) == 68
assert capture == """
ExtendedExampleVirt::run(50), calling parent..
Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
""" # noqa: E501 line too long
cstats = ConstructorStats.get(ExampleVirt)
assert cstats.alive() == 3
del ex12, ex12p, ex12p2
assert cstats.alive() == 0
assert cstats.values() == ['10', '11', '17']
assert cstats.copy_constructions == 0
assert cstats.move_constructions >= 0
def test_inheriting_repeat():
from pybind11_tests import A_Repeat, B_Repeat, C_Repeat, D_Repeat, A_Tpl, B_Tpl, C_Tpl, D_Tpl
class AR(A_Repeat):
def unlucky_number(self):
return 99
class AT(A_Tpl):
def unlucky_number(self):
return 999
obj = AR()
assert obj.say_something(3) == "hihihi"
assert obj.unlucky_number() == 99
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "hi 99"
obj = AT()
assert obj.say_something(3) == "hihihi"
assert obj.unlucky_number() == 999
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "hi 999"
for obj in [B_Repeat(), B_Tpl()]:
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 13
assert obj.lucky_number() == 7.0
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 13"
for obj in [C_Repeat(), C_Tpl()]:
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 888.0
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 4444"
class CR(C_Repeat):
def lucky_number(self):
return C_Repeat.lucky_number(self) + 1.25
obj = CR()
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 889.25
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 4444"
class CT(C_Tpl):
pass
obj = CT()
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 888.0
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 4444"
class CCR(CR):
def lucky_number(self):
return CR.lucky_number(self) * 10
obj = CCR()
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 8892.5
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 4444"
class CCT(CT):
def lucky_number(self):
return CT.lucky_number(self) * 1000
obj = CCT()
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 888000.0
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 4444"
class DR(D_Repeat):
def unlucky_number(self):
return 123
def lucky_number(self):
return 42.0
for obj in [D_Repeat(), D_Tpl()]:
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 4444
assert obj.lucky_number() == 888.0
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 4444"
obj = DR()
assert obj.say_something(3) == "B says hi 3 times"
assert obj.unlucky_number() == 123
assert obj.lucky_number() == 42.0
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.say_everything() == "B says hi 1 times 123"
class DT(D_Tpl):
def say_something(self, times):
return "DT says:" + (' quack' * times)
def unlucky_number(self):
return 1234
def lucky_number(self):
return -4.25
obj = DT()
assert obj.say_something(3) == "DT says: quack quack quack"
assert obj.unlucky_number() == 1234
assert obj.lucky_number() == -4.25
assert obj.say_everything() == "DT says: quack 1234"
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
class DT2(DT):
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
def say_something(self, times):
return "DT2: " + ('QUACK' * times)
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
def unlucky_number(self):
return -3
class BT(B_Tpl):
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
def say_something(self, times):
return "BT" * times
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
def unlucky_number(self):
return -7
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
def lucky_number(self):
return -1.375
obj = BT()
assert obj.say_something(3) == "BTBTBT"
Fix template trampoline overload lookup failure Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
2016-08-29 22:16:46 +00:00
assert obj.unlucky_number() == -7
assert obj.lucky_number() == -1.375
assert obj.say_everything() == "BT -7"
@pytest.mark.skipif(not hasattr(pybind11_tests, 'NCVirt'),
reason="NCVirt test broken on ICPC")
def test_move_support():
from pybind11_tests import NCVirt, NonCopyable, Movable
class NCVirtExt(NCVirt):
def get_noncopyable(self, a, b):
# Constructs and returns a new instance:
nc = NonCopyable(a * a, b * b)
return nc
def get_movable(self, a, b):
# Return a referenced copy
self.movable = Movable(a, b)
return self.movable
class NCVirtExt2(NCVirt):
def get_noncopyable(self, a, b):
# Keep a reference: this is going to throw an exception
self.nc = NonCopyable(a, b)
return self.nc
def get_movable(self, a, b):
# Return a new instance without storing it
return Movable(a, b)
ncv1 = NCVirtExt()
assert ncv1.print_nc(2, 3) == "36"
assert ncv1.print_movable(4, 5) == "9"
ncv2 = NCVirtExt2()
assert ncv2.print_movable(7, 7) == "14"
# Don't check the exception message here because it differs under debug/non-debug mode
with pytest.raises(RuntimeError):
ncv2.print_nc(9, 9)
nc_stats = ConstructorStats.get(NonCopyable)
mv_stats = ConstructorStats.get(Movable)
assert nc_stats.alive() == 1
assert mv_stats.alive() == 1
del ncv1, ncv2
assert nc_stats.alive() == 0
assert mv_stats.alive() == 0
assert nc_stats.values() == ['4', '9', '9', '9']
assert mv_stats.values() == ['4', '5', '7', '7']
assert nc_stats.copy_constructions == 0
assert mv_stats.copy_constructions == 1
assert nc_stats.move_constructions >= 0
assert mv_stats.move_constructions >= 0