diff --git a/include/boost/python/operators.hpp b/include/boost/python/operators.hpp new file mode 100644 index 00000000..df401f85 --- /dev/null +++ b/include/boost/python/operators.hpp @@ -0,0 +1,555 @@ +// (C) Copyright Ullrich Koethe and David Abrahams 2000-2001. 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. +// +// The authors gratefully acknowlege the support of Dragon Systems, Inc., in +// producing this work. +// +// Revision History: +// 23 Jan 2001 - Another stupid typo fix by Ralf W. Grosse-Kunstleve (David Abrahams) +// 20 Jan 2001 - Added a fix from Ralf W. Grosse-Kunstleve (David Abrahams) +#ifndef OPERATORS_UK112000_H_ +# define OPERATORS_UK112000_H_ +# ifdef BOOST_PYTHON_V2 + +# include + +# else + +# include +# include + +// When STLport is used with native streams, _STL::ostringstream().str() is not +// _STL::string, but std::string. This confuses to_python(), so we'll use +// strstream instead. Also, GCC 2.95.2 doesn't have sstream. +# if defined(__SGI_STL_PORT) ? defined(__SGI_STL_OWN_IOSTREAMS) : (!defined(__GNUC__) || __GNUC__ > 2) +# define BOOST_PYTHON_USE_SSTREAM +# endif + +# if defined(BOOST_PYTHON_USE_SSTREAM) +# include +# else +# include +# endif + +namespace boost { namespace python { + +BOOST_PYTHON_DECL tuple standard_coerce(ref l, ref r); + +namespace detail { + + // helper class for automatic operand type detection + // during operator wrapping. + struct auto_operand {}; +} + +// Define operator ids that can be or'ed together +// (boost::python::op_add | boost::python::op_sub | boost::python::op_mul). +// This allows to wrap several operators in one line. +enum operator_id +{ + op_add = 0x1, + op_sub = 0x2, + op_mul = 0x4, + op_div = 0x8, + op_mod = 0x10, + op_divmod =0x20, + op_pow = 0x40, + op_lshift = 0x80, + op_rshift = 0x100, + op_and = 0x200, + op_xor = 0x400, + op_or = 0x800, + op_neg = 0x1000, + op_pos = 0x2000, + op_abs = 0x4000, + op_invert = 0x8000, + op_int = 0x10000, + op_long = 0x20000, + op_float = 0x40000, + op_str = 0x80000, + op_cmp = 0x100000, + op_gt = 0x200000, + op_ge = 0x400000, + op_lt = 0x800000, + op_le = 0x1000000, + op_eq = 0x2000000, + op_ne = 0x4000000 +}; + +// Wrap the operators given by "which". Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>()); +template +struct operators {}; + +// Wrap heterogeneous operators with given left operand type. Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), +// boost::python::left_operand()); +template +struct left_operand {}; + +// Wrap heterogeneous operators with given right operand type. Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), +// boost::python::right_operand()); +template +struct right_operand {}; + +namespace detail +{ + template + struct operand_select + { + template + struct wrapped + { + typedef Specified type; + }; + }; + + template <> + struct operand_select + { + template + struct wrapped + { + typedef const wrapped_type& type; + }; + }; + + template struct define_operator; + + // Base class which grants access to extension_class_base::add_method() to its derived classes + struct add_operator_base + { + protected: + static inline void add_method(extension_class_base* target, function* method, const char* name) + { target->add_method(method, name); } + }; + +// +// choose_op, choose_unary_op, and choose_rop +// +// These templates use "poor man's partial specialization" to generate the +// appropriate add_method() call (if any) for a given operator and argument set. +// +// Usage: +// choose_op<(which & op_add)>::template args::add(ext_class); +// +// (see extension_class<>::def_operators() for more examples). +// + template + struct choose_op + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template operator_function(), + def_op::name()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_op<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + template + struct choose_unary_op + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template operator_function(), + def_op::name()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_unary_op<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + template + struct choose_rop + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template roperator_function(), + def_op::rname()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_rop<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + +// Fully specialize define_operator for all operators defined in operator_id above. +// Every specialization defines one function object for normal operator calls and one +// for operator calls with operands reversed ("__r*__" function variants). +// Specializations for most operators follow a standard pattern: execute the expression +// that uses the operator in question. This standard pattern is realized by the following +// macros so that the actual specialization can be done by just calling a macro. +# define PY_DEFINE_BINARY_OPERATORS(id, oper) \ + template <> \ + struct define_operator \ + { \ + template \ + struct operator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) oper \ + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())); \ + } \ + \ + const char* description() const \ + { return "__" #id "__"; } \ + }; \ + \ + template \ + struct roperator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) oper \ + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())); \ + } \ + \ + const char* description() const \ + { return "__r" #id "__"; } \ + \ + }; \ + \ + static const char * name() { return "__" #id "__"; } \ + static const char * rname() { return "__r" #id "__"; } \ + } + +# define PY_DEFINE_UNARY_OPERATORS(id, oper) \ + template <> \ + struct define_operator \ + { \ + template \ + struct operator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + oper(BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); \ + } \ + \ + const char* description() const \ + { return "__" #id "__"; } \ + }; \ + \ + static const char * name() { return "__" #id "__"; } \ + } + + PY_DEFINE_BINARY_OPERATORS(add, +); + PY_DEFINE_BINARY_OPERATORS(sub, -); + PY_DEFINE_BINARY_OPERATORS(mul, *); + PY_DEFINE_BINARY_OPERATORS(div, /); + PY_DEFINE_BINARY_OPERATORS(mod, %); + PY_DEFINE_BINARY_OPERATORS(lshift, <<); + PY_DEFINE_BINARY_OPERATORS(rshift, >>); + PY_DEFINE_BINARY_OPERATORS(and, &); + PY_DEFINE_BINARY_OPERATORS(xor, ^); + PY_DEFINE_BINARY_OPERATORS(or, |); + PY_DEFINE_BINARY_OPERATORS(gt, >); + PY_DEFINE_BINARY_OPERATORS(ge, >=); + PY_DEFINE_BINARY_OPERATORS(lt, <); + PY_DEFINE_BINARY_OPERATORS(le, <=); + PY_DEFINE_BINARY_OPERATORS(eq, ==); + PY_DEFINE_BINARY_OPERATORS(ne, !=); + + PY_DEFINE_UNARY_OPERATORS(neg, -); + PY_DEFINE_UNARY_OPERATORS(pos, +); + PY_DEFINE_UNARY_OPERATORS(abs, abs); + PY_DEFINE_UNARY_OPERATORS(invert, ~); + PY_DEFINE_UNARY_OPERATORS(int, long); + PY_DEFINE_UNARY_OPERATORS(long, PyLong_FromLong); + PY_DEFINE_UNARY_OPERATORS(float, double); + +# undef PY_DEFINE_BINARY_OPERATORS +# undef PY_DEFINE_UNARY_OPERATORS + +// Some operators need special treatment, e.g. because there is no corresponding +// expression in C++. These are specialized manually. + +// pow(): Manual specialization needed because an error message is required if this +// function is called with three arguments. The "power modulo" operator is not +// supported by define_operator, but can be wrapped manually (see special.html). + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + if (args.size() == 3 && args[2]->ob_type != Py_None->ob_type) + { + PyErr_SetString(PyExc_TypeError, "expected 2 arguments, got 3"); + throw_argument_error(); + } + + return BOOST_PYTHON_CONVERSION::to_python( + pow(BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()), + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + } + + const char* description() const + { return "__pow__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + if (args.size() == 3 && args[2]->ob_type != Py_None->ob_type) + { + PyErr_SetString(PyExc_TypeError, "bad operand type(s) for pow()"); + throw_argument_error(); + } + + return BOOST_PYTHON_CONVERSION::to_python( + pow(BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()), + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + } + + const char* description() const + { return "__rpow__"; } + + }; + + static const char * name() { return "__pow__"; } + static const char * rname() { return "__rpow__"; } + }; + +// divmod(): Manual specialization needed because we must actually call two operators and +// return a tuple containing both results + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + PyObject * res = PyTuple_New(2); + + PyTuple_SET_ITEM(res, 0, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) / + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + PyTuple_SET_ITEM(res, 1, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) % + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + + return res; + } + + const char* description() const + { return "__divmod__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + PyObject * res = PyTuple_New(2); + + PyTuple_SET_ITEM(res, 0, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) / + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + PyTuple_SET_ITEM(res, 1, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) % + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + + return res; + } + + const char* description() const + { return "__rdivmod__"; } + + }; + + static const char * name() { return "__divmod__"; } + static const char * rname() { return "__rdivmod__"; } + }; + +// cmp(): Manual specialization needed because there is no three-way compare in C++. +// It is implemented by two one-way comparisons with operators reversed in the second. + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + return BOOST_PYTHON_CONVERSION::to_python( + (BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())) ? + - 1 : + (BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())) ? + 1 : + 0) ; + } + + const char* description() const + { return "__cmp__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + return BOOST_PYTHON_CONVERSION::to_python( + (BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())) ? + - 1 : + (BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())) ? + 1 : + 0) ; + } + + const char* description() const + { return "__rcmp__"; } + + }; + + static const char * name() { return "__cmp__"; } + static const char * rname() { return "__rcmp__"; } + }; + +# ifndef BOOST_PYTHON_USE_SSTREAM + class unfreezer { + public: + unfreezer(std::ostrstream& s) : m_stream(s) {} + ~unfreezer() { m_stream.freeze(false); } + private: + std::ostrstream& m_stream; + }; +# endif + +// str(): Manual specialization needed because the string conversion does not follow +// the standard pattern relized by the macros. + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject*) const + { + tuple args(ref(arguments, ref::increment_count)); + +// When STLport is used with native streams, _STL::ostringstream().str() is not +// _STL::string, but std::string. +# ifdef BOOST_PYTHON_USE_SSTREAM + std::ostringstream s; + s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()); + return BOOST_PYTHON_CONVERSION::to_python(s.str()); +# else + std::ostrstream s; + s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) << char(); + auto unfreezer unfreeze(s); + return BOOST_PYTHON_CONVERSION::to_python(const_cast(s.str())); +# endif + } + + const char* description() const + { return "__str__"; } + + }; + + static const char * name() { return "__str__"; } + }; + + +} // namespace detail + +}} // namespace boost::python + +# undef BOOST_PYTHON_USE_SSTREAM +# endif +#endif /* OPERATORS_UK112000_H_ */