[C++-sig] call_method

David Abrahams dave at boost-consulting.com
Tue Nov 12 18:16:34 CET 2002


"Brett Calcott" <brett.calcott at paradise.net.nz> writes:

> From: "David Abrahams" <dave at boost-consulting.com>
> Sent: Monday, November 11, 2002 1:43 AM
>
>
>>
>> > Ok, I thought this looked weird too. But given I want:
>> >
>> > 1) An agent which I can instantiate from python but the execution of
>> > do_something happens in c++.
>> > 2) An agent which inherits all of the c++ agents properties, but can
>> > do_something in python.
>> >
>> > ...then the above code seemed the way to do it. You can stick some
>> > intermediary class in, but it is effectively empty. Hmm. I guess I
>> > am blindly following the examples.
>>
>> No, you are not. You should never expose py_agent explicitly.

Let me rephrase that, since I don't really know what you intend
py_agent to be. You should never explicitly expose a class that's
fulfilling the same role as BaseWrap does in
http://www.boost.org/libs/python/doc/tutorial/doc/class_virtual_functions.html.

> The problem is I wanted to expose agent and py_agent,

Why?

> both of which require using agent as the first template
> parameter. So this means I must use an intermediary class between
> the two - Right?

I don't have any idea what you're trying to accomplish, but no
addition of intermediary classes makes it OK to directly expose a
class fulfilling the same role as BaseWrap does in
http://www.boost.org/libs/python/doc/tutorial/doc/class_virtual_functions.html.

>> No, you should never expose a virtual funtion you intend to override
>> in Python that way. Take a look at how "default_f" is used in
>>
> http://www.boost.org/libs/python/doc/tutorial/doc/class_virtual_functions.ht
> ml.
>> If you made agent::do_something pure virtual, you wouldn't need to
>> expose it at all.
>
> Ok, I need to RTFM.

Always a good plan.

> <ponders>Hmmm. Why can't you do that? Mem fun pointers to virtual functions
> are ok. Aren't you storing them somewhere...</ponders>

Because when they're called from Python, they'll just end up calling
the override which tries to call them from Python again, and you'll
get an infinite recursion.

> I think I would blunder less if I actually knew how this stuff
> worked. I believe you are writing something about the internals - I
> look forward to reading it.

Don't hold your breath; that's not on the front burner at the moment.

> Ok, I have something that works. I don't think it resembles what you had in
> mind as I don't use extract<> or python::object. This is just a 'proof of
> concept' - I would like your feedback on what might be bad/wrong/silly about
> this.
>
> The outline is this:
> 1. I use the intrusive_ptr template in boost. This is currently
> undocumented, and I also needed to add a 'typedef T element_type' to the
> class to make it work with Boost.Python. Note that the pointee<> approach
> documented for the smart_ptr example doesn't work with msvc.

Of course not ;-)

> 2. I use a py_counted_base which is inherited by the class that is pointed
> too. I keep the PyObject *self in this class and Py_INCREF it if the use
> count of the intrusive_ptr goes above 1. (see the code)
>
> 3. I create a to_python converter that returns the PyObject *self out of the
> base class so we get object identity back in python.

Well, I think it's damned cool! I spent a bunch of time turning
approaches like this one over and over in my mind, but I couldn't see
how to make it work. At first I thought your design had to be broken,
but I failed to poke any holes in it. IIUC, the structure looks like
this:


      PyObject
    +-----------+
    | ob_refcnt |       py_agent
    +-----------+       +------------+
    | agent_ptr |------>| use_count_ |
    +-----------+       +------------+
        ^               |     pyobj_ |--+
        |               +------------+  |
        |               |     ...    |  |
        |               +------------+  |
        |                               |
        +-------------------------------+

The key is that as soon as the agent_ptr is copied, you bump
ob_refcnt. Now you know that the PyObject can't die, the only way
use_count_ can go to 1 is if all copies of the agent_ptr have been
destroyed. At that point you can decrement ob_refcnt and let Python's
regular reference management take over.

Your remarks here:

    // need to put them in the boost namespace so they get found
    // before the defaults
    namespace boost
    {
       inline void intrusive_ptr_add_ref(py_counted_base * p)
       {
          p->add_ref();
       }

       inline void intrusive_ptr_release(py_counted_base * p)
       {
          p->release();
       }
    } // end namespace boost

Are wrong, and putting these in namespace boost will not work on
conforming compilers. They need to go in the same namespace as
py_counted_base on most compilers. For compilers that don't do Koenig
lookup namespace boost will probably be OK.

I'd like to explore a few refinements with you. For example, it may be
possible to get real shared_ptr interoperability by deriving
py_counted_base from counted_base. Maybe we should ask Peter to weigh
in on this. Peter, Brett's code is attached to this page:
http://aspn.activestate.com/ASPN/Mail/Message/1430235

--
                       David Abrahams
   dave at boost-consulting.com * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution





More information about the Cplusplus-sig mailing list