1 .. Copyright David Abrahams 2006. Distributed under the Boost
2 .. Software License, Version 1.0. (See accompanying
3 .. file LICENSE_1_0.txt or copy at
4 .. http://www.boost.org/LICENSE_1_0.txt)
6 How Runtime Polymorphism is expressed in Boost.Python:
7 -----------------------------------------------------
9 struct A { virtual std::string f(); virtual ~A(); };
11 std::string call_f(A& x) { return x.f(); }
13 struct B { virtual std::string f() { return "B"; } };
17 Bcb(PyObject* self) : m_self(self) {}
19 virtual std::string f() { return call_method<std::string>(m_sef, "f"); }
20 static std::string f_default(B& b) { return b.B::f(); }
27 virtual std::string f() { return "C"; }
38 When we write, "invokes B::f non-virtually", we mean:
40 void g(B& x) { x.B::f(); }
42 This will call B::f() regardless of the dynamic type of x. Any other
43 way of invoking B::f, including through a function pointer, is a
44 "virtual invocation", and will call the most-derived override of f().
49 \___A_____B_____C_____D____E___
63 2. Python A holds a B*. Probably won't happen once we have forced
70 Implies: A.f invokes A::f() (virtually or otherwise)
72 3. Python B holds a B*.
78 Implies: B.f invokes B::f (virtually or otherwise)
81 4. B constructed from Python
88 Implies: B.f invokes B::f non-virtually. Bcb::f invokes B::f
91 Question: Does it help if we arrange for Python B construction to
92 build a true B object? Then this case doesn't arise.
95 5. D is a Python class derived from B
102 Implies: Bcb::f must invoke call_method to look up the Python
103 method override, otherwise call_f wouldn't work.
105 6. E is like D, but doesn't override f
112 Implies: B.f invokes B::f non-virtually. If it were virtual, x.f()
113 would cause infinite recursion, because we've already
114 determined that Bcb::f must invoke call_method to look up
115 the Python method override.
117 7. Python B object holds a C*
124 Implies: B.f invokes B::f virtually.
126 8. C object constructed from Python
133 Implies: nothing new.
139 2: A.f invokes A::f() (virtually or otherwise)
140 3: B.f invokes B::f (virtually or otherwise)
141 4: B.f invokes B::f non-virtually. Bcb::f invokes B::f non-virtually
142 6: B.f invokes B::f non-virtually.
143 7: B.f invokes B::f virtually.
145 5: Bcb::f invokes call_method to look up the Python method
147 Though (4) is avoidable, clearly 6 and 7 are not, and they
148 conflict. The implication is that B.f must choose its behavior
149 according to the type of the contained C++ object. If it is Bcb, a
150 non-virtual call to B::f must occur. Otherwise, a virtual call to B::f
151 must occur. This is essentially the same scheme we had with
154 Note: in early versions of Boost.Python v1, we solved this problem by
155 introducing a new Python class in the hierarchy, so that D and E
156 actually derive from a B', and B'.f invokes B::f non-virtually, while
157 B.f invokes B::f virtually. However, people complained about the
158 artificial class in the hierarchy, which was revealed when they tried
159 to do normal kinds of Python introspection.
163 Assumption: we will have a function which builds a virtual function
164 dispatch callable Python object.
166 make_virtual_function(pvmf, default_impl, call_policies, dispatch_type)
170 Get first argument from Python arg tuple
171 if it contains dispatch_type
179 1. What about Python multiple inheritance? Do we have the right
180 check in the if clause above?
182 A: Not quite. The correct test looks like:
184 Deduce target type of pvmf, i.e. T in R(T::*)(A1...AN).
185 Find holder in first argument which holds T
186 if it holds dispatch_type...
188 2. Can we make this more efficient?
190 The current "returning" mechanism will look up a holder for T
191 again. I don't know if we know how to avoid that.
194 OK, the solution involves reworking the call mechanism. This is
195 neccesary anyway in order to enable wrapping of function objects.
197 It can result in a reduction in the overall amount of source code,
198 because returning<> won't need to be specialized for every
199 combination of function and member function... though it will still
200 need a void specialization. We will still need a way to dispatch to
201 member functions through a regular function interface. mem_fn is
202 almost the right tool, but it only goes up to 8
203 arguments. Forwarding is tricky if you don't want to incur copies.
204 I think the trick is to use arg_from_python<T>::result_type for each
205 argument to the forwarder.
207 Another option would be to use separate function, function object,
208 and member function dispatchers. Once you know you have a member
209 function, you don't need cv-qualified overloads to call it.
211 Hmm, while we're at this, maybe we should solve the write-back
212 converter problem. Can we do it? Maybe not. Ralf doesn't want to
213 write special write-back functions here, does he? He wants the
214 converter to do the work automatically. We could add
215 cleanup/destructor registration. That would relieve the client from
216 having accessible destructors for types which are being converted by
217 rvalue. I'm not sure that this will really save any code,
218 however. It rather depends on the linker, doesn't it? I wonder if
219 this can be done in a backwards-compatible fashion by generating the
220 delete function when it's not supplied?