[C++-sig] generic object wrappers

Arnaldur Gylfason arnaldur at decode.is
Tue Jan 22 11:08:25 CET 2002


<<<
   > I've looked at mpl and am comfortable using it. I feel we should use
it
   > while in development and not worry about rewriting stuff unless mpl
will
   > not be officially in boost when the boost.python rewrite is ready.

   Agreed. It might be choking one of my most important targets, but I'm
not
   sure yet. I hope not.
>>>

It's an experiment (an interesting one). It either works or not.
If it works the interface to users will be relatively straight forward.
The complexity will be hidden from them in the implementation.
Even if we have to rewrite things, it won't take too long.
(Maybe you were referring to something else?)

<<<
   > We have discussed sets of capabilities like integral, etc. I was
   > wondering if we should pick a few concepts from boost.operators as
   > sets of capabilities.

   Well, I'm beginning to wonder if this is all too complicated to be
   worthwhile. If not, that would be my fault :(.
>>>

I don´t think so. Again, complexity should be hidden from the user.
Also my comment above shouldn't make things complicated. It´s for
convenience.
In operators you define addable, subtractable etc. same way we aim for the
capabilities.
So maybe it makes sense to define sets, like additive, arithmetic, ring
etc. just as in operators.


<<<
   Take the easy way out and re-use the MPL primitives like I did.
   from boost/python/class.hpp in CVS:

     // A type list for specifying bases
     template < BOOST_MPL_LIST_DEFAULT_PARAMETERS(typename B,
   ::boost::mpl::null_argument) >
     struct bases : ::boost::mpl::type_list< BOOST_MPL_LIST_PARAMETERS(B)
   >::type
     {};

     // A type list for specifying arguments
     template < BOOST_MPL_LIST_DEFAULT_PARAMETERS(typename A,
   ::boost::mpl::null_argument) >
     struct args : ::boost::mpl::type_list< BOOST_MPL_LIST_PARAMETERS(A)
   >::type
     {};

   It saved me from having to learn all about the preprocessor library
right
   now ;-)
>>>

Sounds good! I'll do that.


<<<
   As I was checking my latest work I ran into lots of reference-counting
   problems, which just reminded me of the importance of religiously using
the
   stuff in reference.hpp. I began to think about a redesign which would
also
   help hide the fact that, e.g. PyTypeObject isn't really derived from
   PyObject.

   The design looks like this:

   1. A traits class is defined as follows:

       template <class T>
       struct base_type_traits
       {
           typedef PyObject type;
       };

      This could be specialized for types which are derived from other
Python
   types. For example, it is now possible to make a new extension type
derived
   from dict, list, mapping, or int:

       template <>
       struct base_type_traits<MyListSubtype>
       {
           typedef PyListObject type;
       };

   2. A small metaprogram constructs a proxy object which is implicitly
   convertible to T* for T in the chain of base types:

       template <class T>
       struct reference_proxy
           : reference_proxy<typename base_type_traits<T>::type>
       {
           typedef typename base_type_traits<T>::type base_t;
           reference_proxy(T* p)
               : reference_proxy<base_t>((base_t*)p)
           {}
           operator T*() const { return (T*)m_p; }
       }

       template <>
       struct reference_proxy<PyObject>
       {
        public:
           reference_proxy(PyObject* p) : m_p(p) {}
           operator PyObject*() const { return (PyObject*)m_p; }
           PyObject* m_p;
       }

   3. This proxy is the result of reference<T>::get(). The default argument
to
   reference<> is PyObject so we can write python::reference<> most of the
   time.

   4. We can use the traits to manage the implicit convertibility between
   different reference<> specializations.

      Well, anyway, it's a thought. Less ambitious than what you're doing
and
   immediately useful. On the other hand, I can do it if you'd prefer ;-)
>>>

This looks neat!
Just to be sure, reference<MyListSubtype>::get() would return
a reference_proxy<MyListSubtype> which inherits from
reference_proxy<PyListObject> which
inherits from reference_proxy<PyObject>.
It would thus provide automatic conversions to PyObject*, PyListObject* and
MyListSubtype*,
just as if they were related through C++ inheritance.

I have been thinking about it's use though.
In the generic wrappers I only work with PyObject* and don't need anything
else.
In concrete objects like list, we know the type and can safely typecast to
PyListObject*
if/when needed (I did that in the iterator support).
The Python C API always work with PyObject*, even in functions working with
concrete types like list.
The PyObject*is converted to PyListObject* within the function.
If we would have API functions for PyObject*, PyListObject* (and
MyListSubtype*) etc. I could see
direct benefit from the design above. reference<T>::get() would pass in all
cases.

If you want to provide interfaces for different specializations of
reference<T>
and provide implicit convertibility on that level, the design would help.
function f(reference<PyListObject>) could then be called with a
reference<PyListObject> and
reference<MyListSubtype>.
I did not foresee many interfaces that work on the reference level though.
E.g. rather than f(reference<PyListObject>) I would have f(list).
a MyListSubtype would be used as a generic object (object<indexable,...>)
but the automatic conversion
to list would call PyList_Check and pass it. Thus the function f can be
called on a MyListSubtype.

I am probably out in the wild. I'm just trying to imagine usage of the
above design.
Please tell me what usage you had in mind.

Cheers

Arnaldur







More information about the Cplusplus-sig mailing list