Added py::register_exception for simple case (#296)

The custom exception handling added in PR #273 is robust, but is overly
complex for declaring the most common simple C++ -> Python exception
mapping that needs only to copy `what()`.  This add a simpler
`py::register_exception<CppExp>(module, "PyExp");` function that greatly
simplifies the common basic case of translation of a simple CppException
into a simple PythonException, while not removing the more advanced
capabilities of defining custom exception handlers.
diff --git a/docs/advanced.rst b/docs/advanced.rst
index 1158f05..07901e8 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -1228,56 +1228,82 @@
 is insufficient, pybind11 also provides support for registering custom
 exception translators.
 
-The function ``register_exception_translator(translator)`` takes a stateless
-callable (e.g. a function pointer or a lambda function without captured
-variables) with the following call signature: ``void(std::exception_ptr)``.
-
-When a C++ exception is thrown, registered exception translators are tried
-in reverse order of registration (i.e. the last registered translator gets
-a first shot at handling the exception).
-
-Inside the translator, ``std::rethrow_exception`` should be used within
-a try block to re-throw the exception. A catch clause can then use
-``PyErr_SetString`` to set a Python exception as demonstrated
-in :file:`tests/test_exceptions.cpp`.
-
-This example also demonstrates how to create custom exception types
-with ``py::exception``.
-
-The following example demonstrates this for a hypothetical exception class
-``MyCustomException``:
+To register a simple exception conversion that translates a C++ exception into
+a new Python exception using the C++ exception's ``what()`` method, a helper
+function is available:
 
 .. code-block:: cpp
 
+    py::register_exception<CppExp>(module, "PyExp");
+
+This call creates a Python exception class with the name ``PyExp`` in the given
+module and automatically converts any encountered exceptions of type ``CppExp``
+into Python exceptions of type ``PyExp``.
+
+When more advanced exception translation is needed, the function
+``py::register_exception_translator(translator)`` can be used to register
+functions that can translate arbitrary exception types (and which may include
+additional logic to do so).  The function takes a stateless callable (e.g.  a
+function pointer or a lambda function without captured variables) with the call
+signature ``void(std::exception_ptr)``.
+
+When a C++ exception is thrown, the registered exception translators are tried
+in reverse order of registration (i.e. the last registered translator gets the
+first shot at handling the exception).
+
+Inside the translator, ``std::rethrow_exception`` should be used within
+a try block to re-throw the exception.  One or more catch clauses to catch
+the appropriate exceptions should then be used with each clause using
+``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set
+the python exception to a custom exception type (see below).
+
+To declare a custom Python exception type, declare a ``py::exception`` variable
+and use this in the associated exception translator (note: it is often useful
+to make this a static declaration when using it inside a lambda expression
+without requiring capturing).
+
+
+The following example demonstrates this for a hypothetical exception classes
+``MyCustomException`` and ``OtherException``: the first is translated to a
+custom python exception ``MyCustomError``, while the second is translated to a
+standard python RuntimeError:
+
+.. code-block:: cpp
+
+    static py::exception<MyCustomException> exc(m, "MyCustomError");
     py::register_exception_translator([](std::exception_ptr p) {
         try {
             if (p) std::rethrow_exception(p);
         } catch (const MyCustomException &e) {
+            exc(e.what());
+        } catch (const OtherException &e) {
             PyErr_SetString(PyExc_RuntimeError, e.what());
         }
     });
 
-Multiple exceptions can be handled by a single translator. If the exception is
-not caught by the current translator, the previously registered one gets a
-chance.
+Multiple exceptions can be handled by a single translator, as shown in the
+example above. If the exception is not caught by the current translator, the
+previously registered one gets a chance.
 
 If none of the registered exception translators is able to handle the
 exception, it is handled by the default converter as described in the previous
 section.
 
+.. seealso::
+
+    The file :file:`tests/test_exceptions.cpp` contains examples
+    of various custom exception translators and custom exception types.
+
 .. note::
 
-    You must either call ``PyErr_SetString`` for every exception caught in a
-    custom exception translator. Failure to do so will cause Python to crash
-    with ``SystemError: error return without exception set``.
+    You must call either ``PyErr_SetString`` or a custom exception's call
+    operator (``exc(string)``) for every exception caught in a custom exception
+    translator.  Failure to do so will cause Python to crash with ``SystemError:
+    error return without exception set``.
 
-    Exceptions that you do not plan to handle should simply not be caught.
-
-    You may also choose to explicity (re-)throw the exception to delegate it to
-    the other existing exception translators.
-
-    The ``py::exception`` wrapper for creating custom exceptions cannot (yet)
-    be used as a base type.
+    Exceptions that you do not plan to handle should simply not be caught, or
+    may be explicity (re-)thrown to delegate it to the other,
+    previously-declared existing exception translators.
 
 .. _eigen:
 
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index d523d0d..1468467 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1281,7 +1281,7 @@
 template <typename type>
 class exception : public object {
 public:
-    exception(module &m, const std::string name, PyObject* base=PyExc_Exception) {
+    exception(module &m, const std::string &name, PyObject* base=PyExc_Exception) {
         std::string full_name = std::string(PyModule_GetName(m.ptr()))
                 + std::string(".") + name;
         char* exception_name = const_cast<char*>(full_name.c_str());
@@ -1289,8 +1289,32 @@
         inc_ref(); // PyModule_AddObject() steals a reference
         PyModule_AddObject(m.ptr(), name.c_str(), m_ptr);
     }
+
+    // Sets the current python exception to this exception object with the given message
+    void operator()(const char *message) {
+        PyErr_SetString(m_ptr, message);
+    }
 };
 
+/** Registers a Python exception in `m` of the given `name` and installs an exception translator to
+ * translate the C++ exception to the created Python exception using the exceptions what() method.
+ * This is intended for simple exception translations; for more complex translation, register the
+ * exception object and translator directly.
+ */
+template <typename CppException> exception<CppException>& register_exception(module &m, const std::string &name, PyObject* base = PyExc_Exception) {
+    static exception<CppException> ex(m, name, base);
+    register_exception_translator([](std::exception_ptr p) {
+        if (!p) return;
+        try {
+            std::rethrow_exception(p);
+        }
+        catch (const CppException &e) {
+            ex(e.what());
+        }
+    });
+    return ex;
+}
+
 NAMESPACE_BEGIN(detail)
 PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) {
     auto strings = tuple(args.size());
diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp
index 613b135..ca2afa6 100644
--- a/tests/test_exceptions.cpp
+++ b/tests/test_exceptions.cpp
@@ -46,6 +46,18 @@
     std::string message = "";
 };
 
+
+// Like the above, but declared via the helper function
+class MyException5 : public std::logic_error {
+public:
+    explicit MyException5(const std::string &what) : std::logic_error(what) {}
+};
+
+// Inherits from MyException5
+class MyException5_1 : public MyException5 {
+    using MyException5::MyException5;
+};
+
 void throws1() {
     throw MyException("this error should go to a custom type");
 }
@@ -62,6 +74,14 @@
     throw MyException4("this error is rethrown");
 }
 
