blob: b91ebfa3ec72e533023acd3158166077f1f6f48f [file] [log] [blame]
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02001import pytest
Dean Moldovan0bc272b2017-06-22 23:42:11 +02002
3from pybind11_tests import virtual_functions as m
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02004from pybind11_tests import ConstructorStats
5
6
7def test_override(capture, msg):
Dean Moldovan0bc272b2017-06-22 23:42:11 +02008 class ExtendedExampleVirt(m.ExampleVirt):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02009 def __init__(self, state):
10 super(ExtendedExampleVirt, self).__init__(state + 1)
11 self.data = "Hello world"
12
13 def run(self, value):
14 print('ExtendedExampleVirt::run(%i), calling parent..' % value)
15 return super(ExtendedExampleVirt, self).run(value + 1)
16
17 def run_bool(self):
18 print('ExtendedExampleVirt::run_bool()')
19 return False
20
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040021 def get_string1(self):
22 return "override1"
23
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020024 def pure_virtual(self):
25 print('ExtendedExampleVirt::pure_virtual(): %s' % self.data)
26
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040027 class ExtendedExampleVirt2(ExtendedExampleVirt):
28 def __init__(self, state):
29 super(ExtendedExampleVirt2, self).__init__(state + 1)
30
31 def get_string2(self):
32 return "override2"
33
Dean Moldovan0bc272b2017-06-22 23:42:11 +020034 ex12 = m.ExampleVirt(10)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020035 with capture:
Dean Moldovan0bc272b2017-06-22 23:42:11 +020036 assert m.runExampleVirt(ex12, 20) == 30
Dean Moldovan76e993a2016-12-13 00:59:28 +010037 assert capture == """
38 Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
39 """ # noqa: E501 line too long
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020040
41 with pytest.raises(RuntimeError) as excinfo:
Dean Moldovan0bc272b2017-06-22 23:42:11 +020042 m.runExampleVirtVirtual(ex12)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020043 assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
44
45 ex12p = ExtendedExampleVirt(10)
46 with capture:
Dean Moldovan0bc272b2017-06-22 23:42:11 +020047 assert m.runExampleVirt(ex12p, 20) == 32
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020048 assert capture == """
49 ExtendedExampleVirt::run(20), calling parent..
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040050 Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
Dean Moldovan76e993a2016-12-13 00:59:28 +010051 """ # noqa: E501 line too long
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020052 with capture:
Dean Moldovan0bc272b2017-06-22 23:42:11 +020053 assert m.runExampleVirtBool(ex12p) is False
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020054 assert capture == "ExtendedExampleVirt::run_bool()"
55 with capture:
Dean Moldovan0bc272b2017-06-22 23:42:11 +020056 m.runExampleVirtVirtual(ex12p)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020057 assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"
58
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040059 ex12p2 = ExtendedExampleVirt2(15)
60 with capture:
Dean Moldovan0bc272b2017-06-22 23:42:11 +020061 assert m.runExampleVirt(ex12p2, 50) == 68
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040062 assert capture == """
63 ExtendedExampleVirt::run(50), calling parent..
64 Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
Dean Moldovan76e993a2016-12-13 00:59:28 +010065 """ # noqa: E501 line too long
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040066
Dean Moldovan0bc272b2017-06-22 23:42:11 +020067 cstats = ConstructorStats.get(m.ExampleVirt)
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040068 assert cstats.alive() == 3
69 del ex12, ex12p, ex12p2
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020070 assert cstats.alive() == 0
Jason Rhinelander7dfb9322016-09-08 14:49:43 -040071 assert cstats.values() == ['10', '11', '17']
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020072 assert cstats.copy_constructions == 0
73 assert cstats.move_constructions >= 0
74
75
Dean Moldovan0bc272b2017-06-22 23:42:11 +020076def test_alias_delay_initialization1(capture):
77 """`A` only initializes its trampoline class when we inherit from it
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020078
Dean Moldovan0bc272b2017-06-22 23:42:11 +020079 If we just create and use an A instance directly, the trampoline initialization is
80 bypassed and we only initialize an A() instead (for performance reasons).
81 """
82 class B(m.A):
83 def __init__(self):
84 super(B, self).__init__()
85
86 def f(self):
87 print("In python f()")
88
89 # C++ version
90 with capture:
91 a = m.A()
92 m.call_f(a)
93 del a
94 pytest.gc_collect()
95 assert capture == "A.f()"
96
97 # Python version
98 with capture:
99 b = B()
100 m.call_f(b)
101 del b
102 pytest.gc_collect()
103 assert capture == """
104 PyA.PyA()
105 PyA.f()
106 In python f()
107 PyA.~PyA()
108 """
109
110
111def test_alias_delay_initialization2(capture):
112 """`A2`, unlike the above, is configured to always initialize the alias
113
114 While the extra initialization and extra class layer has small virtual dispatch
115 performance penalty, it also allows us to do more things with the trampoline
116 class such as defining local variables and performing construction/destruction.
117 """
118 class B2(m.A2):
119 def __init__(self):
120 super(B2, self).__init__()
121
122 def f(self):
123 print("In python B2.f()")
124
125 # No python subclass version
126 with capture:
127 a2 = m.A2()
128 m.call_f(a2)
129 del a2
130 pytest.gc_collect()
Jason Rhinelander464d9892017-06-12 21:52:48 -0400131 a3 = m.A2(1)
132 m.call_f(a3)
133 del a3
134 pytest.gc_collect()
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200135 assert capture == """
136 PyA2.PyA2()
137 PyA2.f()
138 A2.f()
139 PyA2.~PyA2()
Jason Rhinelander464d9892017-06-12 21:52:48 -0400140 PyA2.PyA2()
141 PyA2.f()
142 A2.f()
143 PyA2.~PyA2()
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200144 """
145
146 # Python subclass version
147 with capture:
148 b2 = B2()
149 m.call_f(b2)
150 del b2
151 pytest.gc_collect()
152 assert capture == """
153 PyA2.PyA2()
154 PyA2.f()
155 In python B2.f()
156 PyA2.~PyA2()
157 """
158
159
Jason Rhinelander391c7542017-07-25 16:47:36 -0400160# PyPy: Reference count > 1 causes call with noncopyable instance
161# to fail in ncv1.print_nc()
162@pytest.unsupported_on_pypy
163@pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC")
164def test_move_support():
165 class NCVirtExt(m.NCVirt):
166 def get_noncopyable(self, a, b):
167 # Constructs and returns a new instance:
168 nc = m.NonCopyable(a * a, b * b)
169 return nc
170
171 def get_movable(self, a, b):
172 # Return a referenced copy
173 self.movable = m.Movable(a, b)
174 return self.movable
175
176 class NCVirtExt2(m.NCVirt):
177 def get_noncopyable(self, a, b):
178 # Keep a reference: this is going to throw an exception
179 self.nc = m.NonCopyable(a, b)
180 return self.nc
181
182 def get_movable(self, a, b):
183 # Return a new instance without storing it
184 return m.Movable(a, b)
185
186 ncv1 = NCVirtExt()
187 assert ncv1.print_nc(2, 3) == "36"
188 assert ncv1.print_movable(4, 5) == "9"
189 ncv2 = NCVirtExt2()
190 assert ncv2.print_movable(7, 7) == "14"
191 # Don't check the exception message here because it differs under debug/non-debug mode
192 with pytest.raises(RuntimeError):
193 ncv2.print_nc(9, 9)
194
195 nc_stats = ConstructorStats.get(m.NonCopyable)
196 mv_stats = ConstructorStats.get(m.Movable)
197 assert nc_stats.alive() == 1
198 assert mv_stats.alive() == 1
199 del ncv1, ncv2
200 assert nc_stats.alive() == 0
201 assert mv_stats.alive() == 0
202 assert nc_stats.values() == ['4', '9', '9', '9']
203 assert mv_stats.values() == ['4', '5', '7', '7']
204 assert nc_stats.copy_constructions == 0
205 assert mv_stats.copy_constructions == 1
206 assert nc_stats.move_constructions >= 0
207 assert mv_stats.move_constructions >= 0
208
209
210def test_dispatch_issue(msg):
211 """#159: virtual function dispatch has problems with similar-named functions"""
212 class PyClass1(m.DispatchIssue):
213 def dispatch(self):
214 return "Yay.."
215
216 class PyClass2(m.DispatchIssue):
217 def dispatch(self):
218 with pytest.raises(RuntimeError) as excinfo:
219 super(PyClass2, self).dispatch()
220 assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
221
222 p = PyClass1()
223 return m.dispatch_issue_go(p)
224
225 b = PyClass2()
226 assert m.dispatch_issue_go(b) == "Yay.."
227
228
229def test_override_ref():
230 """#392/397: overridding reference-returning functions"""
231 o = m.OverrideTest("asdf")
232
233 # Not allowed (see associated .cpp comment)
234 # i = o.str_ref()
235 # assert o.str_ref() == "asdf"
236 assert o.str_value() == "asdf"
237
238 assert o.A_value().value == "hi"
239 a = o.A_ref()
240 assert a.value == "hi"
241 a.value = "bye"
242 assert a.value == "bye"
243
244
245def test_inherited_virtuals():
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200246 class AR(m.A_Repeat):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200247 def unlucky_number(self):
248 return 99
249
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200250 class AT(m.A_Tpl):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200251 def unlucky_number(self):
252 return 999
253
Dean Moldovanbad17402016-11-20 21:21:54 +0100254 obj = AR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200255 assert obj.say_something(3) == "hihihi"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200256 assert obj.unlucky_number() == 99
Jason Rhinelander20978262016-08-29 18:16:46 -0400257 assert obj.say_everything() == "hi 99"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200258
Dean Moldovanbad17402016-11-20 21:21:54 +0100259 obj = AT()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200260 assert obj.say_something(3) == "hihihi"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200261 assert obj.unlucky_number() == 999
Jason Rhinelander20978262016-08-29 18:16:46 -0400262 assert obj.say_everything() == "hi 999"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200263
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200264 for obj in [m.B_Repeat(), m.B_Tpl()]:
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200265 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200266 assert obj.unlucky_number() == 13
267 assert obj.lucky_number() == 7.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400268 assert obj.say_everything() == "B says hi 1 times 13"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200269
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200270 for obj in [m.C_Repeat(), m.C_Tpl()]:
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200271 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200272 assert obj.unlucky_number() == 4444
273 assert obj.lucky_number() == 888.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400274 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200275
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200276 class CR(m.C_Repeat):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200277 def lucky_number(self):
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200278 return m.C_Repeat.lucky_number(self) + 1.25
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200279
Dean Moldovanbad17402016-11-20 21:21:54 +0100280 obj = CR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200281 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200282 assert obj.unlucky_number() == 4444
283 assert obj.lucky_number() == 889.25
Jason Rhinelander20978262016-08-29 18:16:46 -0400284 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200285
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200286 class CT(m.C_Tpl):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200287 pass
288
Dean Moldovanbad17402016-11-20 21:21:54 +0100289 obj = CT()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200290 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200291 assert obj.unlucky_number() == 4444
292 assert obj.lucky_number() == 888.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400293 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200294
Dean Moldovanbad17402016-11-20 21:21:54 +0100295 class CCR(CR):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200296 def lucky_number(self):
Dean Moldovanbad17402016-11-20 21:21:54 +0100297 return CR.lucky_number(self) * 10
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200298
Dean Moldovanbad17402016-11-20 21:21:54 +0100299 obj = CCR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200300 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200301 assert obj.unlucky_number() == 4444
302 assert obj.lucky_number() == 8892.5
Jason Rhinelander20978262016-08-29 18:16:46 -0400303 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200304
Dean Moldovanbad17402016-11-20 21:21:54 +0100305 class CCT(CT):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200306 def lucky_number(self):
Dean Moldovanbad17402016-11-20 21:21:54 +0100307 return CT.lucky_number(self) * 1000
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200308
Dean Moldovanbad17402016-11-20 21:21:54 +0100309 obj = CCT()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200310 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200311 assert obj.unlucky_number() == 4444
312 assert obj.lucky_number() == 888000.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400313 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200314
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200315 class DR(m.D_Repeat):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200316 def unlucky_number(self):
317 return 123
318
319 def lucky_number(self):
320 return 42.0
321
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200322 for obj in [m.D_Repeat(), m.D_Tpl()]:
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200323 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200324 assert obj.unlucky_number() == 4444
325 assert obj.lucky_number() == 888.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400326 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200327
Dean Moldovanbad17402016-11-20 21:21:54 +0100328 obj = DR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200329 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200330 assert obj.unlucky_number() == 123
331 assert obj.lucky_number() == 42.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400332 assert obj.say_everything() == "B says hi 1 times 123"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200333
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200334 class DT(m.D_Tpl):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200335 def say_something(self, times):
Dean Moldovanbad17402016-11-20 21:21:54 +0100336 return "DT says:" + (' quack' * times)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200337
338 def unlucky_number(self):
339 return 1234
340
341 def lucky_number(self):
342 return -4.25
343
Dean Moldovanbad17402016-11-20 21:21:54 +0100344 obj = DT()
345 assert obj.say_something(3) == "DT says: quack quack quack"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200346 assert obj.unlucky_number() == 1234
347 assert obj.lucky_number() == -4.25
Dean Moldovanbad17402016-11-20 21:21:54 +0100348 assert obj.say_everything() == "DT says: quack 1234"
Jason Rhinelander20978262016-08-29 18:16:46 -0400349
Dean Moldovanbad17402016-11-20 21:21:54 +0100350 class DT2(DT):
Jason Rhinelander20978262016-08-29 18:16:46 -0400351 def say_something(self, times):
Dean Moldovanbad17402016-11-20 21:21:54 +0100352 return "DT2: " + ('QUACK' * times)
Jason Rhinelander20978262016-08-29 18:16:46 -0400353
354 def unlucky_number(self):
355 return -3
356
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200357 class BT(m.B_Tpl):
Jason Rhinelander20978262016-08-29 18:16:46 -0400358 def say_something(self, times):
Dean Moldovanbad17402016-11-20 21:21:54 +0100359 return "BT" * times
360
Jason Rhinelander20978262016-08-29 18:16:46 -0400361 def unlucky_number(self):
362 return -7
Dean Moldovanbad17402016-11-20 21:21:54 +0100363
Jason Rhinelander20978262016-08-29 18:16:46 -0400364 def lucky_number(self):
365 return -1.375
366
Dean Moldovanbad17402016-11-20 21:21:54 +0100367 obj = BT()
368 assert obj.say_something(3) == "BTBTBT"
Jason Rhinelander20978262016-08-29 18:16:46 -0400369 assert obj.unlucky_number() == -7
370 assert obj.lucky_number() == -1.375
Dean Moldovanbad17402016-11-20 21:21:54 +0100371 assert obj.say_everything() == "BT -7"