Re: Re: [Implementation] Calling wrapped functions, converters, policies

Daniel Wallin <[email protected]>
Newsgroups gmane.comp.lib.boost.langbinding
Message-ID <[email protected]>
David Abrahams wrote:
> Daniel Wallin <[email protected]> writes:
> 
> 
> 
>>>>IIRC this is close to what you have in boost python today.
>>
>>>Yep, close.  It's missing the "hidden this downcasting" feature,
>>>which we'll need.
>>
>>Right,
>>
>>   template<class R, class T, ..., class WrappedClass>
>>   signature<
>>       mpl::vector3<WrappedClass&, R, ...>
> 
> 
> What does the first element of the vector mean?  Oh, looking back at
> your previous message that's a *very* strange ordering!  Why would you
> put the return type *second*?!

The ordering was arbitrary. Well, sort of at least. The reason was to
keep the indexing consistent. A member function signature is the same as
a function signature, except it has the self-type at index 0. So the
index to some type in the signature is just offset one step. I don't
know if it matters though. :)

>>       ...
>>   >
>>   signature_of(R(T::*)(...), type<WrappedClass>)
>>
>>?
> 
> 
> I'm not sure whether what you're suggesting above actually does the
> right thing.  In fact, I'm almost certain it doesn't.  You need the
> most_derived hook used in signature.hpp.  The idea is, "downcast if
> you can, otherwise leave it alone".

I had a look in signature.hpp. I'm not sure I understand this though;
Target is not more derived than T => Target == T, no? Or is Target
something else than the current WrappedClass type?

> BTW, I like using a WrappedClass* instead of type<WrappedClass> because I
> think it costs fewer compilation resources.

Ok good point.

>>>>  - A CallPolicies concept which is a binary metafunction class that can
>>>>    generate a converter for a type T and argument index N.
>>>
>>>That sounds like a from-XXX converter generator, but not the full
>>>concept that is CallPolicies in my mind, which includes converting
>>>results to-XXX.
>>
>>Right.. I don't remember exactly what we decided on this, if we ever did
>>decide anything. When I wrote this I was thinking that
>>CallPolicies::apply generates a converter that can convert the type in
>>both directions, and the direction is decided later by calling the right
>>member function:
>>
>>    cv.to(...); // converts to XXX
>>    cv.from(...); // converts from XXX
>>
>>Then we'd actually only have the ConverterGenerator concept, which
>>CallPolicies models.
> 
> 
> I have no problem in principle (I think) with bidirectional
> converter *generators*, but bidirectional converters won't work.  They
> need completely different data members, and from-XXX converters need
> to be 2-phase for overloading, while to-XXX converters are one
> phase.  In other words, I have no problem with:
> 
> [snip]

OK agreed. So, should the nested member be a metafunction class?

   some_generator
   {
       typedef /* to converter */ to;
       typedef /* from converter */ from;
   };

And how does this fit in with how the converter_tuple is generated?
Shouldn't all converters be in the tuple, regardless of in what
direction they should convert? How could the converter_tuple_generator
know which types should be "to" and which should be "from"? My version
just iterates over all types and generates a converter for every one,
not knowing anything about directions.

>>To clarify, this models what we have in luabind.
>>
>>   policy1(_2) + policy2(_1)
>>
>>Would create something like:
>>
>>   composite_call_policies<
>>       mpl::vector<
>>           node<mpl::int_<2>, policy1_type>
>>         , node<mpl::int_<1>, policy1_type>
> 
> --------------------------------------^
> 
>>       >
>>    >
> 
> 
> Really??  

No, of course I mistyped. Should have choose better names. :)

> Just so as not to confuse things, I suggest policyA and
> policyB.  So:
> 
>      policyA(_2) + policyB(_1)
> 
>   Would create something like:
> 
>      composite_call_policies<
>          mpl::vector<
>              node<mpl::int_<2>, policyA_type>
>            , node<mpl::int_<1>, policyB_type>
>          >
>       >
> 
> ??

Exactly.

>>And it's nested apply<> would do something like:
>>
>>   apply(Type, Index)
>>      if we have a child, C,  that matches node<Index, _>
>>          C::apply(Type, Index)
>>       else
>>          default_call_policies::apply(Type, Index)
> 
> 
> It looks to me like the Children model a different concept from the
> composite, in that they're index-agnostic (or should be).  The
> children are converter generators, aren't they?  

No I think the children are CallPolicies as well - they should have
precall/postcall actions.

I'm not sure what you mean by index-agnostic.

> Didn't we make a bunch of these decisions about naming already?

Yeah we did. I think we said that there are basically two concepts,
ConverterGenerator and CallPolicies. CallPolicies is a refined
ConverterGenerator which has precall/postcall actions. I'm going to
browse through the archives after I post this, if I was wrong I'll
repost. :)

>>The indices here are generally non-contiguous, since the user rarely
>>would specify an explicit policy for each argument index.
> 
> Why not use an mpl::map or mpl::multimap?

That's fine by me.

> Yes, you or Aleksey will have to implement it.  But I'm sure he'd
> be happy to oblige.   ;->

Didn't you already implement an associative mpl container? IIRC I have
seen you talk about it in some posts.

I did it once for fun, except it wasn't really a map but sort of a
container adaptor.

    // BaseContainer := mpl sequence of mpl::pair<Key, Value>
    template<class BaseContainer>
    struct map_adaptor;