+void throws5() {
+    throw MyException5("this is a helper-defined translated exception");
+}
+
+void throws5_1() {
+    throw MyException5_1("MyException5 subclass");
+}
+
 void throws_logic_error() {
     throw std::logic_error("this error should fall through to the standard handler");
 }
@@ -80,7 +100,8 @@
         try {
             if (p) std::rethrow_exception(p);
         } catch (const MyException &e) {
-            PyErr_SetString(ex.ptr(), e.what());
+            // Set MyException as the active python error
+            ex(e.what());
         }
     });
 
@@ -91,6 +112,7 @@
         try {
             if (p) std::rethrow_exception(p);
         } catch (const MyException2 &e) {
+            // Translate this exception to a standard RuntimeError
             PyErr_SetString(PyExc_RuntimeError, e.what());
         }
     });
@@ -106,10 +128,17 @@
         }
     });
 
+    // A simple exception translation:
+    auto ex5 = py::register_exception<MyException5>(m, "MyException5");
+    // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
+    py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
+
     m.def("throws1", &throws1);
     m.def("throws2", &throws2);
     m.def("throws3", &throws3);
     m.def("throws4", &throws4);
+    m.def("throws5", &throws5);
+    m.def("throws5_1", &throws5_1);
     m.def("throws_logic_error", &throws_logic_error);
 
     m.def("throw_already_set", [](bool err) {
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index 4048d43..a9b4b05 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -22,7 +22,8 @@
 
 
 def test_custom(msg):
-    from pybind11_tests import (MyException, throws1, throws2, throws3, throws4,
+    from pybind11_tests import (MyException, MyException5, MyException5_1,
+                                throws1, throws2, throws3, throws4, throws5, throws5_1,
                                 throws_logic_error)
 
     # Can we catch a MyException?"
@@ -49,3 +50,25 @@
     with pytest.raises(RuntimeError) as excinfo:
         throws_logic_error()
     assert msg(excinfo.value) == "this error should fall through to the standard handler"
+
+    # Can we handle a helper-declared exception?
+    with pytest.raises(MyException5) as excinfo:
+        throws5()
+    assert msg(excinfo.value) == "this is a helper-defined translated exception"
+
+    # Exception subclassing:
+    with pytest.raises(MyException5) as excinfo:
+        throws5_1()
+    assert msg(excinfo.value) == "MyException5 subclass"
+    assert isinstance(excinfo.value, MyException5_1)
+
+    with pytest.raises(MyException5_1) as excinfo:
+        throws5_1()
+    assert msg(excinfo.value) == "MyException5 subclass"
+
+    with pytest.raises(MyException5) as excinfo:
+        try:
+            throws5()
+        except MyException5_1 as e:
+            raise RuntimeError("Exception error: caught child from parent")
+    assert msg(excinfo.value) == "this is a helper-defined translated exception"