transparent conversion of dense and sparse Eigen types
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1fe3fdc..38297db 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -84,6 +84,11 @@
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
 endif()
 
+
+# Check if Eigen is available
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools")
+find_package(Eigen3 QUIET)
+
 # Include path for pybind11 header files
 include_directories(include)
 
@@ -96,6 +101,7 @@
   include/pybind11/common.h
   include/pybind11/complex.h
   include/pybind11/descr.h
+  include/pybind11/eigen.h
   include/pybind11/functional.h
   include/pybind11/numpy.h
   include/pybind11/operators.h
@@ -125,6 +131,15 @@
   example/issues.cpp
 )
 
+if (EIGEN3_FOUND)
+  include_directories(${EIGEN3_INCLUDE_DIR})
+  list(APPEND PYBIND11_EXAMPLES example/eigen.cpp)
+  add_definitions(-DPYBIND11_TEST_EIGEN)
+  message(STATUS "Building Eigen testcase")
+else()
+  message(STATUS "NOT Building Eigen testcase")
+endif()
+
 # Create the binding library
 add_library(example SHARED
   ${PYBIND11_HEADERS}
diff --git a/docs/advanced.rst b/docs/advanced.rst
index eb11176..901faa7 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -792,13 +792,157 @@
 :func:`handle::call` when the input arguments cannot be converted to Python
 objects.
 
+.. _opaque:
+
+Treating STL data structures as opaque objects
+==============================================
+
+pybind11 heavily relies on a template matching mechanism to convert parameters
+and return values that are constructed from STL data types such as vectors,
+linked lists, hash tables, etc. This even works in a recursive manner, for
+instance to deal with lists of hash maps of pairs of elementary and custom
+types, etc.
+
+However, a fundamental limitation of this approach is that internal conversions
+between Python and C++ types involve a copy operation that prevents
+pass-by-reference semantics. What does this mean?
+
+Suppose we bind the following function
+
+.. code-block:: cpp
+
+    void append_1(std::vector<int> &v) {
+       v.push_back(1);
+    }
+
+and call it from Python, the following happens:
+
+.. code-block:: python
+
+   >>> v = [5, 6]
+   >>> append_1(v)
+   >>> print(v)
+   [5, 6]
+
+As you can see, when passing STL data structures by reference, modifications
+are not propagated back the Python side. A similar situation arises when
+exposing STL data structures using the ``def_readwrite`` or ``def_readonly``
+functions:
+
+.. code-block:: cpp
+
+    /* ... definition ... */
+
+    class MyClass {
+        std::vector<int> contents;
+    };
+
+    /* ... binding code ... */
+
+    py::class_<MyClass>(m, "MyClass")
+        .def(py::init<>)
+        .def_readwrite("contents", &MyClass::contents);
+
+In this case, properties can be read and written in their entirety. However, an
+``append`` operaton involving such a list type has no effect:
+
+.. code-block:: python
+
+   >>> m = MyClass()
+   >>> m.contents = [5, 6]
+   >>> print(m.contents)
+   [5, 6]
+   >>> m.contents.append(7)
+   >>> print(m.contents)
+   [5, 6]
+
+To deal with both of the above situations, pybind11 provides a macro named
+``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based conversion
+machinery of types, thus rendering them *opaque*. The contents of opaque
+objects are never inspected or extracted, hence they can be passed by
+reference. For instance, to turn ``std::vector<int>`` into an opaque type, add
+the declaration
+
+.. code-block:: cpp
+
+    PYBIND11_MAKE_OPAQUE(std::vector<int>);
+
+before any binding code (e.g. invocations to ``class_::def()``, etc.). This
+macro must be specified at the top level, since instantiates a partial template
+overload. If your binding code consists of multiple compilation units, it must
+be present in every file preceding any usage of ``std::vector<int>``. Opaque
+types must also have a corresponding ``class_`` declaration to associate them
+with a name in Python, and to define a set of available operations:
+
+.. code-block:: cpp
+
+    py::class_<std::vector<int>>(m, "IntVector")
+        .def(py::init<>())
+        .def("clear", &std::vector<int>::clear)
+        .def("pop_back", &std::vector<int>::pop_back)
+        .def("__len__", [](const std::vector<int> &v) { return v.size(); })
+        .def("__iter__", [](std::vector<int> &v) {
+           return py::make_iterator(v.begin(), v.end());
+        }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
+        // ....
+
+
+.. seealso::
+
+    The file :file:`example/example14.cpp` contains a complete example that
+    demonstrates how to create and expose opaque types using pybind11 in more
+    detail.
+
+.. _eigen:
+
+Transparent conversion of dense and sparse Eigen data types
+===========================================================
+
+Eigen [#f1]_ is C++ header-based library for dense and sparse linear algebra. Due to
+its popularity and widespread adoption, pybind11 provides transparent
+conversion support between Eigen and Scientific Python linear algebra data types.
+
+Specifically, when including the optional header file :file:`pybind11/eigen.h`,
+pybind11 will automatically and transparently convert 
+
+1. Static and dynamic Eigen dense vectors and matrices to instances of
+   ``numpy.ndarray`` (and vice versa).
+
+1. Eigen sparse vectors and matrices to instances of
+   ``scipy.sparse.csr_matrix``/``scipy.sparse.csc_matrix`` (and vice versa).
+
+This makes it possible to bind most kinds of functions that rely on these types.
+One major caveat are functions that take Eigen matrices *by reference* and modify
+them somehow, in which case the information won't be propagated to the caller.
+
+.. code-block:: cpp
+
+    /* The Python bindings of this function won't replicate
+       the intended effect of modifying the function argument */
+    void scale_by_2(Eigen::Vector3f &v) {
+       v *= 2;
+    }
+
+To see why this is, refer to the section on :ref:`opaque` (although that
+section specifically covers STL data types, the underlying issue is the same).
+The next two sections discuss an efficient alternative for exposing the
+underlying native Eigen types as opaque objects in a way that still integrates
+with NumPy and SciPy.
+
+.. [#f1] http://eigen.tuxfamily.org
+
+.. seealso::
+
+    The file :file:`example/eigen.cpp` contains a complete example that
+    shows how to pass Eigen sparse and dense data types in more detail.
+
 Buffer protocol
 ===============
 
 Python supports an extremely general and convenient approach for exchanging
-data between plugin libraries. Types can expose a buffer view [#f1]_,
-which provides fast direct access to the raw internal representation. Suppose
-we want to bind the following simplistic Matrix class:
+data between plugin libraries. Types can expose a buffer view [#f2]_, which
+provides fast direct access to the raw internal data representation. Suppose we
+want to bind the following simplistic Matrix class:
 
 .. code-block:: cpp
 
@@ -856,16 +1000,19 @@
 necessary in the function body. Below, you can see an basic example on how to
 define a custom constructor for the Eigen double precision matrix
 (``Eigen::MatrixXd``) type, which supports initialization from compatible
-buffer
-objects (e.g. a NumPy matrix).
+buffer objects (e.g. a NumPy matrix).
 
 .. code-block:: cpp
 
-    py::class_<Eigen::MatrixXd>(m, "MatrixXd")
-        .def("__init__", [](Eigen::MatrixXd &m, py::buffer b) {
+    /* Bind MatrixXd (or some other Eigen type) to Python */
+    typedef Eigen::MatrixXd Matrix;
+
+    typedef Matrix::Scalar Scalar;
+    constexpr bool rowMajor = Matrix::Flags & Eigen::RowMajorBit;
+
+    py::class_<Matrix>(m, "Matrix")
+        .def("__init__", [](Matrix &m, py::buffer b) {
             typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
-            typedef Eigen::MatrixXd Matrix;
-            typedef Matrix::Scalar Scalar;
 
             /* Request a buffer descriptor from Python */
             py::buffer_info info = b.request();
@@ -878,21 +1025,46 @@
                 throw std::runtime_error("Incompatible buffer dimension!");
 
             auto strides = Strides(
-                info.strides[Matrix::Flags & Eigen::RowMajorBit ? 0 : 1] / sizeof(Scalar),
-                info.strides[Matrix::Flags & Eigen::RowMajorBit ? 1 : 0] / sizeof(Scalar));
+                info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
+                info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
 
             auto map = Eigen::Map<Matrix, 0, Strides>(
-                (Scalar *) info.ptr, info.shape[0], info.shape[1], strides);
+                static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
 
             new (&m) Matrix(map);
         });
 
+For reference, the ``def_buffer()`` call for this Eigen data type should look
+as follows:
+
+.. code-block:: cpp
+
+    .def_buffer([](Matrix &m) -> py::buffer_info {
+        return py::buffer_info(
+            m.data(),                /* Pointer to buffer */
+            sizeof(Scalar),          /* Size of one scalar */
+            /* Python struct-style format descriptor */
+            py::format_descriptor<Scalar>::value,
+            /* Number of dimensions */
+            2,
+            /* Buffer dimensions */
+            { (size_t) m.rows(),
+              (size_t) m.cols() },
+            /* Strides (in bytes) for each index */
+            { sizeof(Scalar) * (rowMajor ? m.cols() : 1),
+              sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
+        );
+     })
+
+For a much easier approach of binding Eigen types (although with some
+limitations), refer to the section on :ref:`eigen`.
+
 .. seealso::
 
     The file :file:`example/example7.cpp` contains a complete example that
     demonstrates using the buffer protocol with pybind11 in more detail.
 
-.. [#f1] http://docs.python.org/3/c-api/buffer.html
+.. [#f2] http://docs.python.org/3/c-api/buffer.html
 
 NumPy support
 =============
@@ -1199,105 +1371,6 @@
     };
 
 
-Treating STL data structures as opaque objects
-==============================================
-
-pybind11 heavily relies on a template matching mechanism to convert parameters
-and return values that are constructed from STL data types such as vectors,
-linked lists, hash tables, etc. This even works in a recursive manner, for
-instance to deal with lists of hash maps of pairs of elementary and custom
-types, etc.
-
-However, a fundamental limitation of this approach is that internal conversions
-between Python and C++ types involve a copy operation that prevents
-pass-by-reference semantics. What does this mean?
-
-Suppose we bind the following function
-
-.. code-block:: cpp
-
-    void append_1(std::vector<int> &v) {
-       v.push_back(1);
-    }
-
-and call it from Python, the following happens:
-
-.. code-block:: python
-
-   >>> v = [5, 6]
-   >>> append_1(v)
-   >>> print(v)
-   [5, 6]
-
-As you can see, when passing STL data structures by reference, modifications
-are not propagated back the Python side. A similar situation arises when
-exposing STL data structures using the ``def_readwrite`` or ``def_readonly``
-functions:
-
-.. code-block:: cpp
-
-    /* ... definition ... */
-
-    class MyClass {
-        std::vector<int> contents;
-    };
-
-    /* ... binding code ... */
-
-    py::class_<MyClass>(m, "MyClass")
-        .def(py::init<>)
-        .def_readwrite("contents", &MyClass::contents);
-
-In this case, properties can be read and written in their entirety. However, an
-``append`` operaton involving such a list type has no effect:
-
-.. code-block:: python
-
-   >>> m = MyClass()
-   >>> m.contents = [5, 6]
-   >>> print(m.contents)
-   [5, 6]
-   >>> m.contents.append(7)
-   >>> print(m.contents)
-   [5, 6]
-
-To deal with both of the above situations, pybind11 provides a macro named
-``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based conversion
-machinery of types, thus rendering them *opaque*. The contents of opaque
-objects are never inspected or extracted, hence they can be passed by
-reference. For instance, to turn ``std::vector<int>`` into an opaque type, add
-the declaration
-
-.. code-block:: cpp
-
-    PYBIND11_MAKE_OPAQUE(std::vector<int>);
-
-before any binding code (e.g. invocations to ``class_::def()``, etc.). This
-macro must be specified at the top level, since instantiates a partial template
-overload. If your binding code consists of multiple compilation units, it must
-be present in every file preceding any usage of ``std::vector<int>``. Opaque
-types must also have a corresponding ``class_`` declaration to associate them
-with a name in Python, and to define a set of available operations:
-
-.. code-block:: cpp
-
-    py::class_<std::vector<int>>(m, "IntVector")
-        .def(py::init<>())
-        .def("clear", &std::vector<int>::clear)
-        .def("pop_back", &std::vector<int>::pop_back)
-        .def("__len__", [](const std::vector<int> &v) { return v.size(); })
-        .def("__iter__", [](std::vector<int> &v) {
-           return py::make_iterator(v.begin(), v.end());
-        }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
-        // ....
-
-
-.. seealso::
-
-    The file :file:`example/example14.cpp` contains a complete example that
-    demonstrates how to create and expose opaque types using pybind11 in more
-    detail.
-
 Pickling support
 ================
 
@@ -1320,7 +1393,7 @@
         int m_extra = 0;
     };
 
-The binding code including the requisite ``__setstate__`` and ``__getstate__`` methods [#f2]_
+The binding code including the requisite ``__setstate__`` and ``__getstate__`` methods [#f3]_
 looks as follows:
 
 .. code-block:: cpp
@@ -1372,14 +1445,14 @@
     The file :file:`example/example15.cpp` contains a complete example that
     demonstrates how to pickle and unpickle types using pybind11 in more detail.
 
-.. [#f2] http://docs.python.org/3/library/pickle.html#pickling-class-instances
+.. [#f3] http://docs.python.org/3/library/pickle.html#pickling-class-instances
 
 Generating documentation using Sphinx
 =====================================
 
-Sphinx [#f3]_ has the ability to inspect the signatures and documentation
+Sphinx [#f4]_ has the ability to inspect the signatures and documentation
 strings in pybind11-based extension modules to automatically generate beautiful
-documentation in a variety formats. The pbtest repository [#f4]_ contains a
+documentation in a variety formats. The pbtest repository [#f5]_ contains a
 simple example repository which uses this approach.
 
 There are two potential gotchas when using this approach: first, make sure that
@@ -1406,6 +1479,6 @@
         ----------
     )mydelimiter");
 
-.. [#f3] http://www.sphinx-doc.org
-.. [#f4] http://github.com/pybind/pbtest
+.. [#f4] http://www.sphinx-doc.org
+.. [#f5] http://github.com/pybind/pbtest
 
diff --git a/docs/basics.rst b/docs/basics.rst
index cf39844..b1765c5 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -275,6 +275,10 @@
 +---------------------------------+--------------------------+-------------------------------+
 | ``std::function<...>``          | STL polymorphic function | :file:`pybind11/functional.h` |
 +---------------------------------+--------------------------+-------------------------------+
+| ``Eigen::Matrix<...>``          | Dense Eigen matrices     | :file:`pybind11/eigen.h`      |
++---------------------------------+--------------------------+-------------------------------+
+| ``Eigen::SparseMatrix<...>``    | Sparse Eigen matrices    | :file:`pybind11/eigen.h`      |
++---------------------------------+--------------------------+-------------------------------+
 
 
 .. [#f1] In practice, implementation and binding code will generally be located
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 103bd38..b5dee5f 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -5,6 +5,7 @@
 
 1.8 (Not yet released)
 ----------------------
+* Transparent conversion of sparse and dense Eigen data types
 * Fixed incorrect default return value policy for functions returning a shared
   pointer
 * Don't allow casting a ``None`` value into a C++ lvalue reference
diff --git a/example/eigen.cpp b/example/eigen.cpp
new file mode 100644
index 0000000..b6fa24a
--- /dev/null
+++ b/example/eigen.cpp
@@ -0,0 +1,73 @@
+/*
+    example/eigen.cpp -- automatic conversion of Eigen types
+
+    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+    All rights reserved. Use of this source code is governed by a
+    BSD-style license that can be found in the LICENSE file.
+*/
+
+#include "example.h"
+#include <pybind11/eigen.h>
+
+void init_eigen(py::module &m) {
+    typedef Eigen::Matrix<float, 5, 6, Eigen::RowMajor> FixedMatrixR;
+    typedef Eigen::Matrix<float, 5, 6> FixedMatrixC;
+    typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> DenseMatrixR;
+    typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> DenseMatrixC;
+    typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
+    typedef Eigen::SparseMatrix<float> SparseMatrixC;
+
+    // Non-symmetric matrix with zero elements
+    Eigen::MatrixXf mat(5, 6);
+    mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0,
+        0, 0, 0, 0, 11, 0, 0, 14, 0, 8, 11;
+
+    m.def("fixed_r", [mat]() -> FixedMatrixR { 
+        return FixedMatrixR(mat);
+    });
+
+    m.def("fixed_c", [mat]() -> FixedMatrixC { 
+        return FixedMatrixC(mat);
+    });
+
+    m.def("fixed_passthrough_r", [](const FixedMatrixR &m) -> FixedMatrixR { 
+        return m;
+    });
+
+    m.def("fixed_passthrough_c", [](const FixedMatrixC &m) -> FixedMatrixC { 
+        return m;
+    });
+
+    m.def("dense_r", [mat]() -> DenseMatrixR { 
+        return DenseMatrixR(mat);
+    });
+
+    m.def("dense_c", [mat]() -> DenseMatrixC { 
+        return DenseMatrixC(mat);
+    });
+
+    m.def("dense_passthrough_r", [](const DenseMatrixR &m) -> DenseMatrixR { 
+        return m;
+    });
+
+    m.def("dense_passthrough_c", [](const DenseMatrixC &m) -> DenseMatrixC { 
+        return m;
+    });
+
+    m.def("sparse_r", [mat]() -> SparseMatrixR { 
+        return Eigen::SparseView<Eigen::MatrixXf>(mat);
+    });
+
+    m.def("sparse_c", [mat]() -> SparseMatrixC { 
+        return Eigen::SparseView<Eigen::MatrixXf>(mat);
+    });
+
+    m.def("sparse_passthrough_r", [](const SparseMatrixR &m) -> SparseMatrixR { 
+        return m;
+    });
+
+    m.def("sparse_passthrough_c", [](const SparseMatrixC &m) -> SparseMatrixC { 
+        return m;
+    });
+}
diff --git a/example/eigen.py b/example/eigen.py
new file mode 100644
index 0000000..accaf23
--- /dev/null
+++ b/example/eigen.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+from __future__ import print_function
+import sys
+sys.path.append('.')
+
+from example import fixed_r, fixed_c
+from example import fixed_passthrough_r, fixed_passthrough_c
+from example import dense_r, dense_c
+from example import dense_passthrough_r, dense_passthrough_c
+from example import sparse_r, sparse_c
+from example import sparse_passthrough_r, sparse_passthrough_c
+import numpy as np
+
+ref = np.array(
+    [[0, 3, 0, 0, 0, 11],
+     [22, 0, 0, 0, 17, 11],
+     [7, 5, 0, 1, 0, 11],
+     [0, 0, 0, 0, 0, 11],
+     [0, 0, 14, 0, 8, 11]])
+
+
+def check(mat):
+    return 'OK' if np.sum(mat - ref) == 0 else 'NOT OK'
+
+print("fixed_r = %s" % check(fixed_r()))
+print("fixed_c = %s" % check(fixed_c()))
+print("pt_r(fixed_r) = %s" % check(fixed_passthrough_r(fixed_r())))
+print("pt_c(fixed_c) = %s" % check(fixed_passthrough_c(fixed_c())))
+print("pt_r(fixed_c) = %s" % check(fixed_passthrough_r(fixed_c())))
+print("pt_c(fixed_r) = %s" % check(fixed_passthrough_c(fixed_r())))
+
+print("dense_r = %s" % check(dense_r()))
+print("dense_c = %s" % check(dense_c()))
+print("pt_r(dense_r) = %s" % check(dense_passthrough_r(dense_r())))
+print("pt_c(dense_c) = %s" % check(dense_passthrough_c(dense_c())))
+print("pt_r(dense_c) = %s" % check(dense_passthrough_r(dense_c())))
+print("pt_c(dense_r) = %s" % check(dense_passthrough_c(dense_r())))
+
+print("sparse_r = %s" % check(sparse_r()))
+print("sparse_c = %s" % check(sparse_c()))
+print("pt_r(sparse_r) = %s" % check(sparse_passthrough_r(sparse_r())))
+print("pt_c(sparse_c) = %s" % check(sparse_passthrough_c(sparse_c())))
+print("pt_r(sparse_c) = %s" % check(sparse_passthrough_r(sparse_c())))
+print("pt_c(sparse_r) = %s" % check(sparse_passthrough_c(sparse_r())))
diff --git a/example/eigen.ref b/example/eigen.ref
new file mode 100644
index 0000000..b87f8ed
--- /dev/null
+++ b/example/eigen.ref
@@ -0,0 +1,18 @@
+fixed_r = OK
+fixed_c = OK
+pt_r(fixed_r) = OK
+pt_c(fixed_c) = OK
+pt_r(fixed_c) = OK
+pt_c(fixed_r) = OK
+dense_r = OK
+dense_c = OK
+pt_r(dense_r) = OK
+pt_c(dense_c) = OK
+pt_r(dense_c) = OK
+pt_c(dense_r) = OK
+sparse_r = OK
+sparse_c = OK
+pt_r(sparse_r) = OK
+pt_c(sparse_c) = OK
+pt_r(sparse_c) = OK
+pt_c(sparse_r) = OK
diff --git a/example/example.cpp b/example/example.cpp
index 34b2df7..b4199e8 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -27,6 +27,10 @@
 void init_ex16(py::module &);
 void init_issues(py::module &);
 
+#if defined(PYBIND11_TEST_EIGEN)
+    void init_eigen(py::module &);
+#endif
+
 PYBIND11_PLUGIN(example) {
     py::module m("example", "pybind example plugin");
 
@@ -48,5 +52,9 @@
     init_ex16(m);
     init_issues(m);
 
+    #if defined(PYBIND11_TEST_EIGEN)
+        init_eigen(m);
+    #endif
+
     return m.ptr();
 }
diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h
new file mode 100644
index 0000000..f2f0985
--- /dev/null
+++ b/include/pybind11/eigen.h
@@ -0,0 +1,261 @@
+/*
+    pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices
+
+    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+    All rights reserved. Use of this source code is governed by a
+    BSD-style license that can be found in the LICENSE file.
+*/
+
+#pragma once
+
+#include "numpy.h"
+#include <Eigen/Core>
+#include <Eigen/SparseCore>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
+#endif
+
+NAMESPACE_BEGIN(pybind11)
+NAMESPACE_BEGIN(detail)
+
+template <typename T> class is_eigen_dense {
+private:
+    template<typename Derived> static std::true_type test(const Eigen::DenseBase<Derived> &);
+    static std::false_type test(...);
+public:
+    static constexpr bool value = decltype(test(std::declval<T>()))::value;
+};
+
+template <typename T> class is_eigen_sparse {
+private:
+    template<typename Derived> static std::true_type test(const Eigen::SparseMatrixBase<Derived> &);
+    static std::false_type test(...);
+public:
+    static constexpr bool value = decltype(test(std::declval<T>()))::value;
+};
+
+template<typename Type>
+struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value>::type> {
+    typedef typename Type::Scalar Scalar;
+    static constexpr bool rowMajor = Type::Flags & Eigen::RowMajorBit;
+
+    bool load(handle src, bool) {
+       array_t<Scalar> buffer(src, true);
+       if (!buffer.check())
+           return false;
+
+        buffer_info info = buffer.request();
+        if (info.ndim == 1) {
+            typedef Eigen::Stride<Eigen::Dynamic, 0> Strides;
+            if (!Type::IsVectorAtCompileTime &&
+                !(Type::RowsAtCompileTime == Eigen::Dynamic &&
+                  Type::ColsAtCompileTime == Eigen::Dynamic))
+                return false;
+
+            if (Type::SizeAtCompileTime != Eigen::Dynamic &&
+                info.shape[0] != (size_t) Type::SizeAtCompileTime)
+                return false;
+
+            auto strides = Strides(info.strides[0] / sizeof(Scalar), 0);
+
+            value = Eigen::Map<Type, 0, Strides>(
+                (Scalar *) info.ptr, info.shape[0], 1, strides);
+        } else if (info.ndim == 2) {
+            typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
+
+            if ((Type::RowsAtCompileTime != Eigen::Dynamic && info.shape[0] != (size_t) Type::RowsAtCompileTime) ||
+                (Type::ColsAtCompileTime != Eigen::Dynamic && info.shape[1] != (size_t) Type::ColsAtCompileTime))
+                return false;
+
+            auto strides = Strides(
+                info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
+                info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
+
+            value = Eigen::Map<Type, 0, Strides>(
+                (Scalar *) info.ptr, info.shape[0], info.shape[1], strides);
+        } else {
+            return false;
+        }
+        return true;
+    }
+
+    static handle cast(const Type *src, return_value_policy policy, handle parent) {
+        return cast(*src, policy, parent);
+    }
+
+    static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
+        array result(buffer_info(
+            /* Pointer to buffer */
+            const_cast<Scalar *>(src.data()),
+            /* Size of one scalar */
+            sizeof(Scalar),
+            /* Python struct-style format descriptor */
+            format_descriptor<Scalar>::value,
+            /* Number of dimensions */
+            2,
+            /* Buffer dimensions */
+            { (size_t) src.rows(),
+              (size_t) src.cols() },
+            /* Strides (in bytes) for each index */
+            { sizeof(Scalar) * (rowMajor ? src.cols() : 1),
+              sizeof(Scalar) * (rowMajor ? 1 : src.rows()) }
+        ));
+        return result.release();
+    }
+
+    template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
+
+    static PYBIND11_DESCR name() {
+        return _("numpy.ndarray[dtype=") + npy_format_descriptor<Scalar>::name() +
+               _(", shape=(") + rows() + _(", ") + cols() + _(")]");
+    }
+
+    operator Type*() { return &value; }
+    operator Type&() { return value; }
+
+private:
+    template <typename T = Type, typename std::enable_if<T::RowsAtCompileTime == Eigen::Dynamic, int>::type = 0>
+    static PYBIND11_DESCR rows() { return _("m"); }
+    template <typename T = Type, typename std::enable_if<T::RowsAtCompileTime != Eigen::Dynamic, int>::type = 0>
+    static PYBIND11_DESCR rows() { return _<T::RowsAtCompileTime>(); }
+    template <typename T = Type, typename std::enable_if<T::ColsAtCompileTime == Eigen::Dynamic, int>::type = 0>
+    static PYBIND11_DESCR cols() { return _("n"); }
+    template <typename T = Type, typename std::enable_if<T::ColsAtCompileTime != Eigen::Dynamic, int>::type = 0>
+    static PYBIND11_DESCR cols() { return _<T::ColsAtCompileTime>(); }
+
+private:
+    Type value;
+};
+
+template<typename Type>
+struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::type> {
+    typedef typename Type::Scalar Scalar;
+    typedef typename std::remove_reference<decltype(*std::declval<Type>().outerIndexPtr())>::type StorageIndex;
+    typedef typename Type::Index Index;
+    static constexpr bool rowMajor = Type::Flags & Eigen::RowMajorBit;
+
+    bool load(handle src, bool) {
+        object obj(src, true);
+        object sparse_module = module::import("scipy.sparse");
+        object matrix_type = sparse_module.attr(
+            rowMajor ? "csr_matrix" : "csc_matrix");
+
+        if (obj.get_type() != matrix_type.ptr()) {
+            try {
+                obj = matrix_type.call(obj);
+            } catch (const error_already_set &) {
+                PyErr_Clear(); 
+                return false;
+            }
+        }
+
+        auto valuesArray = array_t<Scalar>((object) obj.attr("data"));
+        auto innerIndicesArray = array_t<StorageIndex>((object) obj.attr("indices"));
+        auto outerIndicesArray = array_t<StorageIndex>((object) obj.attr("indptr"));
+        auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
+        auto nnz = obj.attr("nnz").cast<Index>();
+
+        if (!valuesArray.check() || !innerIndicesArray.check() ||
+            !outerIndicesArray.check())
+            return false;
+
+        buffer_info outerIndices = outerIndicesArray.request();
+        buffer_info innerIndices = innerIndicesArray.request();
+        buffer_info values = valuesArray.request();
+
+        value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>(
+            shape[0].cast<Index>(),
+            shape[1].cast<Index>(),
+            nnz,
+            static_cast<StorageIndex *>(outerIndices.ptr),
+            static_cast<StorageIndex *>(innerIndices.ptr),
+            static_cast<Scalar *>(values.ptr)
+        );
+
+        return true;
+    }
+
+    static handle cast(const Type *src, return_value_policy policy, handle parent) {
+        return cast(*src, policy, parent);
+    }
+
+    static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
+        const_cast<Type&>(src).makeCompressed();
+
+        object matrix_type = module::import("scipy.sparse").attr(
+            rowMajor ? "csr_matrix" : "csc_matrix");
+
+        array data(buffer_info(
+            // Pointer to buffer
+            const_cast<Scalar *>(src.valuePtr()),
+            // Size of one scalar
+            sizeof(Scalar),
+            // Python struct-style format descriptor
+            format_descriptor<Scalar>::value,
+            // Number of dimensions
+            1,
+            // Buffer dimensions
+            { (size_t) src.nonZeros() },
+            // Strides
+            { sizeof(Scalar) }
+        ));
+
+        array outerIndices(buffer_info(
+            // Pointer to buffer
+            const_cast<StorageIndex *>(src.outerIndexPtr()),
+            // Size of one scalar
+            sizeof(StorageIndex),
+            // Python struct-style format descriptor
+            format_descriptor<StorageIndex>::value,
+            // Number of dimensions
+            1,
+            // Buffer dimensions
+            { (size_t) (rowMajor ? src.rows() : src.cols()) + 1 },
+            // Strides
+            { sizeof(StorageIndex) }
+        ));
+
+        array innerIndices(buffer_info(
+            // Pointer to buffer
+            const_cast<StorageIndex *>(src.innerIndexPtr()),
+            // Size of one scalar
+            sizeof(StorageIndex),
+            // Python struct-style format descriptor
+            format_descriptor<StorageIndex>::value,
+            // Number of dimensions
+            1,
+            // Buffer dimensions
+            { (size_t) src.nonZeros() },
+            // Strides
+            { sizeof(StorageIndex) }
+        ));
+
+        return matrix_type.call(
+            std::make_tuple(data, innerIndices, outerIndices),
+            std::make_pair(src.rows(), src.cols())
+        ).release();
+    }
+
+    template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
+
+    template <typename T = Type, typename std::enable_if<(T::Flags & Eigen::RowMajorBit) != 0, int>::type = 0>
+    static PYBIND11_DESCR name() { return _("scipy.sparse.csr_matrix[dtype=") + npy_format_descriptor<Scalar>::name() + _("]"); }
+    template <typename T = Type, typename std::enable_if<(T::Flags & Eigen::RowMajorBit) == 0, int>::type = 0>
+    static PYBIND11_DESCR name() { return _("scipy.sparse.csc_matrix[dtype=") + npy_format_descriptor<Scalar>::name() + _("]"); }
+
+    operator Type*() { return &value; }
+    operator Type&() { return value; }
+
+private:
+    Type value;
+};
+
+NAMESPACE_END(detail)
+NAMESPACE_END(pybind11)
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h
index 42d027a..b35790b 100644
--- a/include/pybind11/numpy.h
+++ b/include/pybind11/numpy.h
@@ -1,5 +1,5 @@
 /*
-    pybind11/numpy.h: Basic NumPy support, auto-vectorization support
+    pybind11/numpy.h: Basic NumPy support, vectorize() wrapper
 
     Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
 
@@ -20,8 +20,7 @@
 #endif
 
 NAMESPACE_BEGIN(pybind11)
-
-template <typename type, typename SFINAE = void> struct npy_format_descriptor { };
+namespace detail { template <typename type, typename SFINAE = void> struct npy_format_descriptor { }; }
 
 class array : public buffer {
 public:
@@ -84,7 +83,7 @@
 
     template <typename Type> array(size_t size, const Type *ptr) {
         API& api = lookup_api();
-        PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<Type>::value);
+        PyObject *descr = api.PyArray_DescrFromType_(detail::npy_format_descriptor<Type>::value);
         if (descr == nullptr)
             pybind11_fail("NumPy: unsupported buffer format!");
         Py_intptr_t shape = (Py_intptr_t) size;
@@ -134,7 +133,7 @@
         if (ptr == nullptr)
             return nullptr;
         API &api = lookup_api();
-        PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<T>::value);
+        PyObject *descr = api.PyArray_DescrFromType_(detail::npy_format_descriptor<T>::value);
         PyObject *result = api.PyArray_FromAny_(
             ptr, descr, 0, 0,
             API::NPY_ENSURE_ARRAY_ | API::NPY_ARRAY_FORCECAST_ | ExtraFlags,
@@ -144,6 +143,8 @@
     }
 };
 
+NAMESPACE_BEGIN(detail)
+
 template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
 private:
     constexpr static const int values[] = {
@@ -151,17 +152,21 @@
         array::API::NPY_INT_,  array::API::NPY_UINT_,  array::API::NPY_LONGLONG_, array::API::NPY_ULONGLONG_ };
 public:
     enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] };
+    template <typename T2 = T, typename std::enable_if<std::is_signed<T2>::value, int>::type = 0>
+    static PYBIND11_DESCR name() { return _("int") + _<sizeof(T)*8>(); }
+    template <typename T2 = T, typename std::enable_if<!std::is_signed<T2>::value, int>::type = 0>
+    static PYBIND11_DESCR name() { return _("uint") + _<sizeof(T)*8>(); }
 };
 template <typename T> constexpr const int npy_format_descriptor<
     T, typename std::enable_if<std::is_integral<T>::value>::type>::values[8];
 
-#define DECL_FMT(t, n) template<> struct npy_format_descriptor<t> { enum { value = array::API::n }; }
-DECL_FMT(float, NPY_FLOAT_); DECL_FMT(double, NPY_DOUBLE_); DECL_FMT(bool, NPY_BOOL_);
-DECL_FMT(std::complex<float>, NPY_CFLOAT_); DECL_FMT(std::complex<double>, NPY_CDOUBLE_);
+#define DECL_FMT(Type, NumPyName, Name) template<> struct npy_format_descriptor<Type> { \
+    enum { value = array::API::NumPyName }; \
+    static PYBIND11_DESCR name() { return _(Name); } }
+DECL_FMT(float, NPY_FLOAT_, "float32"); DECL_FMT(double, NPY_DOUBLE_, "float64"); DECL_FMT(bool, NPY_BOOL_, "bool");
+DECL_FMT(std::complex<float>, NPY_CFLOAT_, "complex64"); DECL_FMT(std::complex<double>, NPY_CDOUBLE_, "complex128");
 #undef DECL_FMT
 
-NAMESPACE_BEGIN(detail)
-
 template  <class T>
 using array_iterator = typename std::add_pointer<T>::type;
 
@@ -348,7 +353,7 @@
         buffer_info buf = result.request();
         Return *output = (Return *) buf.ptr;
 
-        if(trivial_broadcast) {
+        if (trivial_broadcast) {
             /* Call the function */
             for (size_t i=0; i<size; ++i) {
                 output[i] = f((buffers[Index].size == 1
@@ -379,7 +384,7 @@
 };
 
 template <typename T> struct handle_type_name<array_t<T>> {
-    static PYBIND11_DESCR name() { return _("array[") + type_caster<T>::name() + _("]"); }
+    static PYBIND11_DESCR name() { return _("numpy.ndarray[dtype=") + type_caster<T>::name() + _("]"); }
 };
 
 NAMESPACE_END(detail)
diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h
index 39adc7e..e0177de 100644
--- a/include/pybind11/stl.h
+++ b/include/pybind11/stl.h
@@ -1,5 +1,5 @@
 /*
-    pybind11/complex.h: Complex number support
+    pybind11/stl.h: Transparent conversion for STL data types
 
     Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
 
diff --git a/setup.py b/setup.py
index 7562860..4c6e156 100644
--- a/setup.py
+++ b/setup.py
@@ -20,6 +20,7 @@
         'include/pybind11/cast.h',
         'include/pybind11/complex.h',
         'include/pybind11/descr.h',
+        'include/pybind11/eigen.h',
         'include/pybind11/numpy.h',
         'include/pybind11/pybind11.h',
         'include/pybind11/stl.h',
diff --git a/tools/FindEigen3.cmake b/tools/FindEigen3.cmake
new file mode 100644
index 0000000..9c546a0
--- /dev/null
+++ b/tools/FindEigen3.cmake
@@ -0,0 +1,81 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  endif(NOT EIGEN3_VERSION_OK)
+endmacro(_eigen3_check_version)
+
+if (EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else (EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      PATH_SUFFIXES eigen3 eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+