Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting arguments from kwargs in boost::python

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?

like image 918
orentago Avatar asked May 20 '13 23:05

orentago


People also ask

How do I unpack Kwargs?

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.

How do you pass a Kwargs argument in Python?

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 ** .

How do you get keyword arguments in Python?

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.

What is the correct way to use the Kwargs statement?

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.


1 Answers

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)
like image 81
Tanner Sansbury Avatar answered Oct 05 '22 12:10

Tanner Sansbury