From a870ce20fc54772cff95ced17ba50f224f02cf19 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 8 Mar 2003 03:53:19 +0000 Subject: [PATCH] Added dangling_reference FAQ Various idiomatic MPL cleanups in indirect_traits.hpp raw_function support Patches for CWPro7.2 Patches to pass tests under Python 2.3 with the new bool type. Tests for member operators returning const objects Fixes for testing Boost.Python under Cygwin [SVN r17777] --- doc/v2/def.html | 119 ++-- doc/v2/faq.html | 577 +++++++++--------- doc/v2/reference.html | 26 +- include/boost/python/class.hpp | 2 + include/boost/python/def.hpp | 7 +- .../boost/python/detail/indirect_traits.hpp | 157 +++-- include/boost/python/init.hpp | 5 +- include/boost/python/object/inheritance.hpp | 11 +- .../boost/python/object/make_ptr_instance.hpp | 8 +- src/object/function.cpp | 96 ++- test/args.cpp | 10 + test/args.py | 8 + test/back_reference.py | 3 +- test/enum.cpp | 12 +- test/operators.cpp | 15 +- test/test_builtin_converters.py | 30 +- test/test_pointer_adoption.py | 6 +- 17 files changed, 618 insertions(+), 474 deletions(-) diff --git a/doc/v2/def.html b/doc/v2/def.html index fdd0516f..80978a87 100644 --- a/doc/v2/def.html +++ b/doc/v2/def.html @@ -3,7 +3,7 @@ + "HTML Tidy for Cygwin (vers 1st April 2002), see www.w3.org"> @@ -75,6 +75,12 @@ void def(char const* name, Fn fn, A1 const&, A2 const&, A3 const&);
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Memnonic NameRequirements/Type propertiesEffects
docstringAny ntbs.Value will be bound to the __doc__ attribute of + the resulting method overload.
policiesA model of CallPoliciesA copy will be used as the call policies of the resulting + method overload.
keywordsThe result of a keyword-expression + specifying no more arguments than the arity of fn.A copy will be used as the call policies of the resulting + method overload.
@@ -174,6 +165,8 @@ void def(char const* name, Fn fn, A1 const&, A2 const&, A3 const&); #include <boost/python/module.hpp> #include <boost/python/args.hpp> +using namespace boost::python; + char const* foo(int x, int y) { return "foo"; } BOOST_PYTHON_MODULE(def_test) @@ -184,8 +177,8 @@ BOOST_PYTHON_MODULE(def_test)

- 13 November, 2002 - + 7 March, 2003 +

© Copyright + + "HTML Tidy for Cygwin (vers 1st April 2002), see www.w3.org"> - - Boost.Python - FAQ - + + Boost.Python - FAQ + - +
+
-

- C++ Boost -

+

C++ Boost

-

- Boost.Python -

-

- Frequently Asked Questions (FAQs) -

+

Boost.Python

+ +

Frequently Asked Questions (FAQs)


+
-
- Is return_internal_reference efficient? -
-
- How can I wrap functions which take C++ - containers as arguments? -
-
- fatal error C1204:Compiler limit:internal structure - overflow -
-
- How do I debug my Python extensions? -
-
- Why doesn't my *= operator work? -
-
- Does Boost.Python work with Mac OS X? -
-
- How can I find the existing PyObject that holds a C++ - object? -
-
- How can I wrap a function which needs to take - ownership of a raw pointer? -
+
I'm getting the "attempt to return dangling + reference" error. What am I doing wrong?
+ +
Is return_internal_reference + efficient?
+ +
How can I wrap functions which take C++ + containers as arguments?
+ +
fatal error C1204:Compiler limit:internal + structure overflow
+ +
How do I debug my Python extensions?
+ +
Why doesn't my *= operator + work?
+ +
Does Boost.Python work with Mac OS X?
+ +
How can I find the existing PyObject that holds a + C++ object?
+ +
How can I wrap a function which needs to take + ownership of a raw pointer?

-

- Is return_internal_reference efficient? -

+ +

I'm getting the "attempt to return dangling + reference" error. What am I doing wrong?

+ That exception is protecting you from causing a nasty crash. It usually + happens in response to some code like this: +
+period const& get_floating_frequency() const
+{
+  return boost::python::call_method<period const&>(
+      m_self,"get_floating_frequency");
+}
+
+ And you get: +
+ReferenceError: Attempt to return dangling reference to object of type:
+class period
+
+ +

In this case, the Python method invoked by call_method + constructs a new Python object. You're trying to return a reference to a + C++ object (an instance of class period) contained within + and owned by that Python object. Because the called method handed back a + brand new object, the only reference to it is held for the duration of + get_floating_frequency() above. When the function returns, + the Python object will be destroyed, destroying the instance of + class period, and leaving the returned reference dangling. + That's already undefined behavior, and if you try to do anything with + that reference you're likely to cause a crash. Boost.Python detects this + situation at runtime and helpfully throws an exception instead of letting + you do that.
+  

+
+ +

Is return_internal_reference efficient?

+
Q: I have an object composed of 12 doubles. A const& to this object is returned by a member function of another class. From the @@ -74,126 +99,129 @@ return_internal_reference. Are there considerations that would lead me to prefer one over the other, such as size of generated code or memory overhead? -

- A: copy_const_reference will make an instance with storage for - one of your objects, size = base_size + 12 * sizeof(double). - return_internal_reference will make an instance with storage for a - pointer to one of your objects, size = base_size + sizeof(void*). - However, it will also create a weak reference object which goes in - the source object's weakreflist and a special callback object to - manage the lifetime of the internally-referenced object. My guess? - copy_const_reference is your friend here, resulting in less overall - memory use and less fragmentation, also probably fewer total cycles. -

