Skip to content

Commit

Permalink
Merge pull request #372 from dean0x7d/keywords
Browse files Browse the repository at this point in the history
Keyword arguments and generalized unpacking for C++ API
  • Loading branch information
wjakob committed Sep 6, 2016
2 parents 6f017cf + 60b2680 commit a3dbdc6
Show file tree
Hide file tree
Showing 16 changed files with 575 additions and 130 deletions.
70 changes: 61 additions & 9 deletions docs/advanced.rst
Expand Up @@ -1622,24 +1622,76 @@ It is also possible to call python functions via ``operator()``.
py::object result_py = f(1234, "hello", some_instance);
MyClass &result = result_py.cast<MyClass>();
The special ``f(*args)`` and ``f(*args, **kwargs)`` syntax is also supported to
supply arbitrary argument and keyword lists, although these cannot be mixed
with other parameters.
Keyword arguments are also supported. In Python, there is the usual call syntax:

.. code-block:: python
def f(number, say, to):
... # function code
f(1234, say="hello", to=some_instance) # keyword call in Python
In C++, the same call can be made using:

.. code-block:: cpp
py::function f = <...>;
using pybind11::literals; // to bring in the `_a` literal
f(1234, "say"_a="hello", "to"_a=some_instance); // keyword call in C++
Unpacking of ``*args`` and ``**kwargs`` is also possible and can be mixed with
other arguments:

.. code-block:: cpp
// * unpacking
py::tuple args = py::make_tuple(1234, "hello", some_instance);
f(*args);
// ** unpacking
py::dict kwargs = py::dict("number"_a=1234, "say"_a="hello", "to"_a=some_instance);
f(**kwargs);
// mixed keywords, * and ** unpacking
py::tuple args = py::make_tuple(1234);
py::dict kwargs;
kwargs["y"] = py::cast(5678);
py::object result = f(*args, **kwargs);
py::dict kwargs = py::dict("to"_a=some_instance);
f(*args, "say"_a="hello", **kwargs);
Generalized unpacking according to PEP448_ is also supported:

.. code-block:: cpp
py::dict kwargs1 = py::dict("number"_a=1234);
py::dict kwargs2 = py::dict("to"_a=some_instance);
f(**kwargs1, "say"_a="hello", **kwargs2);
.. seealso::

The file :file:`tests/test_python_types.cpp` contains a complete
example that demonstrates passing native Python types in more detail. The
file :file:`tests/test_kwargs_and_defaults.cpp` discusses usage
of ``args`` and ``kwargs``.
file :file:`tests/test_callbacks.cpp` presents a few examples of calling
Python functions from C++, including keywords arguments and unpacking.

.. _PEP448: https://www.python.org/dev/peps/pep-0448/

Using Python's print function in C++
====================================

The usual way to write output in C++ is using ``std::cout`` while in Python one
would use ``print``. Since these methods use different buffers, mixing them can
lead to output order issues. To resolve this, pybind11 modules can use the
:func:`py::print` function which writes to Python's ``sys.stdout`` for consistency.

Python's ``print`` function is replicated in the C++ API including optional
keyword arguments ``sep``, ``end``, ``file``, ``flush``. Everything works as
expected in Python:

.. code-block:: cpp
py::print(1, 2.0, "three"); // 1 2.0 three
py::print(1, 2.0, "three", "sep"_a="-"); // 1-2.0-three
auto args = py::make_tuple("unpacked", true);
py::print("->", *args, "end"_a="<-"); // -> unpacked True <-
Default arguments revisited
===========================
Expand Down
7 changes: 7 additions & 0 deletions docs/changelog.rst
Expand Up @@ -46,6 +46,13 @@ Breaking changes queued for v2.0.0 (Not yet released)
* Added constructors for ``str`` and ``bytes`` from zero-terminated char pointers,
and from char pointers and length.
* Added ``memoryview`` wrapper type which is constructible from ``buffer_info``.
* New syntax to call a Python function from C++ using keyword arguments and unpacking,
e.g. ``foo(1, 2, "z"_a=3)`` or ``bar(1, *args, "z"_a=3, **kwargs)``.
* Added ``py::print()`` function which replicates Python's API and writes to Python's
``sys.stdout`` by default (as opposed to C's ``stdout`` like ``std::cout``).
* Added ``py::dict`` keyword constructor:``auto d = dict("number"_a=42, "name"_a="World");``
* Added ``py::str::format()`` method and ``_s`` literal:
``py::str s = "1 + 2 = {}"_s.format(3);``
* Various minor improvements of library internals (no user-visible changes)

1.8.1 (July 12, 2016)
Expand Down
54 changes: 5 additions & 49 deletions include/pybind11/attr.h
Expand Up @@ -14,35 +14,6 @@

NAMESPACE_BEGIN(pybind11)

template <typename T> struct arg_t;

/// Annotation for keyword arguments
struct arg {
constexpr explicit arg(const char *name) : name(name) { }

template <typename T>
constexpr arg_t<T> operator=(const T &value) const { return {name, value}; }
template <typename T, size_t N>
constexpr arg_t<const T *> operator=(T const (&value)[N]) const {
return operator=((const T *) value);
}

const char *name;
};

/// Annotation for keyword arguments with default values
template <typename T> struct arg_t : public arg {
constexpr arg_t(const char *name, const T &value, const char *descr = nullptr)
: arg(name), value(value), descr(descr) { }
T value;
const char *descr;
};

inline namespace literals {
/// String literal version of arg
constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
}

/// Annotation for methods
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };

Expand Down Expand Up @@ -238,21 +209,14 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> {
};

/// Process a keyword argument attribute (*with* a default value)
template <typename T>
struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
static void init(const arg_t<T> &a, function_record *r) {
template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
static void init(const arg_v &a, function_record *r) {
if (r->class_ && r->args.empty())
r->args.emplace_back("self", nullptr, handle());

/* Convert keyword value into a Python object */
object o = object(detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(
a.value, return_value_policy::automatic, handle()), false);

if (!o) {
if (!a.value) {
#if !defined(NDEBUG)
std::string descr(typeid(T).name());
detail::clean_type_id(descr);
descr = "'" + std::string(a.name) + ": " + descr + "'";
auto descr = "'" + std::string(a.name) + ": " + a.type + "'";
if (r->class_) {
if (r->name)
descr += " in method '" + (std::string) r->class_.str() + "." + (std::string) r->name + "'";
Expand All @@ -269,7 +233,7 @@ struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
"Compile in debug mode for more information.");
#endif
}
r->args.emplace_back(a.name, a.descr, o.release());
r->args.emplace_back(a.name, a.descr, a.value.inc_ref());
}
};

Expand Down Expand Up @@ -301,9 +265,6 @@ template <int Nurse, int Patient> struct process_attribute<keep_alive<Nurse, Pat
static void postcall(handle args, handle ret) { keep_alive_impl(Nurse, Patient, args, ret); }
};

/// Ignore that a variable is unused in compiler warnings
inline void ignore_unused(const int *) { }

/// Recursively iterate over variadic template arguments
template <typename... Args> struct process_attributes {
static void init(const Args&... args, function_record *r) {
Expand All @@ -324,11 +285,6 @@ template <typename... Args> struct process_attributes {
}
};

/// Compile-time integer sum
constexpr size_t constexpr_sum() { return 0; }
template <typename T, typename... Ts>
constexpr size_t constexpr_sum(T n, Ts... ns) { return n + constexpr_sum(ns...); }

/// Check the number of named arguments at compile time
template <typename... Extra,
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
Expand Down

0 comments on commit a3dbdc6

Please sign in to comment.