keyword argument support, removed last traces of std::function<> usage
diff --git a/include/pybind/pybind.h b/include/pybind/pybind.h
index 4cfaab3..64ae5a0 100644
--- a/include/pybind/pybind.h
+++ b/include/pybind/pybind.h
@@ -76,26 +76,82 @@
     template <typename... T> using arg_value_caster =
         detail::type_caster<typename std::tuple<T...>>;
 
-
-    template <typename... T> void process_args(const std::tuple<T...> &args, function_entry *entry) {
-        process_args(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
+    template <typename... T> static void process_extras(const std::tuple<T...> &args,
+            function_entry *entry, const char **kw, const char **def) {
+        process_extras(args, entry, kw, def, typename detail::make_index_sequence<sizeof...(T)>::type());
     }
 
-    template <typename... T, size_t ... Index> void process_args(const
-            std::tuple<T...> &args, function_entry *entry,
-            detail::index_sequence<Index...>) {
-        int unused[] = { 0, (process_arg(std::get<Index>(args), entry), 0)... };
+    template <typename... T, size_t ... Index> static void process_extras(const std::tuple<T...> &args,
+            function_entry *entry, const char **kw, const char **def, detail::index_sequence<Index...>) {
+        int unused[] = { 0, (process_extra(std::get<Index>(args), entry, kw, def), 0)... };
         (void) unused;
     }
 
-    void process_arg(const char *doc, function_entry *entry) { entry->doc = doc; }
-    void process_arg(const pybind::doc &d, function_entry *entry) { entry->doc = d.value; }
-    void process_arg(const pybind::name &n, function_entry *entry) { entry->name = n.value; }
-    void process_arg(const pybind::arg &, function_entry *entry) { entry->keywords++; }
-    void process_arg(const pybind::is_method &, function_entry *entry) { entry->is_method = true; }
-    void process_arg(const pybind::return_value_policy p, function_entry *entry) { entry->policy = p; }
-    void process_arg(pybind::sibling s, function_entry *entry) { entry->sibling = s.value; }
+    template <typename... T> static void process_extras(const std::tuple<T...> &args,
+            PyObject *pyArgs, PyObject *kwargs, bool is_method) {
+        process_extras(args, pyArgs, kwargs, is_method, typename detail::make_index_sequence<sizeof...(T)>::type());
+    }
 
+    template <typename... T, size_t... Index> static void process_extras(const std::tuple<T...> &args,
+            PyObject *pyArgs, PyObject *kwargs, bool is_method, detail::index_sequence<Index...>) {
+        int index = is_method ? 1 : 0;
+        int unused[] = { 0, (process_extra(std::get<Index>(args), index, pyArgs, kwargs), 0)... };
+        (void) unused;
+    }
+
+    static void process_extra(const char *doc, function_entry *entry, const char **, const char **) { entry->doc = doc; }
+    static void process_extra(const pybind::doc &d, function_entry *entry, const char **, const char **) { entry->doc = d.value; }
+    static void process_extra(const pybind::name &n, function_entry *entry, const char **, const char **) { entry->name = n.value; }
+    static void process_extra(const pybind::arg &a, function_entry *entry, const char **kw, const char **) {
+        if (entry->is_method && entry->keywords == 0)
+            kw[entry->keywords++] = "self";
+        kw[entry->keywords++] = a.name;
+    }
+    template <typename T>
+    static void process_extra(const pybind::arg_t<T> &a, function_entry *entry, const char **kw, const char **def) {
+        if (entry->is_method && entry->keywords == 0)
+            kw[entry->keywords++] = "self";
+        kw[entry->keywords] = a.name;
+        def[entry->keywords++] = strdup(std::to_string(a.value).c_str());
+    }
+
+    static void process_extra(const pybind::is_method &, function_entry *entry, const char **, const char **) { entry->is_method = true; }
+    static void process_extra(const pybind::return_value_policy p, function_entry *entry, const char **, const char **) { entry->policy = p; }
+    static void process_extra(pybind::sibling s, function_entry *entry, const char **, const char **) { entry->sibling = s.value; }
+
+    template <typename T> static void process_extra(T, int &, PyObject *, PyObject *) { }
+    static void process_extra(const pybind::arg &a, int &index, PyObject *args, PyObject *kwargs) {
+        if (kwargs) {
+            if (PyTuple_GET_ITEM(args, index) != nullptr) {
+                index++;
+                return;
+            }
+            PyObject *value = PyDict_GetItemString(kwargs, a.name);
+            if (value) {
+                Py_INCREF(value);
+                PyTuple_SetItem(args, index, value);
+            }
+        }
+        index++;
+    }
+    template <typename T>
+    static void process_extra(const pybind::arg_t<T> &a, int &index, PyObject *args, PyObject *kwargs) {
+        if (PyTuple_GET_ITEM(args, index) != nullptr) {
+            index++;
+            return;
+        }
+        PyObject *value = nullptr;
+        if (kwargs)
+            value = PyDict_GetItemString(kwargs, a.name);
+        if (value) {
+            Py_INCREF(value);
+        } else {
+            value = detail::type_caster<typename detail::decay<T>::type>::cast(
+                a.value, return_value_policy::automatic, nullptr);
+        }
+        PyTuple_SetItem(args, index, value);
+        index++;
+    }
 public:
     cpp_function() { }
 
@@ -104,7 +160,7 @@
     cpp_function(Return (*f)(Arg...), Extra&&... extra) {
         struct capture {
             Return (*f)(Arg...);
-            std::tuple<Extra...> extra;
+            std::tuple<Extra...> extras;
         };
 
         function_entry *entry = new function_entry();
@@ -114,18 +170,23 @@
         typedef return_value_caster<Return> cast_out;
 
         entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * {
+            capture *data = (capture *) entry->data;
+            process_extras(data->extras, pyArgs, kwargs, entry->is_method);
             cast_in args;
-            if (!args.load(pyArgs, true)) return nullptr;
-            auto f = ((capture *) entry->data)->f;
-            (void)kwargs;
-            return cast_out::cast(args.template call<Return>(f), entry->policy, parent);
+            if (!args.load(pyArgs, true))
+                return nullptr;
+            return cast_out::cast(args.template call<Return>(data->f), entry->policy, parent);
         };
 
-        entry->signature = cast_in::name();
+        const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg);
+        std::array<const char *, N> kw{}, def{};
+        process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data());
+
+        entry->signature = cast_in::name(kw.data(), def.data());
         entry->signature += " -> ";
         entry->signature += cast_out::name();
-        process_args(((capture *) entry->data)->extra, entry);
-        initialize(entry);
+
+        initialize(entry, sizeof...(Arg));
     }
 
     /// Delegating helper constructor to deal with lambda functions
@@ -136,7 +197,6 @@
                    std::forward<Extra>(extra)...);
     }
 