+ +

A: copy_const_reference will make an instance with storage + for one of your objects, size = base_size + 12 * sizeof(double). + return_internal_reference will make an instance with storage for a + pointer to one of your objects, size = base_size + sizeof(void*). + However, it will also create a weak reference object which goes in the + source object's weakreflist and a special callback object to manage the + lifetime of the internally-referenced object. My guess? + copy_const_reference is your friend here, resulting in less overall + memory use and less fragmentation, also probably fewer total + cycles.


-

- How can I wrap functions which take C++ - containers as arguments? -

-

- Ralf W. Grosse-Kunstleve provides these notes: -

+ +

How can I wrap functions which take C++ + containers as arguments?

+ +

Ralf W. Grosse-Kunstleve provides these notes:

+
  1. Using the regular class_<> wrapper: -
    +
     class_<std::vector<double> >("std_vector_double")
       .def(...)
       ...
       ;
    -
    This can be moved to a template so that several types (double, int, -long, etc.) can be wrapped with the same code. This technique is used in the -file +
    + This can be moved to a template so that several types (double, int, + long, etc.) can be wrapped with the same code. This technique is used + in the file +
    scitbx/include/scitbx/array_family/boost_python/flex_wrapper.h -
    in the "scitbx" package. The file could easily be - modified for wrapping std::vector<> instantiations. -

    - This type of C++/Python binding is most suitable for containers - that may contain a large number of elements (>10000). -

    + + in the "scitbx" package. The file could easily be modified for + wrapping std::vector<> instantiations. + +

    This type of C++/Python binding is most suitable for containers + that may contain a large number of elements (>10000).

  2. +
  3. Using custom rvalue converters. Boost.Python "rvalue converters" match function signatures such as: -
    +
     void foo(std::vector<double> const& array); // pass by const-reference
     void foo(std::vector<double> array); // pass by value
    -
    Some custom rvalue converters are implemented in the file +
    + Some custom rvalue converters are implemented in the file +
    scitbx/include/scitbx/boost_python/container_conversions.h -
    This code can be used to convert from C++ container - types such as std::vector<> or std::list<> to Python - tuples and vice versa. A few simple examples can be found in the file - + + This code can be used to convert from C++ container types such as + std::vector<> or std::list<> to Python tuples and vice + versa. A few simple examples can be found in the file +
    scitbx/array_family/boost_python/regression_test_module.cpp -
    Automatic C++ container <-> Python tuple - conversions are most suitable for containers of moderate size. These - converters generate significantly less object code compared to - alternative 1 above. + + Automatic C++ container <-> Python tuple conversions are most + suitable for containers of moderate size. These converters generate + significantly less object code compared to alternative 1 above.
  4. -
A disadvantage of using alternative 2 is that operators such as + + A disadvantage of using alternative 2 is that operators such as arithmetic +,-,*,/,% are not available. It would be useful to have custom rvalue converters that convert to a "math_array" type instead of tuples. This is currently not implemented but is possible within the framework of Boost.Python V2 as it will be released in the next couple of weeks. [ed.: this was posted on 2002/03/10] -

- It would also be useful to also have "custom lvalue converters" such as - std::vector<> <-> Python list. These converters would - support the modification of the Python list from C++. For example: -

-

- C++: -

-
+
+    

It would also be useful to also have "custom lvalue converters" such + as std::vector<> <-> Python list. These converters would + support the modification of the Python list from C++. For example:

+ +

C++:

+
 void foo(std::vector<double>& array)
 {
   for(std::size_t i=0;i<array.size();i++) {
     array[i] *= 2;
   }
 }
-
Python: -
+
+ Python: +
 >>> l = [1, 2, 3]
 >>> foo(l)
 >>> print l
 [2, 4, 6]
-
Custom lvalue converters require changes to the Boost.Python core -library and are currently not available. -

- P.S.: -

-

- The "scitbx" files referenced above are available via anonymous CVS: -

-
+
+ Custom lvalue converters require changes to the Boost.Python core library + and are currently not available. + +

P.S.:

+ +

The "scitbx" files referenced above are available via anonymous + CVS:

+
 cvs -d:pserver:anonymous@cvs.cctbx.sourceforge.net:/cvsroot/cctbx login
 cvs -d:pserver:anonymous@cvs.cctbx.sourceforge.net:/cvsroot/cctbx co scitbx
 

-

- fatal error C1204:Compiler limit:internal structure - overflow -

+ +

fatal error C1204:Compiler limit:internal + structure overflow

+
Q: I get this error message when compiling a large source file. What can I do? -

- A: You have two choices: -

+ +

A: You have two choices:

