Added pybind11::make_key_iterator for map iteration

This allows exposing a dict-like interface to python code, allowing
iteration over keys via:

    for k in custommapping:
        ...

while still allowing iteration over pairs, so that you can also
implement 'dict.items()' functionality which returns a pair iterator,
allowing:

    for k, v in custommapping.items():
        ...

example-sequences-and-iterators is updated with a custom class providing
both types of iteration.
diff --git a/docs/advanced.rst b/docs/advanced.rst
index 6f57d0b..c68c33a 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -957,6 +957,12 @@
 |                                      | indicate wrong value passed  |
 |                                      | in ``container.remove(...)`` |
 +--------------------------------------+------------------------------+
+| :class:`pybind11::key_error`         | ``KeyError`` (used to        |
+|                                      | indicate out of bounds       |
+|                                      | accesses in ``__getitem__``, |
+|                                      | ``__setitem__`` in dict-like |
+|                                      | objects, etc.)               |
++--------------------------------------+------------------------------+
 | :class:`pybind11::error_already_set` | Indicates that the Python    |
 |                                      | exception flag has already   |
 |                                      | been initialized             |
diff --git a/example/example-sequences-and-iterators.cpp b/example/example-sequences-and-iterators.cpp
index c677f15..791a929 100644
--- a/example/example-sequences-and-iterators.cpp
+++ b/example/example-sequences-and-iterators.cpp
@@ -116,6 +116,34 @@
     float *m_data;
 };
 
+// Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic
+// map-like functionality.
+class StringMap {
+public:
+    StringMap(std::unordered_map<std::string, std::string> init = {})
+        : map(std::move(init)) {}
+
+    void set(std::string key, std::string val) {
+        map[key] = val;
+    }
+
+    std::string get(std::string key) const {
+        return map.at(key);
+    }
+
+    size_t size() const {
+        return map.size();
+    }
+
+private:
+    std::unordered_map<std::string, std::string> map;
+
+public:
+    decltype(map.cbegin()) begin() const { return map.cbegin(); }
+    decltype(map.cend()) end() const { return map.cend(); }
+};
+
+
 void init_ex_sequences_and_iterators(py::module &m) {
     py::class_<Sequence> seq(m, "Sequence");
 
@@ -164,6 +192,25 @@
        .def(py::self != py::self);
        // Could also define py::self + py::self for concatenation, etc.
 
+    py::class_<StringMap> map(m, "StringMap");
+
+    map .def(py::init<>())
+        .def(py::init<std::unordered_map<std::string, std::string>>())
+        .def("__getitem__", [](const StringMap &map, std::string key) {
+                try { return map.get(key); }
+                catch (const std::out_of_range&) {
+                    throw py::key_error("key '" + key + "' does not exist");
+                }
+                })
+        .def("__setitem__", &StringMap::set)
+        .def("__len__", &StringMap::size)
+        .def("__iter__", [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); },
+                py::keep_alive<0, 1>())
+        .def("items", [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); },
+                py::keep_alive<0, 1>())
+        ;
+
+
 #if 0
     // Obsolete: special data structure for exposing custom iterator types to python
     // kept here for illustrative purposes because there might be some use cases which
diff --git a/example/example-sequences-and-iterators.py b/example/example-sequences-and-iterators.py
index 69ec84e..764a527 100755
--- a/example/example-sequences-and-iterators.py
+++ b/example/example-sequences-and-iterators.py
@@ -3,7 +3,7 @@
 import sys
 sys.path.append('.')
 
-from example import Sequence
+from example import Sequence, StringMap
 
 s = Sequence(5)
 print("s = " + str(s))
@@ -29,6 +29,24 @@
     print(i, end=' ')
 print('')
 
+m = StringMap({ 'hi': 'bye', 'black': 'white' })
+print(m['hi'])
+print(len(m))
+print(m['black'])
+try:
+    print(m['orange'])
+    print('Error: should have thrown exception')
+except KeyError:
+    pass
+m['orange'] = 'banana'
+print(m['orange'])
+
+for k in m:
+    print("key = %s, value = %s" % (k, m[k]))
+
+for k,v in m.items():
+    print("item: (%s, %s)" % (k,v))
+
 from example import ConstructorStats
 cstats = ConstructorStats.get(Sequence)
 print("Instances not destroyed:", cstats.alive())
