2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-20 16:52:15 +00:00
Files
python/todo.txt
Dave Abrahams 57dd8ff535 boost-ification
[SVN r8340]
2000-11-27 08:04:05 +00:00

427 lines
18 KiB
Plaintext

Check for const reference parameters in all from_python functions in py.h, including implementations.
Better python and C++ exception handling/error reporting.
long long support
use Python generic numeric coercion in from_python() for C++ numeric types
Rename PyPtr to Reference.
Report Cygwin linker memory issues
__init__ stuff
Make abstract classes non-instantiable (?)
Call default __init__ functions automatically where applicable (?)
Support for Python LONG types in Objects.h
Throw TypeError after asserting when objects from objects.cpp detect a type mismatch.
Figure out how to package everything as a shared library.
Unicode string support
Add read-only wrapper for __dict__ attribute
Objects.h support for generic objects, Sequence objects, etc.
empty() member functions for objects.hpp
Testing
Python 2.0
object revival in __del__
More thorough tests of objects.h/cpp classes
Better reference-count checking
Optimizations
Remove one level of indirection on type objects (no vtbl?).
Specializations of Caller<> for commmon combinations of argument types (?)
Replace uses of XXXable classes
Don't allocate instance __dict__ unless used.
Documentation:
differences between Python classes and ExtensionClasses
additional capabilities of ExtensionClasses
slice adjustment
Why special attributes other than __doc__ and __name__ are immutable.
An example of the problems with the built-in Python classes.
>>> class A:
... def __getattr__(self, name):
... return 'A.__getattr__'
...
>>> class B(A): pass
...
>>> class C(B): pass
...
>>> C().x
'A.__getattr__'
>>> B.__bases__ = ()
>>> C().x
'A.__getattr__'
Smart pointers
#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
namespace py {
#endif
template <class T>
struct VtkConverters
{
typedef py::PyExtensionClassConverters<T> Converters;
friend vtk_ptr<T>& from_python(PyObject* p, py::Type<vtk_ptr<T>&>)
{ return Converters::ptr_from_python(p, py::Type<vtk_ptr<T> >()); }
friend vtk_ptr<T>& from_python(PyObject* p, py::Type<vtk_ptr<T> >)
{ return Converters::ptr_from_python(p, py::Type<vtk_ptr<T> >()); }
friend const vtk_ptr<T>& from_python(PyObject* p, py::Type<const vtk_ptr<T>&>)
{ return Converters::ptr_from_python(p, py::Type<vtk_ptr<T> >()); }
friend PyObject* to_python(vtk_ptr<T> x)
{ return Converters::ptr_to_python(x); }
};
#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
}
#endif
template <class T>
struct VtkWrapper : py::ClassWrapper<T>, py::VtkConverters<T>
{
typedef py::ClassWrapper<T> Base;
VtkWrapper(Module& module, const char* name)
: Base(module, name) {}
};
exception handling
Advanced Topics:
Advanced Type Conversion
adding conversions for fundamental types
generic conversions for template types (with partial spec).
Interacting with built-in Python objects and types from C++
dealing with non-const reference/pointer parameters
extending multiple-argument support using gen_all.py
Fancy wrapping tricks
templates
Yes. If you look at the examples in extclass_demo.cpp you'll see that I have
exposed several template instantiations (e.g. std::pair<int,int>) in Python.
Keep in mind, however, that you can only expose a template instantiation,
not a template. In other words, MyTemplate<Foo> can be exposed. MyTemplate
itself cannot.
Well, that's not strictly true. Wow, this is more complicated to explain
than I thought.
You can't make an ExtensionClass<MyTemplate>, since after all MyTemplate is
not a type. You can only expose a concrete type to Python.
What you *can* do (if your compiler supports partial ordering of function
templates - MSVC is broken and does not) is to write appropriate
from_python() and to_python() functions for converting a whole class of
template instantiations to/from Python. That won't let you create an
instance of MyTemplate<SomePythonType> from Python, but it will let you
pass/return arbitrary MyTemplate<SomeCplusplusType> instances to/from your
wrapped C++ functions.
template <class T>
MyTemplate<T> from_python(PyObject* x, py::Type<MyTemplate<T> >)
{
// code to convert x into a MyTemplate<T>... that part is up to you
}
template <class T>
PyObject* from_python(const MyTemplate<T>&)
{
// code to convert MyTemplate<T> into a PyObject*... that part is up to
you
}
For example, you could use this to convert Python lists to/from
std::vector<T> 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<Record>::ptr_to_python(const_cast<Record*>(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 <class T>
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<T>&), 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<class_A>.
See http://people.ne.mediaone.net/abrahams/downloads/under-the-hood.html for
a few details.
raw C++ arrays
You could expose a function like this one to get the desired effect:
#include <py_cpp/objects.h>
void set_len(UnitCell& x, py::Tuple tuple)
{
double len[3];
for (std::size_t i =0; i < 3; ++i)
len[i] = py::from_python(tuple[i].get(), py::Type<double>());
x.set_len(len);
}
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.
Alternatively, If the NTL gives you a C/C++ interface, you can also write
your own converter function:
some_ntl_type& from_python(PyObject* p, py::Type<some_NTL_type&>)
{
// an Example implementation. Basically, you need
// to extract the NTL type from the PyObject*.
if (p->ob_type != NTL_long_type) {
PyErr_SetString(PyExc_TypeErr, "NTL long required");
throw py::ArgumentError();
}
return *static_cast<some_NTL_type*>(p);
}
then the C++ functions you're wrapping can take a some_NTL_type& parameter
directly.
"Thin converting wrappers" for constructors
hijack some of the functionality
described in the section on Overridable Virtual Functions (even though you
don't have any virtual functions). I suggest this workaround:
struct UnitCellWrapper : UnitCell
{
UnitCellWrapper(PyObject* self, py::Tuple x, py::Tuple y)
: UnitCell(from_python(x[1], py::Type<double>()),
from_python(x[2], py::Type<double>()),
from_python(x[3], py::Type<double>()),
from_python(y[1], py::Type<double>()),
from_python(y[2], py::Type<double>()),
from_python(y[3], py::Type<double>()))
{}
}
py::ClassWrapper<UnitCell, UnitCellWrapper> unit_cell_class;
unit_cell_class.def(py::Constructor<py::Tuple, py::Tuple>());
...
returning references to wrapped objects
the importance of declaration order of ClassWrappers/ExtensionInstances
out parameters and non-const pointers
Calling back into Python:
// caveat: UNTESTED!
#include <py_cpp/pyptr.h>
#include <py_cpp/callback.h>
#include <py_cpp/py.h>
#include <Python.h>
int main()
{
try {
py::Ptr module(PyImport_ImportModule("weapons"));
const int strength = 10;
const char* manufacturer = "Vordon Empire";
py::Ptr a_blaster(py::Callback<py::Ptr>::call_method(
module.get(), "Blaster", strength, manufacturer));
py::Callback<void>::call_method(a_blaster.get(), "Fire");
int old_strength = py::Callback<int>::call_method(a_blaster.get(), "get_strength");
py::Callback<void>::call_method(a_blaster.get(), "set_strength", 5);
}
catch(...)
{
}
}
Miscellaneous
About the vc6 project and the debug build
About doctest.py
Boost remarks:
> > One of us is completely nuts ;->. How can I move the test
> > (is_prefix(enablers[i].name + 2, name + 2)) outside the loop if it
depends
> > on the loop index, i?
> >
> name += 2;
> for()
> {
> if (is_prefix(enablers[i].name + 2, name))
> }
I see now. I guess I should stop pussyfooting and either go for optimization
or clarity here, eh?
------
> Re: Dict
> Why abbreviate this? Code is read 5 or 6 times for every time its
> written. The few extra characters don't affect compile time or program
> speed. It's part of my personal goal of write what you mean, name them
what
> they are.
I completely agree. Abbrevs rub me the wrong way, 2 ;->
-------
Later:
keyword and varargs?
Put explicit Type<> arguments at the beginnings of overloads, to make them look more like template instance specifications.
Known bugs
can't handle 'const void' return values
Who returns 'const void'? I did it once, by mistake ;)