[C++-sig] Re: C arrays solution

Raoul Gough RaoulGough at yahoo.co.uk
Tue Oct 14 12:43:54 CEST 2003


"Niall Douglas" <s_sourceforge at nedprod.com> writes:

> On 14 Oct 2003 at 1:00, Raoul Gough wrote:
[snip]
>> >> Also, I wonder about the use of member
>> >> function pointers - is there an easier way to get what you want?
>> >
>> > Not at all. Those member functions are the official interface for
>> > accessing the array's contents. They must be called at run-time to
>> > yield correct access.
>> 
>> It was more a question of how you inform your wrapper layer what
>> functions should be called. e.g. you could use a traits class, which
>> would take care of this on a per-type basis (assuming that the
>> functions to call don't vary from one object to another).
>
> Already thought of that and they do vary in name from container to 
> container.

Do you mean they just vary from container type to container type? Or
from one instance of a container type to another? For instance, the
standard library can use char_traits::eq to compare chars, because the
comparison function doesn't vary from one char object to another (just
from one *type* of char to another).

>
>> Or you could
>> just use function overloading, something like the indexing::begin()
>> and indexing::end() functions for arrays.
>
> That's interesting, but wouldn't it require a separate declaration of 
> the required functions somewhere beforehand? The solution I chose 
> means very minimal manual editing of pyste's output - in fact, I'm 
> going to make it a diff file applied by scons after calling pyste.

Yes it would. However, I would assume that the number of different
types of containers within the library is more or less fixed, so you
could just put this sort of thing in a separate header:

template<typename Element>
boost::python::indexing::iterator_pair<Element *>
make_iter_pair (container_type_1<Element> &c1)
{
  return boost::python::indexing::iterator_pair<Element *>
    (c1.getArray(), c1.getArray() + c1.getLength());
}

template<typename Element>
boost::python::indexing::iterator_pair<Element *>
make_iter_pair (container_type_2<Element> &c)
{
  return boost::python::indexing::iterator_pair<Element *>
    (c2.get_array(), c2.get_array() + c2.get_length());
}

Basically, you've somewhere got to say "use getArray here" and "use
get_array" there. If that is invariant on the container type, why mess
around with storing pointers to member functions in every object?
Things would be a bit different if you have a single type that
contains two or more different sequences and you want to select from
any of them at run-time.

>
>> > Now I've explained, your thoughts on whether it would be of use to
>> > others?
>> 
>> Well, it's starting to make more sense to me. In fact, I think there
>> is already something similar to this which returns a Python iterator
>> instead of an indexing::iterator_pair. IIRC it's called
>> boost::python::range or similar. The iterator_pair (with the container
>> suite) has far more functionality than a Python iterator, of course.
>
> Does boost::python::range provide random access? I don't think it 
> does.

Like I said, the iterator pair/container suite combination provides a
lot more functionality, but that doesn't really affect the choice of
interface, since the only requirement is somehow to provide an
iterator range through it. The range() function accepts pointers to
member functions, pointers to normal functions or even pointers to
member variables. Just to prove all my ideas ill-founded, it doesn't
support traits or function overloading :-)

> Basically, I want to make C arrays available as lists to python. Your 
> suite (and thank you for it) with my little extension appears to do 
> this very nicely indeed.
>
> For example, there's now no reason why a bitmap's contents cannot be 
> directly manipulated by python eg; bitmapdata[4]=0xff puts a white 
> pixel at (4,0) on the screen.

Of course, this particular example wouldn't be very fast. That
expression results in a whole bunch of Boost.Python code being
executed to figure out which C++ functions to call and how to extract
the C++ objects from the Python ones. Where you really get performance
is if you can do one call from Python to C++ that then performs a
whole lot of processing.

>
>> To summarise: this boils down to a method for generating an
>> iterator_pair from an arbitrary object which has some way of
>> generating two iterators. Does that sound about right?
>
> Nearly.
>
> This boils down to a method for generating an iterator_pair from an 
> arbitrary object providing no more than a pointer and a length. The 
> pointer and length can be retrieved from any arbitrary function 
> except ones which return the value through their parameters (a lambda 
> wrap could fix these though).

On the other hand, the begin() and end() style is more general.  It's
also easy enough to write a wrapper for it if you only have begin()
and length(), but not so in the other direction. For example, if your
container doesn't have random access, using begin() and length()
doesn't make sense at all.

>
> Once I'm done with the overloads, you won't even need a container 
> object. I have several static global arrays I need to provide to 
> python.

Maybe something like this would be useful to have in the library?

  template<typename T, std::size_t N>
  iterator_pair<T *> make_iterator_pair (T (&array)[N])
  {
    return iterator_pair<T *>(array, array + N);
  }

-- 
Raoul Gough.
(setq dabbrev-case-fold-search nil)





More information about the Cplusplus-sig mailing list