That was pretty far from mpl standard though, so lets wait for Aleksey. :)

>>>>    It also combines it's childrens argument_package types and
>>>>    state types by inheritance.
>>>
>>>Not sure what you mean by "by inheritance".
>>
>>It means it's nested state_type inherits from all it's "childrens"
>>state_types.
> 
> 
> I guess states have to be classes then?  I think there's a way to lift
> this restriction using an explicit get function and inheritance from
> wrappers:
> 
>      template <class Head, class Tail>
>      struct cons : Tail
>      {
>          Head h;
>      };
> 
>      template <class Head, class Tail>
>      Head& get(cons<Head,Tail>& x) { return x.h; }
> 
> Now each state type (the "head" of a cons sub-list) can be whatever
> you like.

And extract the state type by explicitly supplying Head?

   get<my_state_type>(state);

It isn't as nice as implicit conversion, but I guess it'll work. I don't
know though; do states really need to be non-class types? Sure, I can
see why someone could find the need for it, but if you don't wrap your
type in a class, there's always a risk for collisions with other
policies in a composite. Two policies can use "int" for completely
different things, and we can't discriminate them.

>>Again,
>>
>>   composite_call_policies<
>>       mpl::vector<
>>           node<mpl::int_<2>, policy1_type>
>>         , node<mpl::int_<1>, policy1_type>
>>       >
>>    >
>>
>>Would make something like:
>>
>>    template<class Base, class CallPolicies>
>>    struct inherit_state : Base, CallPolicies
>>    {
>>        template<class U>
>>        inherit_state(const U& init)
>>            : Base(init), CallPolicies(init)
>>        {}
>>    };
>>
>>    typedef typename inherit_linearly<
>>        PoliciesSequence
>>      , empty
>>      , inherit_state<_1, _2>
>>    >::type
>>
>>Except that it's a bit more complicated; every state type is only
>>inherited once.
> 
> mpl::set, anyone?

Sure. Now I'm doing something like:

   template<bool B, class Base, class CallPolicies>
   struct inherit_state_aux : Base, CallPolicies::state_type
   {};

   template<class Base, class CallPolicies>
   struct inherit_state_aux<true, Base, CallPolicies>
     : Base
   {};

   template<class Base, class CallPolicies>
   struct inherit_state
      : inherit_state_aux<
            is_convertible<
                Base*, typename CallPolicies::state_type>::value
            >::value
          , Base
          , CallPolicies
        >
   {
      ...
   };

   typedef typename inherit_linearly<
         PoliciesSequence
       , empty
       , inherit_state<_1, _2>
   >::type

Except I'm not using partial specialization.. Whether or not we are
using mpl::set or this doesn't matter to me. My guess is that my version
would compile faster, but obviously is less clear. I could be wrong
though.

>>The same goes for argument_package. Here we also have get() functions:
>>
>>    template<class Base, int N>
>>    PyObject* get(PyObject* args, mpl::int_<N>)
>>    { return PySomethingSomething(.., N); }
>>
>>    template<int N>
>>    struct offset_arg // IIRC you have something like this in BPL
>>    {
>>        offset_arg(PyObject*) {}
>>    };
> 
> 
> Yep, in boost/python/make_constructor.hpp
> 
> 
>>With a special overload that handles the composite case where every
>>argument_package has a base type which must be queried:
>>
>>    template<class Base, int M, int N>
>>    PyObject* get(offset_arg<M>, const Base& base, mpl::int_<N>)
>>    { return get(base, mpl::int_<N + M>()); }
> 
> 
> Wha??
> 
> Why isn't the base object stored in the offset_arg<>, as in BPL?  Do
> you need to explain what Base is?

Because the different CallPolicies in the composite_call_policies type
have no knowledge of each other. Meaning we would need to make the
nested argument_package a metafunction:

   struct my_policy
   {
       template<class Base>
       struct argument_package
       {
           typedef offset_arg<1, Base> type;
       };
   };

I think it's nicer to create a composite hierarchy:

   composite_argument_package<
       offset_arg<1>
     , composite_argument_package<
           offset_arg<9>
         , composite_argument_package_native<
               PyObject*
           >
       >
   >

   template<class Pkg, class Base>
   struct composite_argument_package : Base
   {
       // some forwarding constructors goes here

       Pkg pkg_;
   };

   template<class Pkg, class Base, int N>
   PyObject* get(
       const composite_argument_package<Pkg, Base>& x, mpl::int_<N>)
   {
       // forward to correct get() function
       return get(x.pkg_, static_cast<const Base&>(x), mpl::int_<N>());
   }

See what I'm driving at?

BTW, if you implement the composite CallPolicies like you have in BPL
there is no reason why offset_arg couldn't store it's Base. That's of
course a perfectly fine model of CallPolicies as well.
composite_call_policies is just an example of how it can be done without
explicit inheritance from your "Base policy".

-- 
Daniel Wallin



-------------------------------------------------------
This SF. Net email is sponsored by: GoToMyPC
GoToMyPC is the fast, easy and secure way to access your computer from
any Web browser or wireless device. Click here to Try it Free!
https://www.gotomypc.com/tr/OSDN/AW/Q4_2003/t/g22lp?Target=mm/g22lp.tmpl
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.