mirror of
https://github.com/boostorg/python.git
synced 2026-01-20 16:52:15 +00:00
427 lines
18 KiB
Plaintext
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 ;)
|