# HG changeset patch # User Mike Miller # Date 1472262702 25200 # Node ID d362cdd1ddeb589b64dfe98059245c035900b966 # Parent 0e4097c6678877c22ff1611ec83fd7e9bcb75199 pyobject: add conversion methods for single, intX, and uintX types * oct-py-types.cc, oct-py-types.h (pytave::extract_py_uint64): New function. * __py_struct_from_dict__.cc (F__py_uint64_scalar_value__): New function. (F__py_int64_scalar_value__): Simplify and clean up style. Add %!tests. * @pyobject/pyobject.m (pyobject.single, pyobject.int8, pyobject.int16, pyobject.int32, pyobject.uint8, pyobject.uint16, pyobject.uint32, pyobject.uint64): New conversion methods. diff -r 0e4097c66788 -r d362cdd1ddeb @pyobject/pyobject.m --- a/@pyobject/pyobject.m Fri Aug 26 14:05:37 2016 -0700 +++ b/@pyobject/pyobject.m Fri Aug 26 18:51:42 2016 -0700 @@ -109,10 +109,42 @@ endif endfunction + function y = single (x) + y = single (double (x)); + endfunction + + function y = int8 (x) + y = int8 (__py_int64_scalar_value__ (x)); + endfunction + + function y = int16 (x) + y = int16 (__py_int64_scalar_value__ (x)); + endfunction + + function y = int32 (x) + y = int32 (__py_int64_scalar_value__ (x)); + endfunction + function y = int64 (x) y = __py_int64_scalar_value__ (x); endfunction + function y = uint8 (x) + y = uint8 (__py_uint64_scalar_value__ (x)); + endfunction + + function y = uint16 (x) + y = uint16 (__py_uint64_scalar_value__ (x)); + endfunction + + function y = uint32 (x) + y = uint32 (__py_uint64_scalar_value__ (x)); + endfunction + + function y = uint64 (x) + y = __py_uint64_scalar_value__ (x); + endfunction + function y = isa (x, typestr) assert (nargin == 2); assert (ischar (typestr) || iscellstr (typestr)); diff -r 0e4097c66788 -r d362cdd1ddeb __py_struct_from_dict__.cc --- a/__py_struct_from_dict__.cc Fri Aug 26 14:05:37 2016 -0700 +++ b/__py_struct_from_dict__.cc Fri Aug 26 18:51:42 2016 -0700 @@ -71,48 +71,109 @@ DEFUN_DLD (__py_int64_scalar_value__, args, nargout, "-*- texinfo -*-\n\ -@deftypefn {} {} __py_int64_scalar_value__ (@var{x})\n\ +@deftypefn {} {} __py_int64_scalar_value__ (@var{x})\n\ Extract a scalar int64 value from the Python integer @var{x}.\n\ \n\ This is a private internal function not intended for direct use.\n\ @end deftypefn") { - octave_value_list retval; - - int nargin = args.length (); - if (nargin != 1) - { - print_usage (); - return retval; - } + if (args.length () != 1) + print_usage (); if (! (args(0).is_object () && args(0).class_name () == "pyobject")) error ("pyobject.int64: argument must be a Python object"); Py_Initialize (); + PyObject *obj = pytave::pyobject_unwrap_object (args(0)); + if (! obj) + error ("pyobject.int64: argument must be a valid Python object"); + + octave_int64 retval; + try { - // FIXME: PyObject *obj = look up stored pyobject reference (args(0)); - boost::python::object arg; - pytave::octvalue_to_pyobj (arg, args(0)); - PyObject *obj = arg.ptr (); - - retval(0) = octave_int64 (pytave::extract_py_int64 (obj)); + retval = pytave::extract_py_int64 (obj); } catch (pytave::object_convert_exception const &) { - error ("pyobject.int64: error in return value type conversion"); + error ("pyobject.int64: argument must be a Python int or long object"); } catch (boost::python::error_already_set const &) { std::string message = pytave::fetch_exception_message (); error ("pyobject.int64: %s", message.c_str ()); } + Py_DECREF (obj); - return retval; + return ovl (retval); } +/* +%!assert (__py_int64_scalar_value__ (pyobject (pyeval ("0"))), int64 (0)) +%!assert (__py_int64_scalar_value__ (pyobject (pyeval ("2**62"))), int64 (2^62)) +%!assert (__py_int64_scalar_value__ (pyobject (pyeval ("-2**62"))), int64 (-2^62)) +%!assert (__py_int64_scalar_value__ (pyobject (pyeval ("2**128"))), intmax ("int64")) +%!assert (__py_int64_scalar_value__ (pyobject (pyeval ("-2**128"))), intmin ("int64")) + +%!error __py_int64_scalar_value__ () +%!error __py_int64_scalar_value__ (1) +%!error __py_int64_scalar_value__ (pyeval ("None")) +%!error __py_int64_scalar_value__ (1, 2) +*/ + +DEFUN_DLD (__py_uint64_scalar_value__, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {} {} __py_uint64_scalar_value__ (@var{x})\n\ +Extract a scalar uint64 value from the Python integer @var{x}.\n\ +\n\ +This is a private internal function not intended for direct use.\n\ +@end deftypefn") +{ + if (args.length () != 1) + print_usage (); + + if (! (args(0).is_object () && args(0).class_name () == "pyobject")) + error ("pyobject.uint64: argument must be a Python object"); + + Py_Initialize (); + + PyObject *obj = pytave::pyobject_unwrap_object (args(0)); + if (! obj) + error ("pyobject.uint64: argument must be a valid Python object"); + + octave_uint64 retval; + + try + { + retval = pytave::extract_py_uint64 (obj); + } + catch (pytave::object_convert_exception const &) + { + error ("pyobject.uint64: argument must be a Python int or long object"); + } + catch (boost::python::error_already_set const &) + { + std::string message = pytave::fetch_exception_message (); + error ("pyobject.uint64: %s", message.c_str ()); + } + Py_DECREF (obj); + + return ovl (retval); +} + +/* +%!assert (__py_uint64_scalar_value__ (pyobject (pyeval ("0"))), uint64 (0)) +%!assert (__py_uint64_scalar_value__ (pyobject (pyeval ("2**62"))), uint64 (2^62)) +%!assert (__py_uint64_scalar_value__ (pyobject (pyeval ("2**128"))), intmax ("uint64")) +%!assert (__py_uint64_scalar_value__ (pyobject (pyeval ("-2**128"))), intmin ("uint64")) + +%!error __py_uint64_scalar_value__ () +%!error __py_uint64_scalar_value__ (1) +%!error __py_uint64_scalar_value__ (pyeval ("None")) +%!error __py_uint64_scalar_value__ (1, 2) +*/ + DEFUN_DLD (__py_is_none__, args, nargout, "-*- texinfo -*-\n\ @deftypefn {} {} __py_is_none__ (@var{x})\n\ diff -r 0e4097c66788 -r d362cdd1ddeb oct-py-types.cc --- a/oct-py-types.cc Fri Aug 26 14:05:37 2016 -0700 +++ b/oct-py-types.cc Fri Aug 26 18:51:42 2016 -0700 @@ -411,6 +411,43 @@ return 0; } +uint64_t +extract_py_uint64 (PyObject *obj) +{ + if (! obj) + throw object_convert_exception ("failed to extract integer: null object"); + + if (PyLong_Check (obj)) + { + // FIXME: if (value < 0), may be very implementation dependent + if (Py_SIZE (obj) < 0) + return 0; + +#if (defined (HAVE_LONG_LONG) && (SIZEOF_LONG_LONG == 8)) + unsigned PY_LONG_LONG value = PyLong_AsUnsignedLongLong (obj); + bool overflow = (value == static_cast (-1)); +#else + unsigned long value = PyLong_AsUnsignedLong (obj); + bool overflow = (value == static_cast (-1)); +#endif + if (overflow) + { + value = std::numeric_limits::max (); + PyErr_Clear (); + } + + return static_cast (value); + } +#if PY_VERSION_HEX < 0x03000000 + else if (PyInt_Check (obj)) + return static_cast (PyInt_AsLong (obj)); +#endif + else + throw object_convert_exception ("failed to extract integer: wrong type"); + + return 0; +} + PyObject * make_py_tuple (const Cell& cell) { diff -r 0e4097c66788 -r d362cdd1ddeb oct-py-types.h --- a/oct-py-types.h Fri Aug 26 14:05:37 2016 -0700 +++ b/oct-py-types.h Fri Aug 26 18:51:42 2016 -0700 @@ -103,6 +103,13 @@ int64_t extract_py_int64 (PyObject *obj); +//! Extract the integer value of the given Python int or long object. +//! +//! @param obj Python int or long object +//! @return integer value of @a obj +uint64_t +extract_py_uint64 (PyObject *obj); + //! Create a Python int object with the value of the given @c int32_t value. //! //! @param value integer value