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