-
     /// Class methods (non-const)
     template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
             Return (Class::*f)(Arg...), Extra&&... extra) {
@@ -157,7 +217,7 @@
     void initialize(Func &&f, Return (*)(Arg...), Extra&&... extra) {
         struct capture {
             typename std::remove_reference<Func>::type f;
-            std::tuple<Extra...> extra;
+            std::tuple<Extra...> extras;
         };
 
         function_entry *entry = new function_entry();
@@ -167,27 +227,50 @@
         typedef return_value_caster<Return> cast_out;
 
         entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{
+            capture *data = (capture *)entry->data;
+            process_extras(data->extras, pyArgs, kwargs, entry->is_method);
             cast_in args;
-            if (!args.load(pyArgs, true)) return nullptr;
-            Func &f = ((capture *) entry->data)->f;
-            (void)kwargs;
-            return cast_out::cast(args.template call<Return>(f), entry->policy, parent);
+            if (!args.load(pyArgs, true))
+                return nullptr;
+            return cast_out::cast(args.template call<Return>(data->f), entry->policy, parent);
         };
 
-        entry->signature = cast_in::name();
+        const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg);
+        std::array<const char *, N> kw{}, def{};
+        process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data());
+
+        entry->signature = cast_in::name(kw.data(), def.data());
         entry->signature += " -> ";
         entry->signature += cast_out::name();