diff --git a/example/example-sequences-and-iterators.ref b/example/example-sequences-and-iterators.ref
index 909a93a..d658fba 100644
--- a/example/example-sequences-and-iterators.ref
+++ b/example/example-sequences-and-iterators.ref
@@ -13,9 +13,19 @@
 0.0 56.779998779296875 0.0 0.0 12.34000015258789 
 0.0 56.779998779296875 0.0 0.0 12.34000015258789 
 True
-### Sequence @ 0x153c4b0 created of size 3  from std::vector
-### Sequence @ 0x153c4b0 destroyed
+### Sequence @ 0x1b4d1f0 created of size 3 from std::vector
+### Sequence @ 0x1b4d1f0 destroyed
 2.0 56.779998779296875 2.0 0.0 2.0 
+bye
+2
+white
+banana
+key = orange, value = banana
+key = hi, value = bye
+key = black, value = white
+item: (orange, banana)
+item: (hi, bye)
+item: (black, white)
 Instances not destroyed: 3
 ### Sequence @ 0x1535b00 destroyed
 Instances not destroyed: 2
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index cd12866..185c943 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -55,6 +55,7 @@
                     if (p) std::rethrow_exception(p);
                 } catch (const error_already_set &)      {                                                 return;
                 } catch (const index_error &e)           { PyErr_SetString(PyExc_IndexError,    e.what()); return;
+                } catch (const key_error &e)             { PyErr_SetString(PyExc_KeyError,      e.what()); return;
                 } catch (const value_error &e)           { PyErr_SetString(PyExc_ValueError,    e.what()); return;
                 } catch (const stop_iteration &e)        { PyErr_SetString(PyExc_StopIteration, e.what()); return;
                 } catch (const std::bad_alloc &e)        { PyErr_SetString(PyExc_MemoryError,   e.what()); return;
diff --git a/include/pybind11/common.h b/include/pybind11/common.h
index 6a08116..0b20929 100644
--- a/include/pybind11/common.h
+++ b/include/pybind11/common.h
@@ -314,6 +314,7 @@
 class error_already_set : public std::runtime_error { public: error_already_set() : std::runtime_error(detail::error_string())  {} };
 PYBIND11_RUNTIME_EXCEPTION(stop_iteration)
 PYBIND11_RUNTIME_EXCEPTION(index_error)
+PYBIND11_RUNTIME_EXCEPTION(key_error)
 PYBIND11_RUNTIME_EXCEPTION(value_error)
 PYBIND11_RUNTIME_EXCEPTION(cast_error) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
 PYBIND11_RUNTIME_EXCEPTION(reference_cast_error) /// Used internally
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index ebd6f6f..427b12c 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1117,7 +1117,7 @@
     keep_alive_impl(nurse, patient);
 }
 
-template <typename Iterator> struct iterator_state {
+template <typename Iterator, bool KeyIterator = false> struct iterator_state {
     Iterator it, end;
     bool first;
 };
@@ -1148,11 +1148,37 @@
 
     return (iterator) cast(state { first, last, true });
 }
+template <typename Iterator,
+          typename KeyType = decltype(std::declval<Iterator>()->first),
+          typename... Extra>
+iterator make_key_iterator(Iterator first, Iterator last, Extra &&... extra) {
+    typedef detail::iterator_state<Iterator, true> state;
+
+    if (!detail::get_type_info(typeid(state))) {
+        class_<state>(handle(), "")
+            .def("__iter__", [](state &s) -> state& { return s; })
+            .def("__next__", [](state &s) -> KeyType {
+                if (!s.first)
+                    ++s.it;
+                else
+                    s.first = false;
+                if (s.it == s.end)
+                    throw stop_iteration();
+                return s.it->first;
+            }, return_value_policy::reference_internal, std::forward<Extra>(extra)...);
+    }
+
+    return (iterator) cast(state { first, last, true });
+}
 
 template <typename Type, typename... Extra> iterator make_iterator(Type &value, Extra&&... extra) {
     return make_iterator(std::begin(value), std::end(value), extra...);
 }
 
+template <typename Type, typename... Extra> iterator make_key_iterator(Type &value, Extra&&... extra) {
+    return make_key_iterator(std::begin(value), std::end(value), extra...);
+}
+
 template <typename InputType, typename OutputType> void implicitly_convertible() {
     auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
         if (!detail::type_caster<InputType>().load(obj, false))