+
    -
  1. - Upgrade your compiler (preferred) -
  2. +
  3. Upgrade your compiler (preferred)
  4. +
  5. Break your source file up into multiple translation units. -

    - my_module.cpp: -

    -
    +
    +          

    my_module.cpp:

    +
     ...
     void more_of_my_module();
     BOOST_PYTHON_MODULE(my_module)
    @@ -203,23 +231,25 @@ BOOST_PYTHON_MODULE(my_module)
        ...
        more_of_my_module();
     }
    -
    more_of_my_module.cpp: -
    +
    + more_of_my_module.cpp: +
     void more_of_my_module()
     {
        def("baz", baz);
        ...
     }
    -
    If you find that a class_<...> declaration can't fit -in a single source file without triggering the error, you can always pass a -reference to the class_ object to a function in another source -file, and call some of its member functions (e.g. .def(...)) in -the auxilliary source file: -

    - more_of_my_class.cpp: -

    -
    +
    + If you find that a class_<...> declaration + can't fit in a single source file without triggering the error, you + can always pass a reference to the class_ object to a + function in another source file, and call some of its member + functions (e.g. .def(...)) in the auxilliary source + file: + +

    more_of_my_class.cpp:

    +
     void more_of_my_class(class<my_class>& x)
     {
        x
    @@ -234,12 +264,11 @@ void more_of_my_class(class<my_class>& x)
           

-

- How do I debug my Python extensions? -

-

- Greg Burley gives the following answer for Unix GCC users: -

+ +

How do I debug my Python extensions?

+ +

Greg Burley gives the following answer for Unix GCC users:

+
Once you have created a boost python extension for your c++ library or class, you may need to debug the code. Afterall this is one of the @@ -249,13 +278,12 @@ void more_of_my_class(class<my_class>& x) boost::python either works or it doesn't. (ie. While errors can occur when the wrapping method is invalid, most errors are caught by the compiler ;-). -

- The basic steps required to initiate a gdb session to debug a c++ - library via python are shown here. Note, however that you should - start the gdb session in the directory that contains your BPL - my_ext.so module. -

-
+
+      

The basic steps required to initiate a gdb session to debug a c++ + library via python are shown here. Note, however that you should start + the gdb session in the directory that contains your BPL my_ext.so + module.

+
 (gdb) target exec python
 (gdb) run
  >>> from my_ext import *
@@ -269,44 +297,43 @@ Current language:  auto; currently c++
 (gdb) do debugging stuff
 
-

- Greg's approach works even better using Emacs' "gdb" - command, since it will show you each line of source as you step through - it. -

-

- On Windows, my favorite debugging solution is the debugger that - comes with Microsoft Visual C++ 7. This debugger seems to work with - code generated by all versions of Microsoft and Metrowerks toolsets; - it's rock solid and "just works" without requiring any special tricks - from the user. -

-

- Unfortunately for Cygwin and MinGW users, as of this writing gdb on - Windows has a very hard time dealing with shared libraries, which could - make Greg's approach next to useless for you. My best advice for you is - to use Metrowerks C++ for compiler conformance and Microsoft Visual - Studio as a debugger when you need one. -

-

- Debugging extensions through Boost.Build -

If you are launching your extension module tests with Greg's approach works even better using Emacs' "gdb" + command, since it will show you each line of source as you step through + it.

+ +

On Windows, my favorite debugging solution is the debugger that + comes with Microsoft Visual C++ 7. This debugger seems to work with code + generated by all versions of Microsoft and Metrowerks toolsets; it's rock + solid and "just works" without requiring any special tricks from the + user.

+ +

Unfortunately for Cygwin and MinGW users, as of this writing gdb on + Windows has a very hard time dealing with shared libraries, which could + make Greg's approach next to useless for you. My best advice for you is + to use Metrowerks C++ for compiler conformance and Microsoft Visual + Studio as a debugger when you need one.

+ +

Debugging extensions through Boost.Build

+ If you are launching your extension module tests with
Boost.Build using the boost-python-runtest rule, you can ask it to launch your debugger for you by adding "-sPYTHON_LAUNCH=debugger" to your bjam command-line: -
+
 bjam -sTOOLS=metrowerks "-sPYTHON_LAUNCH=devenv /debugexe" test
 bjam -sTOOLS=gcc -sPYTHON_LAUNCH=gdb test
-
It can also be extremely useful to add the -d+2 option -when you run your test, because Boost.Build will then show you the exact -commands it uses to invoke it. This will invariably involve setting up -PYTHONPATH and other important environment variables such as LD_LIBRARY_PATH -which may be needed by your debugger in order to get things to work right. +
+ It can also be extremely useful to add the -d+2 option when + you run your test, because Boost.Build will then show you the exact + commands it uses to invoke it. This will invariably involve setting up + PYTHONPATH and other important environment variables such as + LD_LIBRARY_PATH which may be needed by your debugger in order to get + things to work right.
-

- Why doesn't my *= operator work? -

+ +

Why doesn't my *= operator work?

+
Q: I have exported my class to python, with many overloaded operators. it works fine for me except the *= @@ -314,60 +341,57 @@ which may be needed by your debugger in order to get things to work right. type". If I use p1.__imul__(p2) instead of p1 *= p2, it successfully executes my code. What's wrong with me? -

- A: There's nothing wrong with you. This is a bug in Python - 2.2. You can see the same effect in Pure Python (you can learn a lot - about what's happening in Boost.Python by playing with new-style - classes in Pure Python). -

-
+
+      

A: There's nothing wrong with you. This is a bug in Python + 2.2. You can see the same effect in Pure Python (you can learn a lot + about what's happening in Boost.Python by playing with new-style + classes in Pure Python).

+
 >>> class X(object):
 ...     def __imul__(self, x):
 ...         print 'imul'
 ...
 >>> x = X()
 >>> x *= 1
-
To cure this problem, all you need to do is upgrade your Python to -version 2.2.1 or later. +
+ To cure this problem, all you need to do is upgrade your Python to + version 2.2.1 or later.

-

- Does Boost.Python work with Mac OS X? -

+ +

Does Boost.Python work with Mac OS X?

+
-

- The short answer: as of January 2003, unfortunately not. -

-

