I have a C++ class that I'm building into a python module using boost::python. I have a few functions that I want to take keyword arguments. I've set up wrapper functions to pass to raw_arguments and that works fine, but I want to build in some error checking for the function arguments. Is there a standard way to do this?
My function prototype, in C++, looks a bit like this:
double MyClass::myFunction(int a, int b, int c);
The third argument is optional, with a default value of 0 (I've implemented this in boost::python using macros up till now). In python, I want to be able to achieve the following behaviour:
MyClass.my_function(1) # Raises exception
MyClass.my_function(2, 3) # So that a = 2, b = 3 and c defaults to 0
MyClass.my_function(2, 3, 1) # As above, but now c = 1
MyClass.my_function(2, 3, 1, 3) # Raises exception
MyClass.my_function(3, 1, c = 2) # So a = 3, b = 1 and c = 2
MyClass.my_function(a = 2, b = 2, c = 3) # Speaks for itself
MyClass.my_function(b = 2, c = 1) # Raises exception
Is there something in boost::python or the raw_function wrapper that can facilitate this, or do I need to write the code to check all this myself? If I do need to, how can I raise the exceptions? Is there a standard way of doing this?
Unpacking kwargs and dictionaries You cannot directly send a dictionary as a parameter to a function accepting kwargs. The dictionary must be unpacked so that the function may make use of its elements. This is done by unpacking the dictionary, by placing ** before the dictionary name as you pass it into the function.
Python **kwargs In the function, we use the double asterisk ** before the parameter name to denote this type of argument. The arguments are passed as a dictionary and these arguments make a dictionary inside function with name same as the parameter excluding double asterisk ** .
Embrace keyword arguments in Python Consider using the * operator to require those arguments be specified as keyword arguments. And remember that you can accept arbitrary keyword arguments to the functions you define and pass arbitrary keyword arguments to the functions you call by using the ** operator.
Understanding **kwargs The double asterisk form of **kwargs is used to pass a keyworded, variable-length argument dictionary to a function. Again, the two asterisks ( ** ) are the important element here, as the word kwargs is conventionally used, though not enforced by the language.
The boost/python/args.hpp
file provides a family of classes for specifying argument keywords. In particular, Boost.Python provides an arg
type, that represents a potential keyword argument. It overloads the comma operator to allow for a more natural definition of an argument list.
Exposing myFunction
on MyClass
as my_function
, where a
, b
, and c
are keyword arguments, and c
has a default value of 0
could be written as follows:
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<MyClass>("MyClass")
.def("my_function", &MyClass::myFunction,
(python::arg("a"), "b", python::arg("c")=0))
;
}
Here is a complete example:
#include <boost/python.hpp>
class MyClass
{
public:
double myFunction(int a, int b, int c)
{
return a + b + c;
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<MyClass>("MyClass")
.def("my_function", &MyClass::myFunction,
(python::arg("a"), "b", python::arg("c")=0))
;
}
Interactive Usage:
>>> import example
>>> my_class = example.MyClass()
>>> my_class.my_function(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
MyClass.my_function(MyClass, int)
did not match C++ signature:
my_function(MyClass {lvalue}, int a, int b, int c=0)
>>> assert(5 == my_class.my_function(2, 3))
>>> assert(6 == my_class.my_function(2, 3, 1))
>>> my_class.my_function(2, 3, 1, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
MyClass.my_function(MyClass, int, int, int, int)
did not match C++ signature:
my_function(MyClass {lvalue}, int a, int b, int c=0)
>>> assert(6 == my_class.my_function(3, 1, c=2))
>>> assert(7 == my_class.my_function(a=2, b=2, c=3))
>>> my_class.my_function(b=2, c=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
MyClass.my_function(MyClass)
did not match C++ signature:
my_function(MyClass {lvalue}, int a, int b, int c=0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With