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

Daniel Wallin <[email protected]>
Newsgroups gmane.comp.lib.boost.langbinding
Message-ID <[email protected]>
At 05:33 2003-09-17, David Abrahams wrote:
>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.

Maybe, unless there's some object of complex type stored in the
policy which doesn't need to be copied. If a separate state type
is generated, we could have both shared storage in the policy and
a call-context storage. Of course, in those cases you could just
hold the object with a shared_ptr to avoid the copying..

I agree though that the implementation of b is more complicated and
perhaps unnecessary.

> > 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().

Right.

> > 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.

This cost is one char on the stack per policy, does that even matter?

This is also the cost associated with the policies that are kept in function
object. This isn't very much, since there are very few policies held in the
function object.

For this purpose I implemented a compressed_tuple<..>, it didn't turn
out well thought, since EBO breaks apart on some compilers when
MI is used. I ended up using some ugly hacks instead of EBO:
Use EBO to detect empty bases, and just cast the this pointer for
those types instead of inheriting.

> >>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.

Yes it does; the dynamically-registered converters are implemented as
a default policy which selects the appropriate converter type. There
is no reason the system cannot be aware of the state type:

struct default_state
{
    postcall_chain* chain;
};

template<class T>
struct rvalue_converter
{
    T& convert(default_state& state, ...)
};

Note that this is quite possible even if the policy object is copied and
used as state:

struct default_policy
{
    postcall_chain* chain;
    void postcall(...) { for (postcall_chain* c = chain; c != 0; c = c 
->next) (*c)(..);
};

> > 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.

Right.

> > 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.

Yep, lets keep discussing it.
---

Daniel Wallin



-------------------------------------------------------
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.