- The longer answer: using Mac OS 10.2.3 with the December Developer's - Kit, Python 2.3a1, and bjam's darwin-tools.jam, Boost.Python compiles - fine, including the examples. However, there are problems at runtime - (see http://mail.python.org/pipermail/c++-sig/2003-January/003267.html). - Solutions are currently unknown. -

-

- It is known that under certain circumstances objects are - double-destructed. See http://mail.python.org/pipermail/c++-sig/2003-January/003278.html - for details. It is not clear however if this problem is related to - the Boost.Python runtime issues. -

+

The short answer: as of January 2003, unfortunately not.

+ +

The longer answer: using Mac OS 10.2.3 with the December Developer's + Kit, Python 2.3a1, and bjam's darwin-tools.jam, Boost.Python compiles + fine, including the examples. However, there are problems at runtime + (see http://mail.python.org/pipermail/c++-sig/2003-January/003267.html). + Solutions are currently unknown.

+ +

It is known that under certain circumstances objects are + double-destructed. See http://mail.python.org/pipermail/c++-sig/2003-January/003278.html + for details. It is not clear however if this problem is related to the + Boost.Python runtime issues.


-

- How can I find the existing PyObject that holds a C++ - object? -

+ +

How can I find the existing PyObject that holds a C++ + object?

+
"I am wrapping a function that always returns a pointer to an already-held C++ object." -
One way to do that is to hijack the mechanisms used for - wrapping a class with virtual functions. If you make a wrapper class with - an initial PyObject* constructor argument and store that PyObject* as - "self", you can get back to it by casting down to that wrapper type in a - thin wrapper function. For example: -
+    
+    One way to do that is to hijack the mechanisms used for wrapping a class
+    with virtual functions. If you make a wrapper class with an initial
+    PyObject* constructor argument and store that PyObject* as "self", you
+    can get back to it by casting down to that wrapper type in a thin wrapper
+    function. For example: 
+
 class X { X(int); virtual ~X(); ... };
 X* f();  // known to return Xs that are managed by Python objects
 
@@ -393,44 +417,43 @@ def("f", f_wrap());
 class_<X,X_wrap>("X", init<int>())
    ...
    ;
-
Of course, if X has no virtual functions you'll have to use -static_cast instead of dynamic_cast with no runtime -check that it's valid. This approach also only works if the X -object was constructed from Python, because Xs constructed from -C++ are of course never X_wrap objects. -

- Another approach to this requires some work on Boost.Python, but it's - work we've been meaning to get to anyway. Currently, when a - shared_ptr<X> is converted from Python, the - shared_ptr actually manages a reference to the containing Python - object. I plan to make it so that when a shared_ptr<X> is - converted back to Python, the library checks to see if it's one of - those "Python object managers" and if so just returns the original - Python object. To exploit this you'd have to be able to change the C++ - code you're wrapping so that it deals with shared_ptr instead of raw - pointers. -

-

- There are other approaches too. The functions that receive the Python - object that you eventually want to return could be wrapped with a thin - wrapper that records the correspondence between the object address and - its containing Python object, and you could have your f_wrap function - look in that mapping to get the Python object out. -

-

- How can I wrap a function which needs to take - ownership of a raw pointer? -

+
+ Of course, if X has no virtual functions you'll have to use + static_cast instead of dynamic_cast with no + runtime check that it's valid. This approach also only works if the + X object was constructed from Python, because + Xs constructed from C++ are of course never + X_wrap objects. + +

Another approach to this requires you to change your C++ code a bit; + if that's an option for you it might be a better way to go. work we've + been meaning to get to anyway. When a shared_ptr<X> is + converted from Python, the shared_ptr actually manages a reference to the + containing Python object. When a shared_ptr<X> is converted back to + Python, the library checks to see if it's one of those "Python object + managers" and if so just returns the original Python object. So you could + just write object(p) to get the Python object back. To + exploit this you'd have to be able to change the C++ code you're wrapping + so that it deals with shared_ptr instead of raw pointers.

+ +

There are other approaches too. The functions that receive the Python + object that you eventually want to return could be wrapped with a thin + wrapper that records the correspondence between the object address and + its containing Python object, and you could have your f_wrap function + look in that mapping to get the Python object out.

+ +

How can I wrap a function which needs to take + ownership of a raw pointer?

+
Part of an API that I'm wrapping goes something like this: -
+
 struct A {}; struct B { void add( A* ); }
 where B::add() takes ownership of the pointer passed to it.
 
-

- However: -

-
+
+      

However:

+
 a = mod.A()
 b = mod.B()
 b.add( a )
@@ -439,41 +462,43 @@ del b
 # python interpreter crashes 
 # later due to memory corruption.
 
-

- Even binding the lifetime of a to b via - with_custodian_and_ward doesn't prevent the python object a from - ultimately trying to delete the object it's pointing to. Is there a - way to accomplish a 'transfer-of-ownership' of a wrapped C++ object? -

-

- --Bruce Lowery -

-
Yes: Make sure the C++ object is held by auto_ptr: -
+
+      

Even binding the lifetime of a to b via + with_custodian_and_ward doesn't prevent the python object a from + ultimately trying to delete the object it's pointing to. Is there a way + to accomplish a 'transfer-of-ownership' of a wrapped C++ object?

+ +

--Bruce Lowery

+ + Yes: Make sure the C++ object is held by auto_ptr: +
 class_<A, std::auto_ptr<A> >("A")
     ...
     ;
-
Then make a thin wrapper function which takes an auto_ptr parameter: -
+
+ Then make a thin wrapper function which takes an auto_ptr parameter: +
 void b_insert(B& b, std::auto_ptr<A> a)
 {
     b.insert(a.get());
     a.release();
 }
-
Wrap that as B.add. Note that pointers returned via - manage_new_object - will also be held by auto_ptr, so this - transfer-of-ownership will also work correctly. +
+ Wrap that as B.add. Note that pointers returned via manage_new_object + will also be held by auto_ptr, so this transfer-of-ownership + will also work correctly.
-

- Revised - - 23 January, 2003 - -

-

- © Copyright Dave - Abrahams 2002-2003. All Rights Reserved. + +

Revised + + 23 January, 2003 +

+ +

© Copyright Dave Abrahams 2002-2003. All + Rights Reserved.

+ diff --git a/doc/v2/reference.html b/doc/v2/reference.html index 55a3da34..ae2ec560 100644 --- a/doc/v2/reference.html +++ b/doc/v2/reference.html @@ -3,7 +3,7 @@ + "HTML Tidy for Cygwin (vers 1st April 2002), see www.w3.org"> @@ -13,7 +13,7 @@ p.c3 {font-style: italic} h2.c2 {text-align: center} h1.c1 {text-align: center} - + @@ -527,6 +527,24 @@ + + +
raw_function.hpp
+ +
+
+
Functions
+ +
+
+
raw_function
+
+
+
+
+ +

Models of CallPolicies

@@ -913,8 +931,8 @@

Revised - 13 November, 2002 - + 7 March, 2003 +

© Copyright > >::failed test0; +# if !BOOST_WORKAROUND(__MWERKS__, <= 0x2407) typedef typename assertion >::failed test1; +# endif typedef typename assertion >::failed test2; not_a_derived_class_member(Fn()); } diff --git a/include/boost/python/def.hpp b/include/boost/python/def.hpp index 8914818b..1c77736d 100644 --- a/include/boost/python/def.hpp +++ b/include/boost/python/def.hpp @@ -76,12 +76,17 @@ namespace detail detail::define_with_defaults( name, stubs, current, detail::get_signature(sig)); } + + template + object make_function1(T fn, ...) { return make_function(fn); } + + object make_function1(object const& x, object const*) { return x; } } template void def(char const* name, Fn fn) { - detail::scope_setattr_doc(name, boost::python::make_function(fn), 0); + detail::scope_setattr_doc(name, detail::make_function1(fn, &fn), 0); } template diff --git a/include/boost/python/detail/indirect_traits.hpp b/include/boost/python/detail/indirect_traits.hpp index 3f6a0098..b1883cd3 100644 --- a/include/boost/python/detail/indirect_traits.hpp +++ b/include/boost/python/detail/indirect_traits.hpp @@ -6,7 +6,6 @@ #ifndef INDIRECT_TRAITS_DWA2002131_HPP # define INDIRECT_TRAITS_DWA2002131_HPP # include -# include # include # include # include @@ -16,8 +15,18 @@ # include # include # include + +# include + +# include +# if 0 && BOOST_WORKAROUND(__MWERKS__, <= 0x2407) +# include +# endif + # include # include +# include +# include # include # ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION @@ -28,27 +37,24 @@ namespace boost { namespace python { namespace detail { # ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template -struct is_reference_to_const +struct is_reference_to_const : mpl::false_ { - BOOST_STATIC_CONSTANT(bool, value = false); }; template -struct is_reference_to_const +struct is_reference_to_const : mpl::true_ { - BOOST_STATIC_CONSTANT(bool, value = true); }; # if defined(BOOST_MSVC) && _MSC_FULL_VER <= 13102140 // vc7.01 alpha workaround template -struct is_reference_to_const +struct is_reference_to_const : mpl::true_ { - static const bool value = true; }; # endif template -struct is_reference_to_function : mpl::bool_ +struct is_reference_to_function : mpl::false_ { }; @@ -58,9 +64,8 @@ struct is_reference_to_function : is_function }; template -struct is_pointer_to_function : mpl::bool_ +struct is_pointer_to_function : mpl::false_ { - BOOST_STATIC_CONSTANT(bool, value = false); }; // There's no such thing as a pointer-to-cv-function, so we don't need @@ -71,7 +76,7 @@ struct is_pointer_to_function : is_function }; template -struct is_reference_to_member_function_pointer_impl : mpl::bool_ +struct is_reference_to_member_function_pointer_impl : mpl::false_ { }; @@ -91,23 +96,23 @@ struct is_reference_to_member_function_pointer template struct is_reference_to_function_pointer_aux + : mpl::and_< + is_reference + , is_pointer_to_function< + typename remove_cv< + typename remove_reference::type + >::type + > + > { // There's no such thing as a pointer-to-cv-function, so we don't need specializations for those - BOOST_STATIC_CONSTANT(bool, value = ( - is_reference::value - & is_pointer_to_function< - typename remove_cv< - typename remove_reference::type - >::type - >::value)); - typedef mpl::bool_ type; }; template struct is_reference_to_function_pointer - : mpl::if_c< - is_reference_to_function::value - , mpl::bool_ + : mpl::if_< + is_reference_to_function + , mpl::false_ , is_reference_to_function_pointer_aux >::type { @@ -115,70 +120,60 @@ struct is_reference_to_function_pointer template struct is_reference_to_non_const + : mpl::and_< + is_reference + , mpl::not_< + is_reference_to_const + > + > { - BOOST_STATIC_CONSTANT( - bool, value = ( - ::boost::type_traits::ice_and< - ::boost::is_reference::value - , ::boost::type_traits::ice_not< - ::boost::python::detail::is_reference_to_const::value>::value - >::value) - ); }; template -struct is_reference_to_volatile +struct is_reference_to_volatile : mpl::false_ { - BOOST_STATIC_CONSTANT(bool, value = false); }; template -struct is_reference_to_volatile +struct is_reference_to_volatile : mpl::true_ { - BOOST_STATIC_CONSTANT(bool, value = true); }; # if defined(BOOST_MSVC) && _MSC_FULL_VER <= 13102140 // vc7.01 alpha workaround template -struct is_reference_to_volatile +struct is_reference_to_volatile : mpl::true_ { - static const bool value = true; }; # endif template -struct is_reference_to_pointer +struct is_reference_to_pointer : mpl::false_ { - BOOST_STATIC_CONSTANT(bool, value = false); }; template -struct is_reference_to_pointer +struct is_reference_to_pointer : mpl::true_ { - BOOST_STATIC_CONSTANT(bool, value = true); }; template -struct is_reference_to_pointer +struct is_reference_to_pointer : mpl::true_ { - BOOST_STATIC_CONSTANT(bool, value = true); }; template -struct is_reference_to_pointer +struct is_reference_to_pointer : mpl::true_ { - BOOST_STATIC_CONSTANT(bool, value = true); }; template -struct is_reference_to_pointer +struct is_reference_to_pointer : mpl::true_ { - BOOST_STATIC_CONSTANT(bool, value = true); }; template -struct is_reference_to_class +struct is_reference_to_classx { BOOST_STATIC_CONSTANT( bool, value @@ -196,19 +191,47 @@ struct is_reference_to_class }; template -struct is_pointer_to_class +struct is_reference_to_class + : mpl::and_< + is_reference +# if 0 && BOOST_WORKAROUND(__MWERKS__, <= 0x2407) + , mpl::not_< + is_enum< + typename remove_cv< + typename remove_reference::type + >::type + > + > +# endif + , is_class< + typename remove_cv< + typename remove_reference::type + >::type + > + > +{ +}; + +template +struct is_pointer_to_class + : mpl::and_< + is_pointer +# if 0 && BOOST_WORKAROUND(__MWERKS__, <= 0x2407) + , mpl::not_< + is_enum< + typename remove_cv< + typename remove_pointer::type + >::type + > + > +# endif + , is_class< + typename remove_cv< + typename remove_pointer::type + >::type + > + > { - BOOST_STATIC_CONSTANT( - bool, value - = (boost::type_traits::ice_and< - is_pointer::value - , is_class< - typename remove_cv< - typename remove_pointer::type - >::type - >::value - >::value) - ); }; # else @@ -220,8 +243,8 @@ typedef char (&outer_no_type)[1]; template struct is_const_help { - typedef typename mpl::if_c< - is_const::value + typedef typename mpl::if_< + is_const , inner_yes_type , inner_no_type >::type type; @@ -230,8 +253,8 @@ struct is_const_help template struct is_volatile_help { - typedef typename mpl::if_c< - is_volatile::value + typedef typename mpl::if_< + is_volatile , inner_yes_type , inner_no_type >::type type; @@ -240,8 +263,8 @@ struct is_volatile_help template struct is_pointer_help { - typedef typename mpl::if_c< - is_pointer::value + typedef typename mpl::if_< + is_pointer , inner_yes_type , inner_no_type >::type type; @@ -250,8 +273,8 @@ struct is_pointer_help template struct is_class_help { - typedef typename mpl::if_c< - is_class::value + typedef typename mpl::if_< + is_class , inner_yes_type , inner_no_type >::type type; diff --git a/include/boost/python/init.hpp b/include/boost/python/init.hpp index bfc398d6..39bf4ac7 100644 --- a/include/boost/python/init.hpp +++ b/include/boost/python/init.hpp @@ -315,7 +315,10 @@ namespace detail , mpl::push_front<> >::type args; - typedef typename ClassT::holder_selector::type selector_t; + typedef typename ClassT::holder_selector holder_selector_t; +# if !BOOST_WORKAROUND(__MWERKS__, <= 0x2407) + typedef typename holder_selector_t::type selector_t; +# endif typedef typename ClassT::held_type held_type_t; cl.def( diff --git a/include/boost/python/object/inheritance.hpp b/include/boost/python/object/inheritance.hpp index de8cae43..610f458c 100644 --- a/include/boost/python/object/inheritance.hpp +++ b/include/boost/python/object/inheritance.hpp @@ -11,6 +11,7 @@ # include # include # include +# include namespace boost { namespace python { namespace objects { @@ -108,16 +109,20 @@ struct implicit_cast_generator template struct cast_generator { - // CWPro7 will return false sometimes, but that's OK since we can - // always cast up with dynamic_cast<> + // It's OK to return false, since we can always cast up with + // dynamic_cast<> if neccessary. +# if BOOST_WORKAROUND(__MWERKS__, <= 0x2407) + BOOST_STATIC_CONSTANT(bool, is_upcast = false); +# else BOOST_STATIC_CONSTANT( bool, is_upcast = ( is_base_and_derived::value )); +# endif typedef typename mpl::if_c< is_upcast -# if defined(__MWERKS__) && __MWERKS__ <= 0x2406 +# if BOOST_WORKAROUND(__MWERKS__, <= 0x2407) // grab a few more implicit_cast cases for CodeWarrior || !is_polymorphic::value || !is_polymorphic::value diff --git a/include/boost/python/object/make_ptr_instance.hpp b/include/boost/python/object/make_ptr_instance.hpp index 0b07ae26..25dbd77c 100644 --- a/include/boost/python/object/make_ptr_instance.hpp +++ b/include/boost/python/object/make_ptr_instance.hpp @@ -48,8 +48,14 @@ struct make_ptr_instance } template - static inline PyTypeObject* get_derived_class_object(mpl::false_, U*) + static inline PyTypeObject* get_derived_class_object(mpl::false_, U* x) { +# if BOOST_WORKAROUND(__MWERKS__, <= 0x2407) + if (typeid(*x) != typeid(U)) + return get_derived_class_object(mpl::true_(), x); +# else + (void)x; +# endif return 0; } }; diff --git a/src/object/function.cpp b/src/object/function.cpp index bf6e8665..761a9866 100644 --- a/src/object/function.cpp +++ b/src/object/function.cpp @@ -39,10 +39,15 @@ function::function( unsigned keyword_offset = m_max_arity > num_keywords ? m_max_arity - num_keywords : 0; - - m_arg_names = object(handle<>(PyTuple_New(m_max_arity))); - for (unsigned j = 0; j < keyword_offset; ++j) - PyTuple_SET_ITEM(m_arg_names.ptr(), j, incref(Py_None)); + + unsigned tuple_size = num_keywords ? m_max_arity : 0; + m_arg_names = object(handle<>(PyTuple_New(tuple_size))); + + if (num_keywords != 0) + { + for (unsigned j = 0; j < keyword_offset; ++j) + PyTuple_SET_ITEM(m_arg_names.ptr(), j, incref(Py_None)); + } for (unsigned i = 0; i < num_keywords; ++i) { @@ -68,7 +73,7 @@ function::function( function::~function() { } - + PyObject* function::call(PyObject* args, PyObject* keywords) const { std::size_t nargs = PyTuple_GET_SIZE(args); @@ -76,47 +81,68 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const std::size_t total_args = nargs + nkeywords; function const* f = this; + + // Try overloads looking for a match do { // Check for a plausible number of arguments if (total_args >= f->m_min_arity && total_args <= f->m_max_arity) { + // This will be the args that actually get passed handle<> args2(allow_null(borrowed(args))); - if (nkeywords > 0) + + if (nkeywords > 0) // Keyword arguments were supplied { - if (!f->m_arg_names - || static_cast(PyTuple_Size(f->m_arg_names.ptr())) < total_args) + if (f->m_arg_names.ptr() == Py_None) // this overload doesn't accept keywords { args2 = handle<>(); // signal failure } else { - // build a new arg tuple - args2 = handle<>(PyTuple_New(total_args)); + std::size_t max_args + = static_cast(PyTuple_Size(f->m_arg_names.ptr())); - // Fill in the positional arguments - for (std::size_t i = 0; i < nargs; ++i) - PyTuple_SET_ITEM(args2.get(), i, incref(PyTuple_GET_ITEM(args, i))); - - // Grab remaining arguments by name from the keyword dictionary - for (std::size_t j = nargs; j < total_args; ++j) + // "all keywords are none" is a special case + // indicating we will accept any number of keyword + // arguments + if (max_args == 0) { - PyObject* value = PyDict_GetItem( - keywords, PyTuple_GET_ITEM(f->m_arg_names.ptr(), j)); - - if (!value) + // no argument preprocessing + } + else if (max_args < total_args) + { + args2 = handle<>(); + } + else + { + // build a new arg tuple + args2 = handle<>(PyTuple_New(total_args)); + + // Fill in the positional arguments + for (std::size_t i = 0; i < nargs; ++i) + PyTuple_SET_ITEM(args2.get(), i, incref(PyTuple_GET_ITEM(args, i))); + + // Grab remaining arguments by name from the keyword dictionary + for (std::size_t j = nargs; j < total_args; ++j) { - PyErr_Clear(); - args2 = handle<>(); - break; + PyObject* value = PyDict_GetItem( + keywords, PyTuple_GET_ITEM(f->m_arg_names.ptr(), j)); + + if (!value) + { + PyErr_Clear(); + args2 = handle<>(); + break; + } + PyTuple_SET_ITEM(args2.get(), j, incref(value)); } - PyTuple_SET_ITEM(args2.get(), j, incref(value)); } } } - // Call the function - PyObject* result = args2 ? f->m_fn(args2.get(), 0) : 0; + // Call the function. Pass keywords in case it's a + // function accepting any number of keywords + PyObject* result = args2 ? f->m_fn(args2.get(), keywords) : 0; // If the result is NULL but no error was set, m_fn failed // the argument-matching test. @@ -482,4 +508,20 @@ handle<> function_handle_impl(py_function const& f, unsigned min_arity, unsigned new function(f, min_arity, max_arity, 0, 0))); } -}}} // namespace boost::python::objects +} + +namespace detail +{ + object BOOST_PYTHON_DECL make_raw_function(objects::py_function f, std::size_t min_args) + { + static keyword k; + + return objects::function_object( + f + , min_args + , std::numeric_limits::max() + , keyword_range(&k,&k)); + } +} + +}} // namespace boost::python::objects diff --git a/test/args.cpp b/test/args.cpp index 281ca236..a65933cb 100644 --- a/test/args.cpp +++ b/test/args.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "test_class.hpp" @@ -39,12 +40,20 @@ struct X BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(X_f_overloads, X::f, 0, 3) + +tuple raw_func(tuple args, dict kw) +{ + return make_tuple(args, kw); +} + BOOST_PYTHON_MODULE(args_ext) { def("f", f, args("x", "y", "z") , "This is f's docstring" ); + def("raw", raw_function(raw_func)); + #if defined(BOOST_MSVC) && BOOST_MSVC <= 1200 // MSVC6 gives a fatal error LNK1179: invalid or corrupt file: // duplicate comdat error if we try to re-use the exact type of f @@ -57,6 +66,7 @@ BOOST_PYTHON_MODULE(args_ext) class_("Y", init(args("value"), "Y's docstring")) .def("value", &Y::value) + .def("raw", raw_function(raw_func)) ; class_("X", "This is X's docstring") diff --git a/test/args.py b/test/args.py index 665fae41..2c9218b1 100644 --- a/test/args.py +++ b/test/args.py @@ -1,6 +1,9 @@ """ >>> from args_ext import * +>>> raw(3, 4, foo = 'bar', baz = 42) +((3, 4), {'foo': 'bar', 'baz': 42}) + >>> f(x= 1, y = 3, z = 'hello') (1, 3.0, 'hello') @@ -101,6 +104,11 @@ >>> inner(n = 1, self = q).value() 1 + +>>> y = Y(value = 33) +>>> y.raw(this = 1, that = 'the other')[1] +{'this': 1, 'that': 'the other'} + """ def run(args = None): import sys diff --git a/test/back_reference.py b/test/back_reference.py index 21d5d1c0..7eac13c3 100644 --- a/test/back_reference.py +++ b/test/back_reference.py @@ -10,8 +10,7 @@ >>> z2 = copy_Z(z) >>> x_instances() 4 ->>> y_identity(y) is y -1 +>>> assert y_identity(y) is y >>> y_equality(y, y) 1 ''' diff --git a/test/enum.cpp b/test/enum.cpp index 09a916c3..1a4f178b 100644 --- a/test/enum.cpp +++ b/test/enum.cpp @@ -7,11 +7,21 @@ #include #include #include - +#if BOOST_WORKAROUND(__MWERKS__, <= 0x2407) +# include +# include +#endif using namespace boost::python; enum color { red = 1, green = 2, blue = 4 }; +#if BOOST_WORKAROUND(__MWERKS__, <= 0x2407) +namespace boost // Pro7 has a hard time detecting enums +{ + template <> struct is_enum : boost::mpl::true_ {}; +} +#endif + color identity_(color x) { return x; } struct colorized { diff --git a/test/operators.cpp b/test/operators.cpp index e4ac1e07..9096cb68 100755 --- a/test/operators.cpp +++ b/test/operators.cpp @@ -21,7 +21,13 @@ using namespace boost::python; -typedef test_class<> X; +struct X : test_class<> +{ + typedef test_class<> base_t; + + X(int x) : base_t(x) {} + X const operator+(X const& r) const { return X(value() + r.value()); } +}; X operator-(X const& l, X const& r) { return X(l.value() - r.value()); } X operator-(int l, X const& r) { return X(l - r.value()); } @@ -39,17 +45,17 @@ X abs(X x) { return X(x.value() < 0 ? -x.value() : x.value()); } X pow(X x, int y) { - return X(int(pow(double(x.value()), y))); + return X(int(pow(double(x.value()), double(y)))); } X pow(X x, X y) { - return X(int(pow(double(x.value()), y.value()))); + return X(int(pow(double(x.value()), double(y.value())))); } int pow(int x, X y) { - return int(pow(double(x), y.value())); + return int(pow(double(x), double(y.value()))); } std::ostream& operator<<(std::ostream& s, X const& x) @@ -61,6 +67,7 @@ BOOST_PYTHON_MODULE(operators_ext) { class_("X", init()) .def("value", &X::value) + .def(self + self) .def(self - self) .def(self - int()) .def(other() - self) diff --git a/test/test_builtin_converters.py b/test/test_builtin_converters.py index 80e2149d..0b3bc645 100644 --- a/test/test_builtin_converters.py +++ b/test/test_builtin_converters.py @@ -61,19 +61,15 @@ r""" ... else: print 'expected an OverflowError!' ->>> abs(rewrap_value_float(4.2) - 4.2) < .000001 -1 +>>> assert abs(rewrap_value_float(4.2) - 4.2) < .000001 >>> rewrap_value_double(4.2) - 4.2 0.0 >>> rewrap_value_long_double(4.2) - 4.2 0.0 ->>> abs(rewrap_value_complex_float(4+.2j) - (4+.2j)) < .000001 -1 ->>> abs(rewrap_value_complex_double(4+.2j) - (4+.2j)) < .000001 -1 ->>> abs(rewrap_value_complex_long_double(4+.2j) - (4+.2j)) < .000001 -1 +>>> assert abs(rewrap_value_complex_float(4+.2j) - (4+.2j)) < .000001 +>>> assert abs(rewrap_value_complex_double(4+.2j) - (4+.2j)) < .000001 +>>> assert abs(rewrap_value_complex_long_double(4+.2j) - (4+.2j)) < .000001 >>> rewrap_value_cstring('hello, world') 'hello, world' @@ -136,19 +132,15 @@ r""" 42L ->>> abs(rewrap_const_reference_float(4.2) - 4.2) < .000001 -1 +>>> assert abs(rewrap_const_reference_float(4.2) - 4.2) < .000001 >>> rewrap_const_reference_double(4.2) - 4.2 0.0 >>> rewrap_const_reference_long_double(4.2) - 4.2 0.0 ->>> abs(rewrap_const_reference_complex_float(4+.2j) - (4+.2j)) < .000001 -1 ->>> abs(rewrap_const_reference_complex_double(4+.2j) - (4+.2j)) < .000001 -1 ->>> abs(rewrap_const_reference_complex_long_double(4+.2j) - (4+.2j)) < .000001 -1 +>>> assert abs(rewrap_const_reference_complex_float(4+.2j) - (4+.2j)) < .000001 +>>> assert abs(rewrap_const_reference_complex_double(4+.2j) - (4+.2j)) < .000001 +>>> assert abs(rewrap_const_reference_complex_long_double(4+.2j) - (4+.2j)) < .000001 >>> rewrap_const_reference_cstring('hello, world') 'hello, world' @@ -221,11 +213,9 @@ Check that classic classes also work ... else: print 'expected a TypeError exception' # show that arbitrary handle instantiations can be returned ->>> get_type(1) is type(1) -1 +>>> assert get_type(1) is type(1) ->>> return_null_handle() is None -1 +>>> assert return_null_handle() is None """ def run(args = None): diff --git a/test/test_pointer_adoption.py b/test/test_pointer_adoption.py index f684b062..d811bce8 100644 --- a/test/test_pointer_adoption.py +++ b/test/test_pointer_adoption.py @@ -70,11 +70,9 @@ Test call policies for constructors here >>> num_a_instances() 0 ->>> as_A(create('dynalloc')) is None -0 +>>> assert as_A(create('dynalloc')) is not None >>> base = Base() ->>> as_A(base) is None -1 +>>> assert as_A(base) is None """ def run(args = None): import sys