changeset 374:d362cdd1ddeb

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.
author Mike Miller <mtmiller@octave.org>
date Fri, 26 Aug 2016 18:51:42 -0700
parents 0e4097c66788
children d0a7f66393fc
files @pyobject/pyobject.m __py_struct_from_dict__.cc oct-py-types.cc oct-py-types.h
diffstat 4 files changed, 154 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- 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));
--- 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\
--- 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<unsigned PY_LONG_LONG> (-1));
+#else
+      unsigned long value = PyLong_AsUnsignedLong (obj);
+      bool overflow = (value == static_cast<unsigned long> (-1));
+#endif
+      if (overflow)
+        {
+          value = std::numeric_limits<uint64_t>::max ();
+          PyErr_Clear ();
+        }
+
+      return static_cast<uint64_t> (value);
+    }
+#if PY_VERSION_HEX < 0x03000000
+  else if (PyInt_Check (obj))
+    return static_cast<uint64_t> (PyInt_AsLong (obj));
+#endif
+  else
+    throw object_convert_exception ("failed to extract integer: wrong type");
+
+  return 0;
+}
+
 PyObject *
 make_py_tuple (const Cell& cell)
 {
--- 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