diff --git a/include/boost/python/class.hpp b/include/boost/python/class.hpp index 50afba85..51fef4fa 100644 --- a/include/boost/python/class.hpp +++ b/include/boost/python/class.hpp @@ -24,6 +24,7 @@ # include # include # include +# include namespace boost { namespace python { @@ -37,6 +38,9 @@ namespace detail template struct has_noncopyable; + template + struct operator_; + // Register a to_python converter for a class T, depending on the // type of the first (tag) argument. The 2nd argument is a pointer // to the type of holder that must be created. The 3rd argument is a @@ -117,6 +121,18 @@ class class_ : public objects::class_base return *this; } + + template + self& def(detail::operator_ const& op) + { + typedef detail::operator_ op_t; + // Use function::add_to_namespace to achieve overloading if + // appropriate. + objects::function::add_to_namespace( + this->object(), op.name(), + ref(detail::wrap_function(&op_t::template apply::execute))); + return *this; + } // Define the constructor with the given Args, which should be an // MPL sequence of types. diff --git a/include/boost/python/converter/builtin_converters.hpp b/include/boost/python/converter/builtin_converters.hpp index c0d2ba09..c9c3142e 100644 --- a/include/boost/python/converter/builtin_converters.hpp +++ b/include/boost/python/converter/builtin_converters.hpp @@ -8,6 +8,7 @@ # include # include # include +# include # include # include @@ -33,40 +34,37 @@ namespace converter BOOST_PYTHON_DECL PyObject* do_callback_to_python(PyObject*); } -# define BOOST_PYTHON_CALL_TO_PYTHON_BY_VALUE(T, expr) \ - template <> struct to_python_value \ - : detail::builtin_to_python \ - { \ - PyObject* operator()(T const& x) const \ - { \ - return (expr); \ - } \ - }; \ - template <> struct to_python_value \ - : detail::builtin_to_python \ - { \ - PyObject* operator()(T const& x) const \ - { \ - return (expr); \ - } \ +# define BOOST_PYTHON_CALL_TO_PYTHON_BY_VALUE(T, expr) \ + template <> struct to_python_value \ + : detail::builtin_to_python \ + { \ + PyObject* operator()(T const& x) const \ + { \ + return (expr); \ + } \ + }; \ + template <> struct to_python_value \ + : detail::builtin_to_python \ + { \ + PyObject* operator()(T const& x) const \ + { \ + return (expr); \ + } \ }; -# define BOOST_PYTHON_CALLBACK_TO_PYTHON_BY_VALUE(T, expr) \ - namespace converter \ - { \ - template <> struct callback_to_python< T > \ - { \ - callback_to_python(T const& x) \ - : m_held(expr) {} \ - PyObject* get() const \ - { return m_held.get(); } \ - private: \ - ref m_held; \ - }; \ +# define BOOST_PYTHON_CALLBACK_TO_PYTHON_BY_VALUE(T, expr) \ + namespace converter \ + { \ + template <> struct callback_to_python< T > \ + : detail::callback_to_python_holder \ + { \ + callback_to_python(T const& x) \ + : detail::callback_to_python_holder(expr) {} \ + }; \ } -# define BOOST_PYTHON_TO_PYTHON_BY_VALUE(T, expr) \ - BOOST_PYTHON_CALL_TO_PYTHON_BY_VALUE(T,expr) \ +# define BOOST_PYTHON_TO_PYTHON_BY_VALUE(T, expr) \ + BOOST_PYTHON_CALL_TO_PYTHON_BY_VALUE(T,expr) \ BOOST_PYTHON_CALLBACK_TO_PYTHON_BY_VALUE(T,expr) # define BOOST_PYTHON_TO_INT(T) \ diff --git a/include/boost/python/converter/callback_to_python_base.hpp b/include/boost/python/converter/callback_to_python_base.hpp index c878850e..d03f2f60 100644 --- a/include/boost/python/converter/callback_to_python_base.hpp +++ b/include/boost/python/converter/callback_to_python_base.hpp @@ -16,7 +16,8 @@ namespace detail struct callback_to_python_holder { callback_to_python_holder(PyObject* obj); - inline PyObject* get() const; + PyObject* get() const; + PyObject* get_incref() const; private: ref m_held; }; @@ -38,6 +39,13 @@ namespace detail { return m_held.get(); } + + inline PyObject* callback_to_python_holder::get_incref() const + { + PyObject* result = m_held.get(); + Py_XINCREF(result); + return result; + } } }}} // namespace boost::python::converter diff --git a/include/boost/python/detail/msvc_typeinfo.hpp b/include/boost/python/detail/msvc_typeinfo.hpp index 3da411f0..21e47389 100644 --- a/include/boost/python/detail/msvc_typeinfo.hpp +++ b/include/boost/python/detail/msvc_typeinfo.hpp @@ -57,8 +57,8 @@ template struct bool_t{}; template inline typeinfo typeid_nonref(boost::type* = 0) { - BOOST_STATIC_CONSTANT(bool, c = is_const::value); - BOOST_STATIC_CONSTANT(bool, v = is_volatile::value); + bool const c = is_const::value; + bool const v = is_volatile::value; return value_id_accessor<(2 * v + c)>::get((T*)0); } diff --git a/include/boost/python/detail/operator_id.hpp b/include/boost/python/detail/operator_id.hpp new file mode 100755 index 00000000..9005c7ae --- /dev/null +++ b/include/boost/python/detail/operator_id.hpp @@ -0,0 +1,54 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef OPERATOR_ID_DWA2002531_HPP +# define OPERATOR_ID_DWA2002531_HPP + +namespace boost { namespace python { namespace detail { + +enum operator_id +{ + op_add, + op_sub, + op_mul, + op_div, + op_mod, + op_divmod, + op_pow, + op_lshift, + op_rshift, + op_and, + op_xor, + op_or, + op_neg, + op_pos, + op_abs, + op_invert, + op_int, + op_long, + op_float, + op_str, + op_cmp, + op_gt, + op_ge, + op_lt, + op_le, + op_eq, + op_ne, + op_iadd, + op_isub, + op_imul, + op_idiv, + op_imod, + op_ilshift, + op_irshift, + op_iand, + op_ixor, + op_ior +}; + +}}} // namespace boost::python::detail + +#endif // OPERATOR_ID_DWA2002531_HPP diff --git a/include/boost/python/operators2.hpp b/include/boost/python/operators2.hpp new file mode 100755 index 00000000..c4365b35 --- /dev/null +++ b/include/boost/python/operators2.hpp @@ -0,0 +1,284 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef OPERATORS2_DWA2002530_HPP +# define OPERATORS2_DWA2002530_HPP + +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +namespace detail +{ + // This is essentially the old v1 to_python(). It will be eliminated + // once the public interface for to_python is settled on. + template + PyObject* convert_result(T const& x) + { + return converter::callback_to_python(x).get_incref(); + } + + // Operator implementation template declarations. The nested apply + // declaration here keeps MSVC6 happy. + template struct operator_l + { + template struct apply; + }; + + template struct operator_r + { + template struct apply; + }; + + template struct operator_1 + { + template struct apply; + }; + + // MSVC6 doesn't want us to do this sort of inheritance on a nested + // class template, so we use this layer of indirection to avoid + // ::template<...> on the nested apply functions below + template + struct operator_l_inner + : operator_l::template apply + {}; + + template + struct operator_r_inner + : operator_r::template apply + {}; + + template + struct operator_1_inner + : operator_1::template apply + {}; + + // Define three different binary_op templates which take care of + // these cases: + // self op self + // self op R + // L op self + // + // The inner apply metafunction is used to adjust the operator to + // the class type being defined. Inheritance of the outer class is + // simply used to provide convenient access to the operation's + // name(). + + // self op self + template + struct binary_op : operator_l + { + template + struct apply : operator_l_inner + { + }; + }; + + // self op R + template + struct binary_op_l : operator_l + { + template + struct apply : operator_l_inner + { + }; + }; + + // L op self + template + struct binary_op_r : operator_r + { + template + struct apply : operator_r_inner + { + }; + }; + + template + struct unary_op : operator_1 + { + template + struct apply : operator_1_inner + { + }; + }; + + // This type is what actually gets returned from operators used on + // self_t + template + struct operator_ + : mpl::select_type< + (is_same::value) + , typename mpl::select_type< + (is_same::value) + , binary_op + , binary_op_l::type> + >::type + , typename mpl::select_type< + (is_same::value) + , unary_op + , binary_op_r::type> + >::type + >::type + { + }; +} + +# define BOOST_PYTHON_BINARY_OPERATOR(id, rid, op) \ +namespace detail \ +{ \ + template <> \ + struct operator_l \ + { \ + template \ + struct apply \ + { \ + static inline PyObject* execute(L const& l, R const& r) \ + { \ + return detail::convert_result(l op r); \ + } \ + }; \ + static char const* name() { return "__" #id "__"; } \ + }; \ + \ + template <> \ + struct operator_r \ + { \ + template \ + struct apply \ + { \ + static inline PyObject* execute(R const& r, L const& l) \ + { \ + return detail::convert_result(l op r); \ + } \ + }; \ + static char const* name() { return "__" #rid "__"; } \ + }; \ +} \ +namespace self_ns \ +{ \ + template \ + inline detail::operator_ \ + operator##op(L const&, R const&) \ + { \ + return detail::operator_(); \ + } \ +} + +BOOST_PYTHON_BINARY_OPERATOR(add, radd, +) +BOOST_PYTHON_BINARY_OPERATOR(sub, rsub, -) +BOOST_PYTHON_BINARY_OPERATOR(mul, rmul, *) +BOOST_PYTHON_BINARY_OPERATOR(div, rdiv, /) +BOOST_PYTHON_BINARY_OPERATOR(mod, rmod, %) +BOOST_PYTHON_BINARY_OPERATOR(lshift, rlshift, <<) +BOOST_PYTHON_BINARY_OPERATOR(rshift, rrshift, >>) +BOOST_PYTHON_BINARY_OPERATOR(and, rand, &) +BOOST_PYTHON_BINARY_OPERATOR(xor, rxor, ^) +BOOST_PYTHON_BINARY_OPERATOR(or, ror, |) +BOOST_PYTHON_BINARY_OPERATOR(gt, lt, >) +BOOST_PYTHON_BINARY_OPERATOR(ge, le, >=) +BOOST_PYTHON_BINARY_OPERATOR(lt, gt, <) +BOOST_PYTHON_BINARY_OPERATOR(le, ge, <=) +BOOST_PYTHON_BINARY_OPERATOR(eq, eq, ==) +BOOST_PYTHON_BINARY_OPERATOR(ne, ne, !=) + +# define BOOST_PYTHON_INPLACE_OPERATOR(id, op) \ +namespace detail \ +{ \ + template <> \ + struct operator_l \ + { \ + template \ + struct apply \ + { \ + static inline PyObject* \ + execute(back_reference l, R const& r) \ + { \ + l.get() op r; \ + return l.reference().release(); \ + } \ + }; \ + static char const* name() { return "__" #id "__"; } \ + }; \ +} \ +namespace self_ns \ +{ \ + template \ + inline detail::operator_ \ + operator##op(self_t const&, R const&) \ + { \ + return detail::operator_(); \ + } \ +} + +BOOST_PYTHON_INPLACE_OPERATOR(iadd,+=) +BOOST_PYTHON_INPLACE_OPERATOR(isub,-=) +BOOST_PYTHON_INPLACE_OPERATOR(imul,*=) +BOOST_PYTHON_INPLACE_OPERATOR(idiv,/=) +BOOST_PYTHON_INPLACE_OPERATOR(imod,%=) +BOOST_PYTHON_INPLACE_OPERATOR(ilshift,<<=) +BOOST_PYTHON_INPLACE_OPERATOR(irshift,>>=) +BOOST_PYTHON_INPLACE_OPERATOR(iand,&=) +BOOST_PYTHON_INPLACE_OPERATOR(ixor,^=) +BOOST_PYTHON_INPLACE_OPERATOR(ior,|=) + +# define BOOST_PYTHON_UNARY_OPERATOR(id, op, func_name) \ +namespace detail \ +{ \ + template <> \ + struct operator_1 \ + { \ + template \ + struct apply \ + { \ + static PyObject* execute(T const& x) \ + { \ + return detail::convert_result(op(x)); \ + } \ + }; \ + static char const* name() { return "__" #id "__"; } \ + }; \ +} \ +namespace self_ns \ +{ \ + inline detail::operator_ \ + func_name(self_t const&) \ + { \ + return detail::operator_(); \ + } \ +} + +BOOST_PYTHON_UNARY_OPERATOR(neg, -, operator-) +BOOST_PYTHON_UNARY_OPERATOR(pos, +, operator+) +BOOST_PYTHON_UNARY_OPERATOR(abs, abs, abs) +BOOST_PYTHON_UNARY_OPERATOR(invert, ~, operator~) +BOOST_PYTHON_UNARY_OPERATOR(int, long, int_) +BOOST_PYTHON_UNARY_OPERATOR(long, PyLong_FromLong, long_) +BOOST_PYTHON_UNARY_OPERATOR(float, double, float_) + +}} // namespace boost::python + +# ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +using boost::python::self_ns::abs; +using boost::python::self_ns::int_; +using boost::python::self_ns::long_; +using boost::python::self_ns::float_; +# endif + +#endif // OPERATORS2_DWA2002530_HPP + + + + + + + + diff --git a/include/boost/python/other.hpp b/include/boost/python/other.hpp new file mode 100755 index 00000000..0142da34 --- /dev/null +++ b/include/boost/python/other.hpp @@ -0,0 +1,113 @@ +#ifndef OTHER_DWA20020601_HPP +# define OTHER_DWA20020601_HPP +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +# if _MSC_VER+0 >= 1020 +# pragma once +# endif + +# include + +namespace boost { namespace python { + +template struct other +{ + typedef T type; +}; + +# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +namespace detail +{ + template + class is_other + { + public: + BOOST_STATIC_CONSTANT(bool, value = false); + }; + + template + class is_other > + { + public: + BOOST_STATIC_CONSTANT(bool, value = true); + }; + + template + class unwrap_other + { + public: + typedef T type; + }; + + template + class unwrap_other > + { + public: + typedef T type; + }; +} +# else // no partial specialization + +}} // namespace boost::python + +#include + +namespace boost { namespace python { + +namespace detail +{ + typedef char (&yes_other_t)[1]; + typedef char (&no_other_t)[2]; + + no_other_t is_other_test(...); + + template + yes_other_t is_other_test(type< other >); + + template + struct other_unwrapper + { + template + struct apply + { + typedef T type; + }; + }; + + template<> + struct other_unwrapper + { + template + struct apply + { + typedef typename T::type type; + }; + }; + + template + class is_other + { + public: + BOOST_STATIC_CONSTANT( + bool, value = ( + sizeof(detail::is_other_test(type())) + == sizeof(detail::yes_other_t))); + }; + + template + class unwrap_other + : public detail::other_unwrapper< + is_other::value + >::template apply + {}; +} + +# endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +}} // namespace boost::python + +#endif // #ifndef OTHER_DWA20020601_HPP diff --git a/include/boost/python/self.hpp b/include/boost/python/self.hpp new file mode 100755 index 00000000..c7d3f670 --- /dev/null +++ b/include/boost/python/self.hpp @@ -0,0 +1,36 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef SELF_DWA2002531_HPP +# define SELF_DWA2002531_HPP + +# include + +namespace boost { namespace python { + +//# if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 +# define BOOST_PYTHON_SELF_IS_CLASS +//# endif + +// Sink self_t into its own namespace so that we have a safe place to +// put the completely general operator templates which operate on +// it. It is possible to avoid this, but it turns out to be much more +// complicated and finally GCC 2.95.2 chokes on it. +namespace self_ns +{ +# ifndef BOOST_PYTHON_SELF_IS_CLASS + enum self_t { self }; +# else + struct self_t {}; + extern BOOST_PYTHON_DECL self_t self; +# endif +} + +using self_ns::self_t; +using self_ns::self; + +}} // namespace boost::python + +#endif // SELF_DWA2002531_HPP diff --git a/src/object/class.cpp b/src/object/class.cpp index 85fcb7d7..ea0c332b 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -8,12 +8,20 @@ #include #include #include +#include #include #include #include namespace boost { namespace python { +# ifdef BOOST_PYTHON_SELF_IS_CLASS +namespace self_ns +{ + self_t self; +} +# endif + instance_holder::instance_holder() : m_next(0) { diff --git a/src/object/function.cpp b/src/object/function.cpp index a05d7e92..f4913fd8 100644 --- a/src/object/function.cpp +++ b/src/object/function.cpp @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include +#include namespace boost { namespace python { namespace objects { @@ -77,6 +81,67 @@ void function::add_overload(function* overload_) parent->m_overloads = overload_; } +namespace +{ + char const* const binary_operator_names[] = + { + "add__", + "and__", + "div__", + "eq__", + "ge__", + "gt__", + "le__", + "lshift__", + "lt__", + "mod__", + "mul__", + "ne__", + "or__", + "radd__", + "rand__", + "rdiv__", + "rlshift__", + "rmod__", + "rmul__", + "ror__", + "rrshift__", + "rshift__", + "rsub__", + "rxor__", + "sub__", + "xor__", + }; + + inline bool is_binary_operator(char const* name) + { + return name[0] == '_' + && name[1] == '_' + && std::binary_search( + &binary_operator_names[0] + , binary_operator_names + sizeof(binary_operator_names)/sizeof(*binary_operator_names) + , name + 2 + , bind(std::less(), + bind(BOOST_CSTD_::strcmp, _1, _2), 0) + ); + } + + // Something for the end of the chain of binary operators + PyObject* not_implemented_impl(PyObject*, PyObject*) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + function* not_implemented_function() + { + static function* result = new function(py_function(¬_implemented_impl), 2, 3); + static ref keeper(result); + + return result; + } +} + void function::add_to_namespace( ref const& name_space, char const* name_, ref const& attribute) { @@ -99,11 +164,19 @@ void function::add_to_namespace( ref existing(PyObject_GetItem(dict, name.get()), ref::null_ok); - if (existing.get() && existing->ob_type == &function_type) + if (existing.get()) { - static_cast(existing.get())->add_overload( - static_cast(attribute.get())); - return; + if (existing->ob_type == &function_type) + { + static_cast(attribute.get())->add_overload( + static_cast(existing.get())); + } + } + // Binary operators need an additional overload which returns NotImplemented + else if (is_binary_operator(name_)) + { + static_cast(attribute.get())->add_overload( + not_implemented_function()); } } diff --git a/test/Jamfile b/test/Jamfile index 80c7a916..794dff42 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -56,6 +56,7 @@ bpl-test pearu1 : test_cltree.py cltree.cpp ; bpl-test try : newtest.py m1.cpp m2.cpp ; bpl-test builtin_converters : test_builtin_converters.py test_builtin_converters.cpp ; bpl-test test_pointer_adoption ; +bpl-test operators ; bpl-test callbacks ; bpl-test virtual_functions ; bpl-test back_reference ; diff --git a/test/operators.cpp b/test/operators.cpp new file mode 100755 index 00000000..3a33990f --- /dev/null +++ b/test/operators.cpp @@ -0,0 +1,56 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#include +#include +#include +#include +#include "test_class.hpp" + +using namespace boost::python; + +typedef test_class<> X; + +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()); } +X operator-(X const& l, int r) { return X(l.value() - r); } + +X operator-(X const& x) { return X(-x.value()); } + +X& operator-=(X& l, X const& r) { l.set(l.value() - r.value()); return l; } + +bool operator<(X const& x, X const& y) { return x.value() < y.value(); } +bool operator<(X const& x, int y) { return x.value() < y; } +bool operator<(int x, X const& y) { return x < y.value(); } + +X abs(X x) { return X(x.value() < 0 ? -x.value() : x.value()); } + +BOOST_PYTHON_MODULE_INIT(operators_ext) +{ + module("operators_ext") + .add( + class_("X") + .def_init(args()) + .def("value", &X::value) + .def(self - self) + .def(self - int()) + .def(other() - self) + .def(-self) + .def(self < other()) + .def(self < self) + .def(1 < self) + .def(self -= self) + .def(abs(self)) + ) + .add( + class_ >("Z") + .def_init(args()) + .def(int_(self)) + .def(float_(self)) + ) + ; +} + +#include "module_tail.cpp" diff --git a/test/operators.py b/test/operators.py new file mode 100644 index 00000000..951ac806 --- /dev/null +++ b/test/operators.py @@ -0,0 +1,75 @@ +''' +>>> from operators_ext import * +>>> x = X(42) +>>> x.value() +42 +>>> y = x - X(5) +>>> y.value() +37 +>>> y = x - 4 +>>> y.value() +38 +>>> y = 3 - x +>>> y.value() +-39 +>>> (-y).value() +39 + +>>> abs(y).value() +39 + +>>> x < 10 +0 +>>> x < 43 +1 + +>>> 10 < x +1 +>>> 43 < x +0 + +>>> x < y +0 +>>> y < x +1 + + ------ +>>> x > 10 +1 +>>> x > 43 +0 + +>>> 10 > x +0 +>>> 43 > x +1 + +>>> x > y +1 +>>> y > x +0 + +>>> y = x - 5 +>>> x -= y +>>> x.value() +5 + +>>> z = Z(10) +>>> int(z) +10 +>>> float(z) +10.0 +''' + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + sys.exit(run()[0])