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:

> At 18:39 2003-09-17, David Abrahams wrote:
>
>>I think we need to stop using the term "policy" without qualification.
>>It gets too confusing.  Every time I do it, I mean CallPolicy (as in
>>Boost.Python).  I think when you do it you mean ConversionPolicy.  In
>>fact, why don't we say "[argument|result] [to|from]_xxx converter
>>generator" for what I called ConversionPolicy?
>
> Agreed. We still need to settle what a CallPolicy and converter generator
> actually is though. :)


I think a FromXXXConverterGenerator is a mixed compile-time/runtime
function object which produces a converter type.

>>Maybe that's the way you see it?  The default from_xxx converter
>>generator builds a static converter which uses dynamic lookup and uses
>>a default kind of state (say, a postcall chain) which is known to all
>>the dynamically-registered converters, and the others use a kind of
>>state which is determined at compile time by the static type of the
>>converter (say, nested in the converter).
>
> I think this is exactly what I mean.

OK.

>> >> > 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, ...)
>> >> > };
>> >>
>> >>If it's always a postcall chain, what's the point of nesting its type
>> >>in the CallPolicy?
>> >
>> > My point was that it isn't always the postcall chain, it is in the case of
>> > the default policy though. It isn't a postcall chain in the case of the
>> > PyUnicode you mentioned earlier.
>>
>>Actually it could be handled that way... but I suppose that would be
>>wasteful.
>
> Right.
>
>>As a matter of fact I think many static from_xxx converter types will
>>want to wrap the default converter with additional behavior, so their
>>state will need to include the postcall chain anyway.
>
> Yeah, I don't know how that would work. :|

Derivation or aggregation are obvious approaches.

>> > ConverterPolicy is also a model of CallPolicy
>>
>>Noooooo!  At least, not the way I understand it.
>>
>>CallPolicy is the Boost.Python concept that includes pre- and
>>post-processing of the native interpreter argument tuple.
>
> Ok, your view of CallPolicies is the same as mine. It's just that I think
> of the converter generators as a model of CallPolicy as well.
>
> I wanted the converter generators to have precall/postcall functions
> as well, since that would enable the "dynamic postcall action chain"
> thing in default_generator. 

Hmm.  Part of the point of using the "dynamic postcall action chain"
is that dynamic converters *don't* pay for a postcall action unless
they need one, which is rare.  Of course they could register a null
postcall function pointer, which would incur a test/call sequence for
each argument, as opposed to a single test/call for the whole chain.
Under that scheme, dynamic (default) converters don't need a postcall
action, because the CallPolicies' one postcall chain handles
everything.

> In that case the default generator would generate an appropriate
> converter and pass it's state to the convert() functions, which can
> add postcall actions to the chain. When we call postcall() on the
> generator, it could call it's chain..

Oh, that's scary.  You want to actually instantiate one of each kind
of generator object.  Well, I understand what you're up to, but I
don't think I agree with the implication that converters generated by
different generator types can't share any state.

> I guess default generator could model both a generator and CallPolicy
> instead though.

Instead of what?  How is that different from what you've been
proposing?

AFAICT, too many concepts are getting collapsed together here.  Can
we try to draw more distinctions?

> BTW, in luabind the converter object also has precall/postcall actions.
> This can be very useful. For example, they are used in out_value_policy,
> which can handle T* and T& which are meant to be out values.

The thing I dislike about that is that converters get used in non-call
contexts where "precall" and "postcall" are nonsense.

> void f(int& x); is treated like int f_wrap(int x) { f(x); return x; }
>
> Here the postcall function in the converter is used to convert
> the result back to lua.

Seems to me like the wrong way to do things.  IMO the result converter
should be responsible for that, but it should have access to the
argument converter in order to do its job.

>> > with the additional nested template type 'apply' which generates a
>> > converter type.
>>
>>In that case, what's the concept you have been calling "CallPolicy?"
>
> Same as you, something that has precall/postcall.

I still don't think that's quite the same as my concept.  My
CallPolicy also has a result (but not argument) converter generator.
I thought when that concept changed I would end up adding all the
argument converter generators.  In other words, it's the whole wad
required to actually build the wrapped function.

>> > with the signature void f(T0, T1) and Policies we would wind up with
>> > something like:
>> >
>> >    Make copy of policy objects
>> >
>> >    Call precall on each of the copied policy objects.
>> >
>> >    Fetch converter policy type for index 0.
>> >    Create converter for index 0.
>>
>>What's the point of copying the policies in this scheme?  Why not just
>>store the state in the converter itself?
>
> Because the converter is a unique object for each argument in the signature.
> This state needs to be shared by several converters.
>
>> >    Fetch converter policy type for index 1.
>> >    Create converter for index 1.
>> >
>> >    call ConverterN.match(...), if it fails overload
>> >    resolution fails.
>> >
>> >    f(Converter0.convert(CopiedPolicyObject0),
>> >    Converter1.convert(CopiedPolicyObject1));
>> >
>> >    Call postcall on each of the copied policy objects.
>> >
>> > ----- And sample C++ code..
>> >
>> >    // the copied policy object, same policy for T0 and T1
>> >    // so it's just one policy
>> >    default_policy policy0;
>>
>>Just one?!  Wow, I'm really confused about that.  What if they each
>>need their own state?
>
> I differentiate between "converter state" and "call (policy) state". The
> converters already have their own state.
>
> I think of this state as the glue between converters that are generated by
> the same generator type.
>
>> >    policy0.precall(...);
>>                      ^^^
>>What gets passed here?
>
> I really don't know. I guess something that can describe the argument
> tuple in python and the stack in lua.
>
>> >    // create converters
>> >    typename default_policy::apply<T0>::type c0;
>> >    typename default_policy::apply<T1>::type c1;
>> >
>> >    int match_value = 0;
>> >
>> >    match_value += c0.match(...);
>> >    match_value += c1.match(...);
>> >
>> >    // do some magic with the match value to choose "best" overload
>> >
>> >    f(c0.convert(policy0, ...), c1.convert(policy0, ...));
>> >
>> >    policy0.postcall(...);
>> >
>> > I'm having a really hard time getting my message across here. :/
>>
>>I think we're having a problem with terminology.
>
> Yeah, that and my ability to express myself. ;)

Well, I'm beginning to understand what you're driving at, but I'm
still not comfortable with it.  It seems very general and abstract
still.

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