
| copyright: | Copyright David Abrahams and Brett Calcott 2003. See accompanying license for terms of use. |
|---|---|
| ঈ裺 | ঈ蘋Ӑ David Abrahamsꍂrett Calcott 2003ᣍ 컋鴿儊鈴/a>̵ﮡ㍊ |
| 筒룺 | 켯a> |
| ҫ΄輐o | 2008.5.23 |
In both of these cases, I'm quite capable of reading code - but the thing I don't get from scanning the source is a sense of the architecture, both structurally, and temporally (er, I mean in what order things go on).
攓ڒԏΊ̢㬎ҿɒԔĶtꂫѰՒ䰰衪᪵늇ΒΞ槏ӤԴ亂뵃迤ĊǕ댥萣쎞ʇ鉏빊NJἤɏ㨟#쎒儒⋼ʇʂǩ碉굄˳㩡㍊
What happens when you do the following:
ςaբoʱ롷≺ʲo㺼/p>
struct boring {};
...etc...
class_<boring>("boring")
;
There seems to be a fair bit going on.
ˆ굓Џ൱櫳顣
- Python needs a new ClassType to be registered.
- We need to construct a new type that can hold our boring struct.
- Inward and outward converters need to be registered for the type.
- PythonҪע⡐μălassType/li>
- ΒCҪ鷫쐂儀Ѝ4ᣴ梯ring顣
- ҪΪ脹Ѝע⡏ℚꍏ⍢儗껻Ʒ/li>
Can you gesture in the general direction where these things are done?
ģĜԚ䳷폲ɏָ㶕ʂǩʇԚĄ/儂ࣿ
I only have time for a "off-the-top-of-my-head" answer at the moment; I suggest you step through the code with a debugger after reading this to see how it works, fill in details, and make sure I didn't forget anything.
䋿̎Җ듐ʱ줗縶᰼䐋ᱵĻشࣻ Β鄣ﴁ˕Ҕ곣쓃嬰Ԇ紎⽔ːꂫ㬲鿴˼ȧꎹ䗷㬍 ˲鳤ϸ횣첢֤ʵΒuӐͼ쇊ᣍ
A new (Python) subclass of Boost.Python.Instance (see libs/python/src/object/class.cpp) is created by invoking Boost.Python.class, the metatype:
巔Aoost.Python.classʱ㬴包KBoost.Python.Instance㨼덊libs/python/src/object/class.cpp㩵Ē븶Ђ廣萹thon㩗Ӏ࣬ ͣ譥tatype㩎꣺
>>> boring = Boost.Python.class( ... 'boring' ... , bases_tuple # in this case, just () ... , { ... '__module__' : module_name ... , '__doc__' : doc_string # optional ... } ... )A handle to this object is stuck in the m_class_object field of the registration associated with typeid(boring). The registry will keep that object alive forever, even if you wipe out the 'boring' attribute of the extension module (probably not a good thing).
բ趶ԏ㵄걣䦓ڍ typeid(boring) ע⡏儭_class_objectז採㍊ע⡱ȃբһ攏㓀Զ뮗ţ썊촊鄣㹵)չģ冀ħboring'ʴ㨿Ʉܲ늇һ쾺E£顣
Because you didn't specify class<boring, non_copyable, ...>, a to-python converter for boring is registered which copies its argument into a value_holder held by the the Python boring object.
ҲΪģuӐָ樍 class<boring, non_copyable, ...>㬍 ᗢ⡢oring䓃ython儗껻Ʒ㬍 胗껻Ʒ贖Ɔ䲎ʽython boring攏㋹㖓Ķalue_holderᣍ
Because you didn't specify class<boring ...>(no_init), an __init__ function object is added to the class dictionary which default-constructs a boring in a value_holder (because you didn't specify some smart pointer or derived wrapper class as a holder) held by the Python boring object.
ҲΪģuӐָ樍 class<boring ...>(no_init)㬍 __init__ꯊϳጭ쓵퀠ז夢썊˼롔ڶalue_holderĬȏ鷫좯ring 㨒⎪ģuӐָ樖DŽܖ蕫벅ɉ굄༗ Ϊ㖓ߣ養 膠alue_holderӉPython boring攏㋹㖓㍊
register_class_from_python is used to register a from-python converter for shared_ptr<boring>. boost::shared_ptrs are special among smart pointers because their Deleter argument can be made to manage the whole Python object, not just the C++ object it contains, no matter how the C++ object is held.
register_class_from_python Ϊshared_ptr<boring> ע⡴Ӑython彃儗껻Ʒᣍ boost::shared_ptr ԚևĜָիʇ̘ʢ廣썊ҲΪ˼C億eleter⎊ҔӃӚ需핻趐ython攏㣬 渲떻ʇ˼˹༺쵄C++攏㣬 Ҳ܃++攏八ȧꎱ㴦儡㍊
If there were any bases<>, we'd also be registering the relationship between these base classes and boring in the up/down cast graph (inheritance.[hpp/cpp]).
ȧ黓Јκ bases<>㬍 Ԛϲɏ/ϲςcast_graph㨼tt class="literal">inheritance.[hpp/cpp]㩣썊ΒCҲ롗ⲡբ빀ຍboring儹؏塣
In earlier versions of the code, we'd be registering lvalue from-python converters for the class here, but now from-python conversion for wrapped classes is handled as a special case, before consulting the registry, if the source Python object's metaclass is the Boost.Python metaclass.
Hmm, that from-python converter probably ought to be handled the way class converters are, with no explicit conversions registered.
Ԛԧƚ亂떐㬍 ΒCԚ䋗ⲡ 䓐ython彃儗㖵ת뻆磬 瑯֔ڣ숧黔䐹thon攏㵄Ԫ 㨭etaclass㩊ǂoost.PythonԪ 㬍 碗 䓐ython彃儗껻㬊ǔڲ鋤ע⡱햮ǰ㬰䌘ʢǩﶴ怭廣썊
Can you give a brief overview of the data structures that are present in the registry
ģĜ첵帥ʶһςע⡱햐儊邰
The registry is simple: it's just a map from typeid -> registration (see boost/python/converter/registrations.hpp). lvalue_chain and rvalue_chain are simple endogenous linked lists.
ע⡱첵壺ֻʇtypeid -> ע⡏ӳɤ㨼뢯ost/python/converter/registrations.hpp㩡㍊lvalue_chain ꍼtt class="literal">rvalue_chain ʇ첵嵄Ět᭡㍊
If you want to know more, just ask.
ȧ黃㏫֪睬춠㬾͎ʎҡ㼯p>
If you want to know about the cast graph, ask me something specific in a separate message.
ȧ黃㏫Kꐍͼ㨣ast graph㩣쇫Ԛ奶5ď돢䰖ЎʎҒ됩嵄Ί̢/p>
and an overview of the process that happens as a type makes its way from c++ to python and back again.
빓ဠЍ䓣++彰ython4똗껻儴㸅齳̡㍊
Big subject. I suggest some background reading: look for relevant info in the LLNL progress reports and the messages they link to. Also,
բʇ趴㎊̢ᣎҽ蒩Ԅ恣ꍊԚLLNL鱨視ՒϠ陣Đŏ⣬ꍋ샇˹t퓵Đŏ⡣䋍⣬
http://mail.python.org/pipermail/c++-sig/2002-May/001023.html
http://mail.python.org/pipermail/c++-sig/2002-December/003115.html
http://aspn.activestate.com/ASPN/Mail/Message/1280898
http://mail.python.org/pipermail/c++-sig/2002-July/001755.html
from c++ to python:
䓣++彰ython㺼/p>
It depends on the type and the call policies in use or, for call<>(...), call_method<>(...), or object(...), if ref or ptr is used. There are also two basic categories to to-python conversion, "return value" conversion (for Python->C++ calls) and "argument" conversion (for C++->Python calls and explicit object() conversions). The behavior of these two categories differs subtly in various ways whose details I forget at the moment. You can probably find the answers in the above references, and certainly in the code.
˼Ҁ5Ӛ Ѝꍊ鈴儵瓃⟂ԣ컲՟㬍 捍tt class="literal">call<>(...)ᢍ call_method<>(...)㬍 벼tt class="literal">object(...) 4˵㬒