How to set just one default argument pybind11

ghz 12hours ago ⋅ 1 views

I have a function :

void my_functions(int a, int b = 42);

And I want to bind it using only the default argument for b:

 m.def("my_functions", &my_functions, pb::arg("b") = 42); // just default for b

This doesn't work, I get:

/cache/venv/include/pybind11/pybind11.h:219:40: error: static assertion failed: The number of argument annotations does not match the number of function arguments
  219 |             expected_num_args<Extra...>(
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~^
  220 |                 sizeof...(Args), cast_in::args_pos >= 0, cast_in::has_kwargs),

What's the right way of doing it?

Answer

The issue you are encountering arises because pybind11 expects the number of arguments to match the function's signature when you define the function binding. In your case, you want to specify the default argument only for b, but the function signature still requires a to be passed in when calling the function.

The correct way to bind a function with default arguments in pybind11 is to use py::arg for the arguments that you want to explicitly specify, and for the default arguments, simply don't add them in the py::arg list. pybind11 will automatically apply the default values based on the C++ function signature.

Here’s how you can correctly bind your function my_functions with the default value for b:

C++ function:

void my_functions(int a, int b = 42);

Pybind11 binding:

m.def("my_functions", &my_functions, py::arg("a"), py::arg("b") = 42);

Explanation:

  • py::arg("a") explicitly binds the first argument a.
  • py::arg("b") = 42 binds the second argument b and provides its default value of 42. This means if b is not passed when calling the Python function, it will automatically use 42 as the default value.

You don't need to specify the default for b manually in the m.def call — pybind11 will handle it correctly by looking at the default argument in the C++ function signature.

Final Example:

#include <pybind11/pybind11.h>

namespace py = pybind11;

void my_functions(int a, int b = 42) {
    // Your function implementation
    std::cout << "a: " << a << ", b: " << b << std::endl;
}

PYBIND11_MODULE(example, m) {
    m.def("my_functions", &my_functions, py::arg("a"), py::arg("b") = 42);
}

In Python, you can now call the function with either one or two arguments:

import example

example.my_functions(10)  # Uses the default for `b`, prints: "a: 10, b: 42"
example.my_functions(10, 20)  # Uses 20 for `b`, prints: "a: 10, b: 20"

This approach should solve the error and work correctly.