polymorphism.txt 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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)
  5. How Runtime Polymorphism is expressed in Boost.Python:
  6. -----------------------------------------------------
  7. struct A { virtual std::string f(); virtual ~A(); };
  8. std::string call_f(A& x) { return x.f(); }
  9. struct B { virtual std::string f() { return "B"; } };
  10. struct Bcb : B
  11. {
  12. Bcb(PyObject* self) : m_self(self) {}
  13. virtual std::string f() { return call_method<std::string>(m_sef, "f"); }
  14. static std::string f_default(B& b) { return b.B::f(); }
  15. PyObject* m_self;
  16. };
  17. struct C : B
  18. {
  19. virtual std::string f() { return "C"; }
  20. };
  21. >>> class D(B):
  22. ... def f():
  23. ... return 'D'
  24. ...
  25. >>> class E(B): pass
  26. ...
  27. When we write, "invokes B::f non-virtually", we mean:
  28. void g(B& x) { x.B::f(); }
  29. This will call B::f() regardless of the dynamic type of x. Any other
  30. way of invoking B::f, including through a function pointer, is a
  31. "virtual invocation", and will call the most-derived override of f().
  32. Case studies
  33. C++\Python class
  34. \___A_____B_____C_____D____E___
  35. |
  36. A | 1
  37. |
  38. B | 2 3
  39. |
  40. Bcb | 4 5 6
  41. |
  42. C | 7 8
  43. |
  44. 1. Simple case
  45. 2. Python A holds a B*. Probably won't happen once we have forced
  46. downcasting.
  47. Requires:
  48. x.f() -> 'B'
  49. call_f(x) -> 'B'
  50. Implies: A.f invokes A::f() (virtually or otherwise)
  51. 3. Python B holds a B*.
  52. Requires:
  53. x.f() -> 'B'
  54. call_f(x) -> 'B'
  55. Implies: B.f invokes B::f (virtually or otherwise)
  56. 4. B constructed from Python
  57. Requires:
  58. x.f() -> 'B'
  59. call_f(x) -> 'B'
  60. Implies: B.f invokes B::f non-virtually. Bcb::f invokes B::f
  61. non-virtually.
  62. Question: Does it help if we arrange for Python B construction to
  63. build a true B object? Then this case doesn't arise.
  64. 5. D is a Python class derived from B
  65. Requires:
  66. x.f() -> 'D'
  67. call_f(x) -> 'D'
  68. Implies: Bcb::f must invoke call_method to look up the Python
  69. method override, otherwise call_f wouldn't work.
  70. 6. E is like D, but doesn't override f
  71. Requires:
  72. x.f() -> 'B'
  73. call_f(x) -> 'B'
  74. Implies: B.f invokes B::f non-virtually. If it were virtual, x.f()
  75. would cause infinite recursion, because we've already
  76. determined that Bcb::f must invoke call_method to look up
  77. the Python method override.
  78. 7. Python B object holds a C*
  79. Requires:
  80. x.f() -> 'C'
  81. call_f(x) -> 'C'
  82. Implies: B.f invokes B::f virtually.
  83. 8. C object constructed from Python
  84. Requires:
  85. x.f() -> 'C'
  86. call_f(x) -> 'C'
  87. Implies: nothing new.
  88. ------
  89. Total implications:
  90. 2: A.f invokes A::f() (virtually or otherwise)
  91. 3: B.f invokes B::f (virtually or otherwise)
  92. 4: B.f invokes B::f non-virtually. Bcb::f invokes B::f non-virtually
  93. 6: B.f invokes B::f non-virtually.
  94. 7: B.f invokes B::f virtually.
  95. 5: Bcb::f invokes call_method to look up the Python method
  96. Though (4) is avoidable, clearly 6 and 7 are not, and they
  97. conflict. The implication is that B.f must choose its behavior
  98. according to the type of the contained C++ object. If it is Bcb, a
  99. non-virtual call to B::f must occur. Otherwise, a virtual call to B::f
  100. must occur. This is essentially the same scheme we had with
  101. Boost.Python v1.
  102. Note: in early versions of Boost.Python v1, we solved this problem by
  103. introducing a new Python class in the hierarchy, so that D and E
  104. actually derive from a B', and B'.f invokes B::f non-virtually, while
  105. B.f invokes B::f virtually. However, people complained about the
  106. artificial class in the hierarchy, which was revealed when they tried
  107. to do normal kinds of Python introspection.
  108. -------
  109. Assumption: we will have a function which builds a virtual function
  110. dispatch callable Python object.
  111. make_virtual_function(pvmf, default_impl, call_policies, dispatch_type)
  112. Pseudocode:
  113. Get first argument from Python arg tuple
  114. if it contains dispatch_type
  115. call default_impl
  116. else
  117. call through pvmf
  118. Open questions:
  119. 1. What about Python multiple inheritance? Do we have the right
  120. check in the if clause above?
  121. A: Not quite. The correct test looks like:
  122. Deduce target type of pvmf, i.e. T in R(T::*)(A1...AN).
  123. Find holder in first argument which holds T
  124. if it holds dispatch_type...
  125. 2. Can we make this more efficient?
  126. The current "returning" mechanism will look up a holder for T
  127. again. I don't know if we know how to avoid that.
  128. OK, the solution involves reworking the call mechanism. This is
  129. neccesary anyway in order to enable wrapping of function objects.
  130. It can result in a reduction in the overall amount of source code,
  131. because returning<> won't need to be specialized for every
  132. combination of function and member function... though it will still
  133. need a void specialization. We will still need a way to dispatch to
  134. member functions through a regular function interface. mem_fn is
  135. almost the right tool, but it only goes up to 8
  136. arguments. Forwarding is tricky if you don't want to incur copies.
  137. I think the trick is to use arg_from_python<T>::result_type for each
  138. argument to the forwarder.
  139. Another option would be to use separate function, function object,
  140. and member function dispatchers. Once you know you have a member
  141. function, you don't need cv-qualified overloads to call it.
  142. Hmm, while we're at this, maybe we should solve the write-back
  143. converter problem. Can we do it? Maybe not. Ralf doesn't want to
  144. write special write-back functions here, does he? He wants the
  145. converter to do the work automatically. We could add
  146. cleanup/destructor registration. That would relieve the client from
  147. having accessible destructors for types which are being converted by
  148. rvalue. I'm not sure that this will really save any code,
  149. however. It rather depends on the linker, doesn't it? I wonder if
  150. this can be done in a backwards-compatible fashion by generating the
  151. delete function when it's not supplied?