std::experimental::optional (#475)

* Add type caster for std::experimental::optional

* Add tests for std::experimental::optional

* Support both <optional> / <experimental/optional>

* Mention std{::experimental,}::optional in the docs
diff --git a/docs/advanced/cast/overview.rst b/docs/advanced/cast/overview.rst
index 4df1bd1..e9f43be 100644
--- a/docs/advanced/cast/overview.rst
+++ b/docs/advanced/cast/overview.rst
@@ -75,66 +75,70 @@
 an additional extension header to be included). To pass other data structures
 as arguments and return values, refer to the section on binding :ref:`classes`.
 
-+---------------------------------+--------------------------+-------------------------------+
-|  Data type                      |  Description             | Header file                   |
-+=================================+==========================+===============================+
-| ``int8_t``, ``uint8_t``         | 8-bit integers           | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``int16_t``, ``uint16_t``       | 16-bit integers          | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``int32_t``, ``uint32_t``       | 32-bit integers          | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``int64_t``, ``uint64_t``       | 64-bit integers          | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``ssize_t``, ``size_t``         | Platform-dependent size  | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``float``, ``double``           | Floating point types     | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``bool``                        | Two-state Boolean type   | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``char``                        | Character literal        | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``wchar_t``                     | Wide character literal   | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``const char *``                | UTF-8 string literal     | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``const wchar_t *``             | Wide string literal      | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::string``                 | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::wstring``                | STL dynamic wide string  | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::pair<T1, T2>``           | Pair of two custom types | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::tuple<...>``             | Arbitrary tuple of types | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::reference_wrapper<...>`` | Reference type wrapper   | :file:`pybind11/pybind11.h`   |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::complex<T>``             | Complex numbers          | :file:`pybind11/complex.h`    |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::array<T, Size>``         | STL static array         | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::vector<T>``              | STL dynamic array        | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::list<T>``                | STL linked list          | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::map<T1, T2>``            | STL ordered map          | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::unordered_map<T1, T2>``  | STL unordered map        | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::set<T>``                 | STL ordered set          | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::unordered_set<T>``       | STL unordered set        | :file:`pybind11/stl.h`        |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::function<...>``          | STL polymorphic function | :file:`pybind11/functional.h` |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::chrono::duration<...>``  | STL time duration        | :file:`pybind11/chrono.h`     |
-+---------------------------------+--------------------------+-------------------------------+
-| ``std::chrono::time_point<...>``| STL date/time            | :file:`pybind11/chrono.h`     |
-+---------------------------------+--------------------------+-------------------------------+
-| ``Eigen::Matrix<...>``          | Eigen: dense matrix      | :file:`pybind11/eigen.h`      |
-+---------------------------------+--------------------------+-------------------------------+
-| ``Eigen::Map<...>``             | Eigen: mapped memory     | :file:`pybind11/eigen.h`      |
-+---------------------------------+--------------------------+-------------------------------+
-| ``Eigen::SparseMatrix<...>``    | Eigen: sparse matrix     | :file:`pybind11/eigen.h`      |
-+---------------------------------+--------------------------+-------------------------------+
++------------------------------------+---------------------------+-------------------------------+
+|  Data type                         |  Description              | Header file                   |
++=---================================+===========================+===============================+
+| ``int8_t``, ``uint8_t``            | 8-bit integers            | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``int16_t``, ``uint16_t``          | 16-bit integers           | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``int32_t``, ``uint32_t``          | 32-bit integers           | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``int64_t``, ``uint64_t``          | 64-bit integers           | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``ssize_t``, ``size_t``            | Platform-dependent size   | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``float``, ``double``              | Floating point types      | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``bool``                           | Two-state Boolean type    | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``char``                           | Character literal         | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``wchar_t``                        | Wide character literal    | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``const char *``                   | UTF-8 string literal      | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``const wchar_t *``                | Wide string literal       | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::string``                    | STL dynamic UTF-8 string  | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::wstring``                   | STL dynamic wide string   | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::pair<T1, T2>``              | Pair of two custom types  | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::tuple<...>``                | Arbitrary tuple of types  | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::reference_wrapper<...>``    | Reference type wrapper    | :file:`pybind11/pybind11.h`   |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::complex<T>``                | Complex numbers           | :file:`pybind11/complex.h`    |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::array<T, Size>``            | STL static array          | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::vector<T>``                 | STL dynamic array         | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::list<T>``                   | STL linked list           | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::map<T1, T2>``               | STL ordered map           | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::unordered_map<T1, T2>``     | STL unordered map         | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::set<T>``                    | STL ordered set           | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::unordered_set<T>``          | STL unordered set         | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::optional<T>``               | STL optional type (C++17) | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::experimental::optional<T>`` | STL optional type (exp.)  | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::function<...>``             | STL polymorphic function  | :file:`pybind11/functional.h` |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::chrono::duration<...>``     | STL time duration         | :file:`pybind11/chrono.h`     |
++------------------------------------+---------------------------+-------------------------------+
+| ``std::chrono::time_point<...>``   | STL date/time             | :file:`pybind11/chrono.h`     |
++------------------------------------+---------------------------+-------------------------------+
+| ``Eigen::Matrix<...>``             | Eigen: dense matrix       | :file:`pybind11/eigen.h`      |
++------------------------------------+---------------------------+-------------------------------+
+| ``Eigen::Map<...>``                | Eigen: mapped memory      | :file:`pybind11/eigen.h`      |
++------------------------------------+---------------------------+-------------------------------+
+| ``Eigen::SparseMatrix<...>``       | Eigen: sparse matrix      | :file:`pybind11/eigen.h`      |
++------------------------------------+---------------------------+-------------------------------+
diff --git a/docs/changelog.rst b/docs/changelog.rst
index e837aa4..87d38d3 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -59,6 +59,7 @@
   to do it manually via ``PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)``.
 * Default return values policy changes: non-static properties now use ``reference_internal``
   and static properties use ``reference`` (previous default was ``automatic``, i.e. ``copy``).