-        process_args(((capture *) entry->data)->extra, entry);
-        initialize(entry);
+
+        initialize(entry, sizeof...(Arg));
     }
 
     static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs ) {
         function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr);
+        int nargs = (int) PyTuple_Size(args);
         PyObject *result = nullptr;
-        PyObject *parent = PyTuple_Size(args) > 0 ? PyTuple_GetItem(args, 0) : nullptr;
+        PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr;
         try {
             for (function_entry *it = overloads; it != nullptr; it = it->next) {
-                if ((result = it->impl(it, args, kwargs, parent)) != nullptr)
+                PyObject *args_ = args;
+
+                if (it->keywords != 0 && it->keywords != nargs) {
+                    args_ = PyTuple_New(it->keywords);
+                    for (int i=0; i<nargs; ++i) {
+                        PyObject *item = PyTuple_GET_ITEM(args, i);
+                        Py_INCREF(item);
+                        PyTuple_SET_ITEM(args_, i, item);
+                    }
+                }
+
+                result = it->impl(it, args_, kwargs, parent);
+
+                if (args_ != args) {
+                    Py_DECREF(args_);
+                }
+
+                if (result != nullptr)
                     break;
             }
         } catch (const error_already_set &) {                                               return nullptr;
@@ -221,9 +304,14 @@
         }
     }
 
-    void initialize(function_entry *entry) {
+    void initialize(function_entry *entry, int args) {
         if (entry->name == nullptr)
             entry->name = "";
+        if (entry->keywords != 0 && entry->keywords != args)
+            throw std::runtime_error(
+                "cpp_function(): function \"" + std::string(entry->name) + "\" takes " +
+                std::to_string(args) + " arguments, but " + std::to_string(entry->keywords) +
+                " pybind::arg entries were specified!");
 
         entry->is_constructor = !strcmp(entry->name, "__init__");
 
@@ -578,32 +666,32 @@
         return *this;
     }
 
-    class_ &def_property_readonly(const char *name, const cpp_function &fget) {
-        def_property(name, fget, cpp_function());
+    class_ &def_property_readonly(const char *name, const cpp_function &fget, const char *doc = nullptr) {
+        def_property(name, fget, cpp_function(), doc);
         return *this;
     }
 
-    class_ &def_property_readonly_static(const char *name, const cpp_function &fget) {
-        def_property_static(name, fget, cpp_function());
+    class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const char *doc = nullptr) {
+        def_property_static(name, fget, cpp_function(), doc);
         return *this;
     }
 
-    class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset) {
+    class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
+        object doc_obj = doc ? pybind::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
         object property(
             PyObject_CallFunction((PyObject *)&PyProperty_Type,
                                   const_cast<char *>("OOOO"), fget.ptr() ? fget.ptr() : Py_None,
-                                  fset.ptr() ? fset.ptr() : Py_None, Py_None,
-                                  ((object) const_cast<cpp_function&>(fget).attr("__doc__")).ptr()), false);
+                                  fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr()), false);
         attr(name) = property;
         return *this;
     }
 
-    class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset) {
+    class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
+        object doc_obj = doc ? pybind::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
         object property(
             PyObject_CallFunction((PyObject *)&PyProperty_Type,
-                                  const_cast<char *>("OOOO"), fget.ptr() ? fget.ptr() : Py_None,
-                                  fset.ptr() ? fset.ptr() : Py_None, Py_None,
-                                  ((object) const_cast<cpp_function&>(fget).attr("__doc__")).ptr()), false);
+                                  const_cast<char *>("OOOs"), fget.ptr() ? fget.ptr() : Py_None,
+                                  fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr()), false);
         metaclass().attr(name) = property;
         return *this;
     }