diff --git a/release_notes.txt b/release_notes.txt index f6a03421..47deffc0 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,8 +1,16 @@ -2000-11-02 23:25 +2000-11-03 10:58 + + Fix friend function instantiation bug caught by Metrowerks (thanks + Metrowerks!) + + Add proof-of-concept for one technique of wrapping function that return a + pointer Worked around MSVC optimizer bug by writing to_python(double) and to_python(float) out-of-line +2000-11-02 23:25 + Add /Zm200 option to vc6_prj to deal with MSVC resource limitations Remove conflicting /Ot option from vc6_prj release build diff --git a/todo.txt b/todo.txt index 8e260cef..8a6e3da3 100644 --- a/todo.txt +++ b/todo.txt @@ -129,7 +129,165 @@ Documentation: For example, you could use this to convert Python lists to/from std::vector automatically. + Pointer return values + + Case 1: + + > I am now also able to wrap the problematic TextRecordIterator for Python. + > However, one of its function compiles with this warning: + > + > d:\py_cpp/caller.h(33) : warning C4800: 'const class Record *const ' + > : forcing value to bool 'true' or 'false' (performance warning) + > d:\py_cpp/functions.h(54) : see reference to function template + > instantiation 'struct _object *__cdecl py::Caller::call(const class Record + > *const (__thiscall TextRecordIterator::*)(void),struct _object *,struct + > _object *)' being compiled + > + > If you look at the offending code, you'll see that we really do need to + > get back that pointer: + > + > const Record* const TextRecordIterator::Next() { + > if (fStatus != RecordIterator::SUCCESS) { + > return 0; + > } else { + > return &fData; + > } + > } + > + > The point of the TextRecordIterator is to hand over one reord after + > another. A bool wouldn't do us much good here :-) + > + > Do you have any suggestions for fixing this? + + In general, py_cpp doesn't automatically convert pointer return values + to_python because pointers have too many potential meanings. Is it an + iterator? A pointer to a single element? An array? Is ownership being passed + to Python or is the pointer really just a reference? If the latter, what + happens when some C++ code deletes the referent. The only exception to this + rule is const char*, since it has a generally accepted interpretation (could + be trouble with some generic code, though!) + + If you have wrapped the Record class, you could add this to namespace py: + + PyObject* to_python(const Record* p) { + return to_python(*p); + } + + Of course, this will cause the Record class to be copied. If you can't live + with that (Record would have to be /really/ heavyweight to make this + worthwhile), you can follow one of these dangerous approaches: + + 1. Use the technique I described with dangerous_array in + http://www.egroups.com/message/boost/6196. You do not have to expose Record + explicitly in this case. Instead the class you expose will be more of a + Record_proxy + + 2. Wrap Record in the usual way, then add the following to namespace py: + + PyObject* to_python(const Record* p) + { + return ExtensionClass::ptr_to_python(const_cast(p)); + } + + This will cause the Record* to be treated as though it were an owning smart + pointer, even though it's not. Be sure you don't use the reference for + anything from Python once the pointer becomes invalid, though. Don't worry + too much about the const-correctness issue: Const-correctness is completely + lost to Python anyway! + + 3. As above, but instead wrap const Record rather than plain Record. Then + you can avoid the const_cast, but you obviously can't def() any non-const + member functions of Record. + + Case 2: + + > I have yet another question. This is more a general wrapper question. + > Let me say that there is a function that returns a float* which most + > probably is an array. Similarly if I have a function that takes a + > float* as an argument, what is the best way of wrapping this? + + I think you have correctly perceived that it doesn't make sense for me to + automatically convert all pointers, since the ownership semantics are so + blurry. + + > 1) If the array is small it makes sense to convert it to either a + > tuple or list. What is the easiest way to do this?? I am looking + > for a way that makes one write the least code. :) + + How can you tell the length of the array from a single pointer? + Once you've answered that question, you can expose a wrapper function which + returns an instance of the py::Tuple or py::List class from objects.h. If + you are using a List, for example, you could write something like this: + + py::List wrap_f() + { + T* start = f(); + py::List x; + for (T* p = start; p != start + length_constant; ++p) + x.push_back(py::to_python(*p)); + return x; + } + + > 2) If the array is large it may not make sense to use a list/tuple + > esp. if the values are used for computationally intense programs. + + In this case you can do one of several somewhat dangerous things. Why + dangerous? Because python can not control the lifetime of the data, so the + data in the array may be destroyed or become invalid before the last + reference to it disappears. The basic approach is to make a small C++ class + which contains the pointer, and expose that: + + // UNTESTED + template + struct dangerous_array + { + dangerous_array(T* start, T* end) + : m_start(start), m_end(end) {} + + // exposed as "__len__" + std::size_t length() { + return m_end - m_start; + } + + // exposed as "__getitem__" + T get_item(std::size_t n) { + check_range(n); + return start[n]; + } + + // exposed as "__setitem__" if the array is mutable + void set_item(std::size_t n, const T& x) { + check_range(n); + start[n] = x; + } + private: + void check_range(std::size_t n) { + if (n >= m_end - m_start) { + PyErr_SetString(PyExc_IndexError, "array index out of range"); + throw py::ErrorAlreadySet; + } + } + T* m_start; + T* m_end; + }; + + A reasonably safe approach would be to make a wrapper function for each + function that returns a T*, and expose that instead. If you're too lazy and + you really like to live on the edge, though, you can write to_python(T*) in + terms of to_python(const dangerous_array&), and you'll automatically + convert all T* return values to a wrapped dangerous_array. + + > 3) For an arbitrary class "class_A", say, can py_cpp handle + > references to class_A &instance, or class_A *instance?? i.e. will it + > wrap function calls to such objects? This question is obviously + > related to the earlier questions. + + Yes, iff class_A has been exposed to python with a ClassWrapper. + See http://people.ne.mediaone.net/abrahams/downloads/under-the-hood.html for + a few details. + Types that are already wrapped by other libraries + It's not documented yet, but you should be able to use a raw PyObject* or a py::Ptr as one parameter to your C++ function. Then you can manipulate it as any other generic Python object.