+* Support for ``std::experimental::optional<T>`` and ``std::optional<T>`` (C++17).
 * Various minor improvements of library internals (no user-visible changes)
 
 1.8.1 (July 12, 2016)
diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h
index e5c6e3c..b3fe157 100644
--- a/include/pybind11/stl.h
+++ b/include/pybind11/stl.h
@@ -22,6 +22,21 @@
 #pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
 #endif
 
+#ifdef __has_include
+// std::optional
+#  if __has_include(<optional>)
+#    include <optional>
+#    define PYBIND11_HAS_OPTIONAL 1
+#  endif
+// std::experimental::optional
+#  if __has_include(<experimental/optional>)
+#    include <experimental/optional>
+#    if __cpp_lib_experimental_optional  // just in case
+#      define PYBIND11_HAS_EXP_OPTIONAL 1
+#    endif
+#  endif
+#endif
+
 NAMESPACE_BEGIN(pybind11)
 NAMESPACE_BEGIN(detail)
 
@@ -183,6 +198,47 @@
 template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>>
   : map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { };
 
+// This type caster is intended to be used for std::optional and std::experimental::optional
+template<typename T> struct optional_caster {
+    using value_type = typename intrinsic_type<typename T::value_type>::type;
+    using caster_type = type_caster<value_type>;
+
+    static handle cast(const T& src, return_value_policy policy, handle parent) {
+        if (!src)
+            return none();
+        return caster_type::cast(*src, policy, parent);
+    }
+
+    bool load(handle src, bool convert) {
+        if (!src) {
+            return false;
+        } else if (src.is_none()) {
+            value = {};  // nullopt
+            return true;
+        } else if (!inner.load(src, convert)) {
+            return false;
+        } else {
+            value.emplace(static_cast<const value_type&>(inner));
+            return true;
+        }
+    }
+
+    PYBIND11_TYPE_CASTER(T, _("Optional[") + caster_type::name() + _("]"));
+
+private:
+    caster_type inner;
+};
+
+#if PYBIND11_HAS_OPTIONAL
+template<typename T> struct type_caster<std::optional<T>>
+    : public optional_caster<std::optional<T>> {};
+#endif
+
+#if PYBIND11_HAS_EXP_OPTIONAL
+template<typename T> struct type_caster<std::experimental::optional<T>>
+    : public optional_caster<std::experimental::optional<T>> {};
+#endif
+
 NAMESPACE_END(detail)
 
 inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp
index 678a56d..6a9e3cf 100644
--- a/tests/test_python_types.cpp
+++ b/tests/test_python_types.cpp
@@ -289,4 +289,18 @@
 
         return d;
     });
+
+    // this only tests std::experimental::optional for now
+    bool has_optional = false;
+#ifdef PYBIND11_HAS_EXP_OPTIONAL
+    has_optional = true;
+    using opt_int = std::experimental::optional<int>;
+    m.def("double_or_zero", [](const opt_int& x) -> int {
+        return x.value_or(0) * 2;
+    });
+    m.def("half_or_none", [](int x) -> opt_int {
+        return x ? opt_int(x / 2) : opt_int();
+    });
+#endif
+    m.attr("has_optional") = py::cast(has_optional);
 });
diff --git a/tests/test_python_types.py b/tests/test_python_types.py
index 9bba824..3a8a09e 100644
--- a/tests/test_python_types.py
+++ b/tests/test_python_types.py
@@ -1,6 +1,6 @@
 import pytest
 
-from pybind11_tests import ExamplePythonTypes, ConstructorStats
+from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional
 
 
 def test_static():
@@ -295,3 +295,16 @@
     assert d["set"] == 1
     assert d["deferred_set"] == 1
     assert d["var"] == 99
+
+
+@pytest.mark.skipif(not has_optional, reason='no <experimental/optional>')
+def test_optional():
+    from pybind11_tests import double_or_zero, half_or_none
+
+    assert double_or_zero(None) == 0
+    assert double_or_zero(42) == 84
+    pytest.raises(TypeError, double_or_zero, 'foo')
+
+    assert half_or_none(0) is None
+    assert half_or_none(42) == 21
+    pytest.raises(TypeError, half_or_none, 'foo')