Re: [C++-sig] [Implementation] Calling wrapped functions, converters, policies

David Abrahams <[email protected]>
Newsgroups gmane.comp.lib.boost.langbinding
Message-ID <[email protected]>
Daniel Wallin <[email protected]> writes:

> I'm responding to this in the context of boost.langbinding.
>
> At 21:15 2003-09-16, David Abrahams wrote:
>
>>1. Per-call state.
>>
>><snip>
>>    What's needed is a way to get the per-call state onto the program
>>    stack.  I can think of two main approaches:
>>
>>    a. A copy of the Policies object is made on the stack and used
>>       during the function call; the copy can maintain its own state.
>>       An advantage of this approach is simplicity.  A disadvantage is
>>       that you may for things you don't use: storage for per-call
>>       state in the wrapped function itself, and cycles for copying
>>       per-call state to the stack.
>>
>>    b. The policies object has a init_state() function which
>>       produces a new state object of a possibly-different type.  This
>>       doesn't have the disadvantages above, but costs complexity in
>>       several ways:
>>
>>       i. The requirements on the Policies class become more
>>          complicated.  It probably needs to have a nested ::state
>>          type.  It's possible to get around the need for this typedef
>>          by passing the results of init_state() directly to a function
>>          template parameter (e.g. into invoke(...)), but that may
>>          force us to have a function call boundary at an undesirable
>>          place.
>>
>>       ii. Policies composition may become much more complicated.  How
>>           do you come up with the state object corresponding to
>>           several composed policies?  You could use tuples... hmm,
>>           maybe this is a job for mpl::inherit_linearly.
>>
>>    I'm leaning towards a. but I'm really not sure which one is best
>>    and would appreciate comments.
>
> I think b seems far better. 

Why?  I actually doubt the costs of a would amount to much.

> Actually, we were going to implement something like this in luabind
> as well, but never got around to it. The idea there was that the
> policy would instantiate the converter object so that the converter
> could get data stored in the policy into it's state.

I think that's what I was suggesting with init_state().

> on (ii), one state per policy?

That could have undesired costs, too, since most policies don't need
any state.  You'd want to use something like inherit_linearly to take
advantage of the EBO where it's available. It isn't always available.

>>3. Dynamic converter per-call state and postcall actions.
>>
>><snip>
>>    I propose that whichever state model is chosen in item 1 above, the
>>    state contains a chain of dynamically-allocated polymorphic
>>    postcall objects, and that dynamic converter implementations are
>>    passed a reference to that chain so that they can register new
>>    postcall actions.  The postcall actions are invoked when the call
>>    completes successfully, and are unconditionally deleted at the end
>>    of the call.  One thing I'm still not clear about is whether a
>>    converter's convertible() function need access to that chain.
>
> I agree. If we see the policies just as "generators", which generates
> both converters and converter-context-states (or whatever they would
> be called) this fits in nicely without needing to make a special case
> of it:
>
> struct ContextConcept
> {
>     ...
>     something postcall(something_else);
> };
>
> struct ConverterConcept
> {
>     ...
>     T convert(PolicyConcept::context_type& context);
> };

This is passing the per-call state into the convert function?  The
problem I have with this is that the dynamically-registered converter
implementation doesn't have access to the policy's static type, nor
presumably to its nested context_type.

> struct PolicyConcept
> {
>     typedef /* ... */ context_type;
>
>     template<class T>
>     struct apply
>     {
>        typedef /* ... */ type;
>     };
>
>     template<class T>
>     typename apply<T>::type make_converter(...);
>
>     context_type make_context(...);
> };

That's a pretty complicated set of requirements for the policy; I was
trying to avoid that if possible.

> The default policy could just generate a state type which holds the
> post-call chain, and call that chain in it's own postcall()
> function.
>
> Is this making sense? 

Some, but I think some issues need to be worked out still.

> I haven't thought this through enough yet, but
> posting it anyway..
>
>>4. The ability to release the Python interpreter lock during the
>>    wrapped call based on the choice of Policies.  If releasing the
>>    lock becomes the default behavior, it's important that it be
>>    automatically disabled when the wrapped function is handling
>>    python::object or any of its derived classes.  The release must
>>    happen inside the invoke(...)  function, or at least, if the
>>    implementation changes, after all converters have completed their
>>    work, since the lock must be held while any reference counts are
>>    changed.
>>
>>    This suggests that there is an inner layer of action needed, and it
>>    should probably be generalized.  So the flow looks something like:
>>
>><snip>
>>     I'm not sure how this interacts with Daniel's recursive "best
>>     overload" resolution.
>
> Whatever the flow is, it's easy enough to adapt to fit into the
> recursive invoke
> code, I think.
>
>>Comments?
>
> I'll think more and comment more tomorrow. :)

Looking forward.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com



-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.