Permalink
Browse files

Overhauled how builtin dtype objects are accessed for better consiste…

…ncy, added converters for array scalars.
  • Loading branch information...
1 parent ab2225b commit 28a9fab2782b29efe9efc12bd239147d854f6c55 @TallJimbo TallJimbo committed May 13, 2012
View
@@ -23,9 +23,10 @@ namespace numpy {
* It should probably be the first line inside BOOST_PYTHON_MODULE.
*
* @internal This just calls the Numpy C-API functions "import_array()"
- * and "import_ufunc()".
+ * and "import_ufunc()", and then calls
+ * dtype::register_scalar_converters().
*/
-void initialize();
+void initialize(bool register_scalar_converters=true);
} // namespace boost::numpy
} // namespace boost
View
@@ -12,18 +12,14 @@
#include <boost/mpl/for_each.hpp>
#include <boost/type_traits/add_pointer.hpp>
-namespace boost
-{
-namespace numpy
-{
+namespace boost { namespace numpy {
/**
* @brief A boost.python "object manager" (subclass of object) for numpy.dtype.
*
* @todo This could have a lot more interesting accessors.
*/
-class dtype : public python::object
-{
+class dtype : public python::object {
static python::detail::new_reference convert(python::object::object_cref arg, bool align);
public:
@@ -47,19 +43,59 @@ class dtype : public python::object
/// @brief Return the size of the data type in bytes.
int get_itemsize() const;
+ /**
+ * @brief Register from-Python converters for NumPy's built-in array scalar types.
+ *
+ * This is usually called automatically by initialize(), and shouldn't be called twice
+ * (doing so just adds unused converters to the Boost.Python registry).
+ */
+ static void register_scalar_converters();
+
BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dtype, python::object);
};
-} // namespace boost::numpy
-
-namespace python
-{
-namespace converter
+namespace detail
{
+
+template <int bits, bool isUnsigned> dtype get_int_dtype();
+
+template <int bits> dtype get_float_dtype();
+
+template <int bits> dtype get_complex_dtype();
+
+template <typename T, bool isInt=boost::is_integral<T>::value>
+struct builtin_dtype;
+
+template <typename T>
+struct builtin_dtype<T,true> {
+ static dtype get() { return get_int_dtype< 8*sizeof(T), boost::is_unsigned<T>::value >(); }
+};
+
+template <>
+struct builtin_dtype<bool,true> {
+ static dtype get();
+};
+
+template <typename T>
+struct builtin_dtype<T,false> {
+ static dtype get() { return get_float_dtype< 8*sizeof(T) >(); }
+};
+
+template <typename T>
+struct builtin_dtype< std::complex<T>, false > {
+ static dtype get() { return get_complex_dtype< 16*sizeof(T) >(); }
+};
+
+} // namespace detail
+
+template <typename T>
+inline dtype dtype::get_builtin() { return detail::builtin_dtype<T>::get(); }
+
+}} // namespace boost::numpy
+
+namespace boost { namespace python { namespace converter {
NUMPY_OBJECT_MANAGER_TRAITS(numpy::dtype);
-} // namespace boost::python::converter
-} // namespace boost::python
-} // namespace boost
+}}} // namespace boost::python::converter
#endif // !BOOST_NUMPY_DTYPE_HPP_INCLUDED
View
@@ -1,58 +1,78 @@
#define BOOST_NUMPY_INTERNAL
#include <boost/numpy/internal.hpp>
-#define NUMPY_DTYPE_TRAITS_BUILTIN(ctype,code) \
-template <> struct dtype_traits<ctype> \
-{ \
- static dtype get() \
- { \
- return dtype(python::detail::new_reference \
- (reinterpret_cast<PyObject*>(PyArray_DescrFromType(code)))); \
- } \
-}; \
-template dtype dtype::get_builtin<ctype>()
-
-#define NUMPY_DTYPE_TRAITS_COMPLEX(creal, ctype, code) \
-template <> struct dtype_traits< std::complex<creal> > \
-{ \
- static dtype get() \
- { \
- if (sizeof(ctype) != sizeof(std::complex<creal>)) \
- { \
- PyErr_SetString(PyExc_TypeError, "Cannot reinterpret std::complex<T> as T[2]"); \
- python::throw_error_already_set(); \
- } \
- return dtype(python::detail::new_reference \
- (reinterpret_cast<PyObject*>(PyArray_DescrFromType(code)))); \
- } \
-}; \
-template dtype dtype::get_builtin< std::complex<creal> >()
-
-namespace boost
-{
-namespace python
-{
-namespace converter
-{
+#define DTYPE_FROM_CODE(code) \
+ dtype(python::detail::new_reference(reinterpret_cast<PyObject*>(PyArray_DescrFromType(code))))
+
+#define BUILTIN_INT_DTYPE(bits) \
+ template <> struct builtin_int_dtype< bits, false > { \
+ static dtype get() { return DTYPE_FROM_CODE(NPY_INT ## bits); } \
+ }; \
+ template <> struct builtin_int_dtype< bits, true > { \
+ static dtype get() { return DTYPE_FROM_CODE(NPY_UINT ## bits); } \
+ }; \
+ template dtype get_int_dtype< bits, false >(); \
+ template dtype get_int_dtype< bits, true >()
+
+#define BUILTIN_FLOAT_DTYPE(bits) \
+ template <> struct builtin_float_dtype< bits > { \
+ static dtype get() { return DTYPE_FROM_CODE(NPY_FLOAT ## bits); } \
+ }; \
+ template dtype get_float_dtype< bits >()
+
+#define BUILTIN_COMPLEX_DTYPE(bits) \
+ template <> struct builtin_complex_dtype< bits > { \
+ static dtype get() { return DTYPE_FROM_CODE(NPY_COMPLEX ## bits); } \
+ }; \
+ template dtype get_complex_dtype< bits >()
+
+namespace boost { namespace python { namespace converter {
NUMPY_OBJECT_MANAGER_TRAITS_IMPL(PyArrayDescr_Type, numpy::dtype)
-} // namespace boost::python::converter
-} // namespace boost::python
+}}} // namespace boost::python::converter
+
+namespace boost { namespace numpy {
+
+namespace detail {
-namespace numpy
-{
+dtype builtin_dtype<bool,true>::get() { return DTYPE_FROM_CODE(NPY_BOOL); }
-template <typename T> struct dtype_traits;
+template <int bits, bool isUnsigned> struct builtin_int_dtype;
+template <int bits> struct builtin_float_dtype;
+template <int bits> struct builtin_complex_dtype;
-python::detail::new_reference dtype::convert(python::object const & arg, bool align)
-{
+template <int bits, bool isUnsigned> dtype get_int_dtype() {
+ return builtin_int_dtype<bits,isUnsigned>::get();
+}
+template <int bits> dtype get_float_dtype() { return builtin_float_dtype<bits>::get(); }
+template <int bits> dtype get_complex_dtype() { return builtin_complex_dtype<bits>::get(); }
+
+BUILTIN_INT_DTYPE(8);
+BUILTIN_INT_DTYPE(16);
+BUILTIN_INT_DTYPE(32);
+BUILTIN_INT_DTYPE(64);
+BUILTIN_FLOAT_DTYPE(32);
+BUILTIN_FLOAT_DTYPE(64);
+BUILTIN_COMPLEX_DTYPE(64);
+BUILTIN_COMPLEX_DTYPE(128);
+#if NPY_BITSOF_LONGDOUBLE > NPY_BITSOF_DOUBLE
+template <> struct builtin_float_dtype< NPY_BITSOF_LONGDOUBLE > {
+ static dtype get() { return DTYPE_FROM_CODE(NPY_LONGDOUBLE); }
+};
+template dtype get_float_dtype< NPY_BITSOF_LONGDOUBLE >();
+template <> struct builtin_complex_dtype< 2 * NPY_BITSOF_LONGDOUBLE > {
+ static dtype get() { return DTYPE_FROM_CODE(NPY_CLONGDOUBLE); }
+};
+template dtype get_complex_dtype< 2 * NPY_BITSOF_LONGDOUBLE >();
+#endif
+
+} // namespace detail
+
+python::detail::new_reference dtype::convert(python::object const & arg, bool align) {
PyArray_Descr* obj=NULL;
- if (align)
- {
+ if (align) {
if (PyArray_DescrAlignConverter(arg.ptr(), &obj) < 0)
python::throw_error_already_set();
- }
- else
- {
+ } else {
if (PyArray_DescrConverter(arg.ptr(), &obj) < 0)
python::throw_error_already_set();
}
@@ -61,28 +81,72 @@ python::detail::new_reference dtype::convert(python::object const & arg, bool al
int dtype::get_itemsize() const { return reinterpret_cast<PyArray_Descr*>(ptr())->elsize;}
+namespace {
+
+namespace pyconv = boost::python::converter;
+
template <typename T>
-dtype dtype::get_builtin() { return dtype_traits<T>::get(); }
-
-NUMPY_DTYPE_TRAITS_BUILTIN(bool, NPY_BOOL);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_ubyte, NPY_UBYTE);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_byte, NPY_BYTE);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_ushort, NPY_USHORT);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_short, NPY_SHORT);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_uint, NPY_UINT);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_int, NPY_INT);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_ulong, NPY_ULONG);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_long, NPY_LONG);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_longlong, NPY_LONGLONG);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_float, NPY_FLOAT);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_double, NPY_DOUBLE);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_longdouble, NPY_LONGDOUBLE);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_cfloat, NPY_CFLOAT);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_cdouble, NPY_CDOUBLE);
-NUMPY_DTYPE_TRAITS_BUILTIN(npy_clongdouble, NPY_CLONGDOUBLE);
-NUMPY_DTYPE_TRAITS_COMPLEX(float, npy_cfloat, NPY_CFLOAT);
-NUMPY_DTYPE_TRAITS_COMPLEX(double, npy_cdouble, NPY_CDOUBLE);
-NUMPY_DTYPE_TRAITS_COMPLEX(long double, npy_clongdouble, NPY_CLONGDOUBLE);
+class array_scalar_converter {
+public:
+
+ static PyTypeObject const * get_pytype() {
+ // This implementation depends on the fact that get_builtin returns pointers to objects
+ // NumPy has declared statically, and that the typeobj member also refers to a static
+ // object. That means we don't need to do any reference counting.
+ // In fact, I'm somewhat concerned that increasing the reference count of any of these
+ // might cause leaks, because I don't think Boost.Python ever decrements it, but it's
+ // probably a moot point if everything is actually static.
+ return reinterpret_cast<PyArray_Descr*>(dtype::get_builtin<T>().ptr())->typeobj;
+ }
+
+ static void * convertible(PyObject * obj) {
+ if (obj->ob_type == get_pytype()) {
+ return obj;
+ } else {
+ return 0;
+ }
+ }
+
+ static void convert(PyObject * obj, pyconv::rvalue_from_python_stage1_data* data) {
+ void * storage = reinterpret_cast<pyconv::rvalue_from_python_storage<T>*>(data)->storage.bytes;
+ // We assume std::complex is a "standard layout" here and elsewhere; not guaranteed by
+ // C++03 standard, but true in every known implementation (and guaranteed by C++11).
+ PyArray_ScalarAsCtype(obj, reinterpret_cast<T*>(storage));
+ data->convertible = storage;
+ }
+
+ static void declare() {
+ pyconv::registry::push_back(
+ &convertible, &convert, python::type_id<T>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+ , &get_pytype
+#endif
+ );
+ }
+
+};
+
+} // anonymous
+
+void dtype::register_scalar_converters() {
+ array_scalar_converter<bool>::declare();
+ array_scalar_converter<npy_uint8>::declare();
+ array_scalar_converter<npy_int8>::declare();
+ array_scalar_converter<npy_uint16>::declare();
+ array_scalar_converter<npy_int16>::declare();
+ array_scalar_converter<npy_uint32>::declare();
+ array_scalar_converter<npy_int32>::declare();
+ array_scalar_converter<npy_uint64>::declare();
+ array_scalar_converter<npy_int64>::declare();
+ array_scalar_converter<float>::declare();
+ array_scalar_converter<double>::declare();
+ array_scalar_converter< std::complex<float> >::declare();
+ array_scalar_converter< std::complex<double> >::declare();
+#if NPY_BITSOF_LONGDOUBLE > NPY_BITSOF_DOUBLE
+ array_scalar_converter<long double>::declare();
+ array_scalar_converter< std::complex<long double> >::declare();
+#endif
+}
} // namespace boost::numpy
} // namespace boost
View
@@ -1,15 +1,18 @@
#define BOOST_NUMPY_INTERNAL_MAIN
#include <boost/numpy/internal.hpp>
+#include <boost/numpy/dtype.hpp>
namespace boost
{
namespace numpy
{
-void initialize()
+void initialize(bool register_scalar_converters)
{
import_array();
import_ufunc();
+ if (register_scalar_converters)
+ dtype::register_scalar_converters();
}
}
@@ -22,7 +22,7 @@ def PythonUnitTest(env, script, dependencies):
env.Depends(run, dependencies)
return run
-for name in ("ufunc", "templates", "ndarray", "indexing", "shapes"):
+for name in ("dtype", "ufunc", "templates", "ndarray", "indexing", "shapes"):
mod = test_env.LoadableModule("%s_mod" % name, "%s_mod.cpp" % name, LDMODULEPREFIX="")
test.extend(PythonUnitTest(test_env, "%s.py" % name, mod))
Oops, something went wrong.

0 comments on commit 28a9fab

Please sign in to comment.