Boost.Python Internals Boost

Boost.PythonĚĻ

A conversation between Brett Calcott and David Abrahams

Brett Calcottꍄavid Abrahams֮줵ĶԻ༯a>

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ἤɏ㨟#쎒儒⋼ʇʂǩ碉굄˳㩡㍊

  1. 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彃儗껻㬊ǔڲ鋤ע⡱햮ǰ㬰䌘ʢǩﶴ怭廣썊

  1. 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˵㬒