blob: 138be631341b9085fb381d9ad93fc01e908f3167 [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()
131 assert capture == """
132 PyA2.PyA2()
133 PyA2.f()
134 A2.f()
135 PyA2.~PyA2()
136 """
137
138 # Python subclass version
139 with capture:
140 b2 = B2()
141 m.call_f(b2)
142 del b2
143 pytest.gc_collect()
144 assert capture == """
145 PyA2.PyA2()
146 PyA2.f()
147 In python B2.f()
148 PyA2.~PyA2()
149 """
150
151
Jason Rhinelander391c7542017-07-25 16:47:36 -0400152# PyPy: Reference count > 1 causes call with noncopyable instance
153# to fail in ncv1.print_nc()
154@pytest.unsupported_on_pypy
155@pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC")
156def test_move_support():
157 class NCVirtExt(m.NCVirt):
158 def get_noncopyable(self, a, b):
159 # Constructs and returns a new instance:
160 nc = m.NonCopyable(a * a, b * b)
161 return nc
162
163 def get_movable(self, a, b):
164 # Return a referenced copy
165 self.movable = m.Movable(a, b)
166 return self.movable
167
168 class NCVirtExt2(m.NCVirt):
169 def get_noncopyable(self, a, b):
170 # Keep a reference: this is going to throw an exception
171 self.nc = m.NonCopyable(a, b)
172 return self.nc
173
174 def get_movable(self, a, b):
175 # Return a new instance without storing it
176 return m.Movable(a, b)
177
178 ncv1 = NCVirtExt()
179 assert ncv1.print_nc(2, 3) == "36"
180 assert ncv1.print_movable(4, 5) == "9"
181 ncv2 = NCVirtExt2()
182 assert ncv2.print_movable(7, 7) == "14"
183 # Don't check the exception message here because it differs under debug/non-debug mode
184 with pytest.raises(RuntimeError):
185 ncv2.print_nc(9, 9)
186
187 nc_stats = ConstructorStats.get(m.NonCopyable)
188 mv_stats = ConstructorStats.get(m.Movable)
189 assert nc_stats.alive() == 1
190 assert mv_stats.alive() == 1
191 del ncv1, ncv2
192 assert nc_stats.alive() == 0
193 assert mv_stats.alive() == 0
194 assert nc_stats.values() == ['4', '9', '9', '9']
195 assert mv_stats.values() == ['4', '5', '7', '7']
196 assert nc_stats.copy_constructions == 0
197 assert mv_stats.copy_constructions == 1
198 assert nc_stats.move_constructions >= 0
199 assert mv_stats.move_constructions >= 0
200
201
202def test_dispatch_issue(msg):
203 """#159: virtual function dispatch has problems with similar-named functions"""
204 class PyClass1(m.DispatchIssue):
205 def dispatch(self):
206 return "Yay.."
207
208 class PyClass2(m.DispatchIssue):
209 def dispatch(self):
210 with pytest.raises(RuntimeError) as excinfo:
211 super(PyClass2, self).dispatch()
212 assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
213
214 p = PyClass1()
215 return m.dispatch_issue_go(p)
216
217 b = PyClass2()
218 assert m.dispatch_issue_go(b) == "Yay.."
219
220
221def test_override_ref():
222 """#392/397: overridding reference-returning functions"""
223 o = m.OverrideTest("asdf")
224
225 # Not allowed (see associated .cpp comment)
226 # i = o.str_ref()
227 # assert o.str_ref() == "asdf"
228 assert o.str_value() == "asdf"
229
230 assert o.A_value().value == "hi"
231 a = o.A_ref()
232 assert a.value == "hi"
233 a.value = "bye"
234 assert a.value == "bye"
235
236
237def test_inherited_virtuals():
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200238 class AR(m.A_Repeat):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200239 def unlucky_number(self):
240 return 99
241
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200242 class AT(m.A_Tpl):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200243 def unlucky_number(self):
244 return 999
245
Dean Moldovanbad17402016-11-20 21:21:54 +0100246 obj = AR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200247 assert obj.say_something(3) == "hihihi"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200248 assert obj.unlucky_number() == 99
Jason Rhinelander20978262016-08-29 18:16:46 -0400249 assert obj.say_everything() == "hi 99"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200250
Dean Moldovanbad17402016-11-20 21:21:54 +0100251 obj = AT()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200252 assert obj.say_something(3) == "hihihi"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200253 assert obj.unlucky_number() == 999
Jason Rhinelander20978262016-08-29 18:16:46 -0400254 assert obj.say_everything() == "hi 999"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200255
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200256 for obj in [m.B_Repeat(), m.B_Tpl()]:
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200257 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200258 assert obj.unlucky_number() == 13
259 assert obj.lucky_number() == 7.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400260 assert obj.say_everything() == "B says hi 1 times 13"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200261
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200262 for obj in [m.C_Repeat(), m.C_Tpl()]:
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200263 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200264 assert obj.unlucky_number() == 4444
265 assert obj.lucky_number() == 888.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400266 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200267
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200268 class CR(m.C_Repeat):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200269 def lucky_number(self):
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200270 return m.C_Repeat.lucky_number(self) + 1.25
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200271
Dean Moldovanbad17402016-11-20 21:21:54 +0100272 obj = CR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200273 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200274 assert obj.unlucky_number() == 4444
275 assert obj.lucky_number() == 889.25
Jason Rhinelander20978262016-08-29 18:16:46 -0400276 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200277
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200278 class CT(m.C_Tpl):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200279 pass
280
Dean Moldovanbad17402016-11-20 21:21:54 +0100281 obj = CT()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200282 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200283 assert obj.unlucky_number() == 4444
284 assert obj.lucky_number() == 888.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400285 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200286
Dean Moldovanbad17402016-11-20 21:21:54 +0100287 class CCR(CR):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200288 def lucky_number(self):
Dean Moldovanbad17402016-11-20 21:21:54 +0100289 return CR.lucky_number(self) * 10
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200290
Dean Moldovanbad17402016-11-20 21:21:54 +0100291 obj = CCR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200292 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200293 assert obj.unlucky_number() == 4444
294 assert obj.lucky_number() == 8892.5
Jason Rhinelander20978262016-08-29 18:16:46 -0400295 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200296
Dean Moldovanbad17402016-11-20 21:21:54 +0100297 class CCT(CT):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200298 def lucky_number(self):
Dean Moldovanbad17402016-11-20 21:21:54 +0100299 return CT.lucky_number(self) * 1000
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200300
Dean Moldovanbad17402016-11-20 21:21:54 +0100301 obj = CCT()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200302 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200303 assert obj.unlucky_number() == 4444
304 assert obj.lucky_number() == 888000.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400305 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200306
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200307 class DR(m.D_Repeat):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200308 def unlucky_number(self):
309 return 123
310
311 def lucky_number(self):
312 return 42.0
313
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200314 for obj in [m.D_Repeat(), m.D_Tpl()]:
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200315 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200316 assert obj.unlucky_number() == 4444
317 assert obj.lucky_number() == 888.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400318 assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200319
Dean Moldovanbad17402016-11-20 21:21:54 +0100320 obj = DR()
Dean Moldovan99dbdc12016-08-19 13:45:36 +0200321 assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200322 assert obj.unlucky_number() == 123
323 assert obj.lucky_number() == 42.0
Jason Rhinelander20978262016-08-29 18:16:46 -0400324 assert obj.say_everything() == "B says hi 1 times 123"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200325
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200326 class DT(m.D_Tpl):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200327 def say_something(self, times):
Dean Moldovanbad17402016-11-20 21:21:54 +0100328 return "DT says:" + (' quack' * times)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200329
330 def unlucky_number(self):
331 return 1234
332
333 def lucky_number(self):
334 return -4.25
335
Dean Moldovanbad17402016-11-20 21:21:54 +0100336 obj = DT()
337 assert obj.say_something(3) == "DT says: quack quack quack"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +0200338 assert obj.unlucky_number() == 1234
339 assert obj.lucky_number() == -4.25
Dean Moldovanbad17402016-11-20 21:21:54 +0100340 assert obj.say_everything() == "DT says: quack 1234"
Jason Rhinelander20978262016-08-29 18:16:46 -0400341
Dean Moldovanbad17402016-11-20 21:21:54 +0100342 class DT2(DT):
Jason Rhinelander20978262016-08-29 18:16:46 -0400343 def say_something(self, times):
Dean Moldovanbad17402016-11-20 21:21:54 +0100344 return "DT2: " + ('QUACK' * times)
Jason Rhinelander20978262016-08-29 18:16:46 -0400345
346 def unlucky_number(self):
347 return -3
348
Dean Moldovan0bc272b2017-06-22 23:42:11 +0200349 class BT(m.B_Tpl):
Jason Rhinelander20978262016-08-29 18:16:46 -0400350 def say_something(self, times):
Dean Moldovanbad17402016-11-20 21:21:54 +0100351 return "BT" * times
352
Jason Rhinelander20978262016-08-29 18:16:46 -0400353 def unlucky_number(self):
354 return -7
Dean Moldovanbad17402016-11-20 21:21:54 +0100355
Jason Rhinelander20978262016-08-29 18:16:46 -0400356 def lucky_number(self):
357 return -1.375
358
Dean Moldovanbad17402016-11-20 21:21:54 +0100359 obj = BT()
360 assert obj.say_something(3) == "BTBTBT"
Jason Rhinelander20978262016-08-29 18:16:46 -0400361 assert obj.unlucky_number() == -7
362 assert obj.lucky_number() == -1.375
Dean Moldovanbad17402016-11-20 21:21:54 +0100363 assert obj.say_everything() == "BT -7"