Functions#

There are numerous generations options that can be set in order to change function bindings options.

See options.py: all the function related options begin wth fn_ or fn_params (when they deal with function parameters)

Exclude functions and/or params#

Extract from options.py, showing the related options:

    ################################################################################
    #    <functions and method adaptations>
    ################################################################################

    # Exclude certain functions and methods by a regex on their name
    fn_exclude_by_name__regex: str = ""

    # Exclude certain functions and methods by a regex on any of their parameter type and/or return type
    # (those should be decorated type)
    # For example:
    #     options.fn_exclude_by_param_type__regex = "^char\s*$|^unsigned\s+char$|Callback$"
    # would exclude all functions having params of type "char *", "unsigned char", "xxxCallback"
    #
    # Note: this is distinct from `fn_params_exclude_types__regex` which removes params
    # from the function signature, but not the function itself.
    fn_exclude_by_param_type__regex: str = ""

    # ------------------------------------------------------------------------------
    # Exclude some params by name or type
    # ------------------------------------------------------------------------------
    # Remove some params from the python published interface. A param can only be removed if it has a default value
    # in the C++ signature
    fn_params_exclude_names__regex: str = ""
    fn_params_exclude_types__regex: str = ""

As an example, let’s consider the code below, where we would want to:

  • exclude all functions beginning with “priv_”

  • exclude a function parameter if its type name starts with “Private”

import litgen
from litgen.demo import litgen_demo

cpp_code = """
void priv_SetOptions(bool v);

void SetOptions(const PublicOptions& options, const PrivateOptions& privateOptions = PrivateOptions());
"""

By default the generated code will be:

options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
void priv_SetOptions(bool v);

void SetOptions(const PublicOptions& options, const PrivateOptions& privateOptions = PrivateOptions());
def priv_set_options(v: bool) -> None:
    pass

def set_options(
    options: PublicOptions,
    private_options: PrivateOptions = PrivateOptions()
    ) -> None:
    pass


m.def("priv_set_options",
    priv_SetOptions, py::arg("v"));

m.def("set_options",
    SetOptions, py::arg("options"), py::arg("private_options") = PrivateOptions());

But we can set some options to change this.

In the generated code below, look closely at the C++ binding code: you will see that it takes steps to generate a default value for the parameter of type PrivateOptions

options = litgen.LitgenOptions()
options.fn_exclude_by_name__regex = "^priv_"  # Exclude functions whose name begin by "priv_"
options.fn_params_exclude_types__regex = "Private"  # Exclude functions params whose type name contains "Private"

litgen_demo.demo(options, cpp_code, show_pydef=True)
void priv_SetOptions(bool v);

void SetOptions(const PublicOptions& options, const PrivateOptions& privateOptions = PrivateOptions());
def set_options(options: PublicOptions) -> None:
    pass


m.def("set_options",
    [](const PublicOptions & options)
    {
        auto SetOptions_adapt_exclude_params = [](const PublicOptions & options)
        {
            SetOptions(options, PrivateOptions());
        };

        SetOptions_adapt_exclude_params(options);
    },     py::arg("options"));

Return policy#

See relevant doc from pybind11:

Python and C++ use fundamentally different ways of managing the memory and lifetime of objects managed by them. This can lead to issues when creating bindings for functions that return a non-trivial type. Just by looking at the type information, it is not clear whether Python should take charge of the returned value and eventually free its resources, or if this is handled on the C++ side. For this reason, pybind11 provides a several return value policy annotations that can be passed to the module_::def() and class_::def() functions. The default policy is return_value_policy::automatic.

return_value_policy::reference#

In the C++ code below, let’s suppose that C++ is responsible for handling the destruction of the values returned by MakeWidget and MakeFoo: we do not want python to call the destructor automatically.

cpp_code = """
Widget * MakeWidget();
Foo& MakeFoo();
"""

In that case, we can set options.fn_return_force_policy_reference_for_pointers__regex and/or options.fn_return_force_policy_reference_for_references__regex, and the generated pydef binding code, will set the correct return value policy.

options = litgen.LitgenOptions()
options.fn_return_force_policy_reference_for_pointers__regex = r"^Make"
options.fn_return_force_policy_reference_for_references__regex = r"^Make"
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.pydef_code)
m.def("make_widget",
    MakeWidget, pybind11::return_value_policy::reference);

m.def("make_foo",
    MakeFoo, pybind11::return_value_policy::reference);

Custom return value policy#

If you annotate the function declaration with return_value_policy::..., then the generator will use this information:

cpp_code = """
Widget *MakeWidget(); // return_value_policy::take_ownership
"""

options = litgen.LitgenOptions()
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.pydef_code)
m.def("make_widget",
    MakeWidget, "return_value_policy::take_ownership");

Modifiable immutable function params#

Some C++ functions may use a modifiable input/output parameter, for which the corresponding type in python is immutable (e.g. its is a numeric type, or a string).

For example, in the C++ code below, the param inOutFlag is modified by the function.

void SwitchBool(bool* inOutFlag);

In python, a function with the following signature can not change its parameter value, since bool is immutable:

def switch_bool(in_out_v: bool) -> None:
    pass

litgen offers two ways to handle those situations:

  • by using boxed types

  • by adding the modified value to the function output

Using boxed types#

You can decide to replace this kind of parameters type by a “Boxed” type: this is a simple class that encapsulates the value, and makes it modifiable.

Look at the example below where a BoxedBool is created:

  • its python signature is given in the stub

  • its C++ declaration is given in the glue code

  • the pybind11 C++ binding code handle the conversion between bool * and BoxedBool

cpp_code = "void SwitchBool(bool* inOutFlag);"
options = litgen.LitgenOptions()
options.fn_params_replace_modifiable_immutable_by_boxed__regex = r".*"  # "Box" all modifiable immutable parameters
litgen_demo.demo(options, cpp_code, show_pydef=True)
void SwitchBool(bool* inOutFlag);
####################    <generated_from:BoxedTypes>    ####################
class BoxedBool:
    value: bool
    def __init__(self, v: bool = False) -> None:
        pass
    def __repr__(self) -> str:
        pass
####################    </generated_from:BoxedTypes>    ####################


def switch_bool(in_out_flag: BoxedBool) -> None:
    pass


////////////////////    <generated_from:BoxedTypes>    ////////////////////
auto pyClassBoxedBool =
    py::class_<BoxedBool>
        (m, "BoxedBool", "")
    .def_readwrite("value", &BoxedBool::value, "")
    .def(py::init<bool>(),
        py::arg("v") = false)
    .def("__repr__",
        &BoxedBool::__repr__)
    ;
////////////////////    </generated_from:BoxedTypes>    ////////////////////


m.def("switch_bool",
    [](BoxedBool & inOutFlag)
    {
        auto SwitchBool_adapt_modifiable_immutable = [](BoxedBool & inOutFlag)
        {
            bool * inOutFlag_boxed_value = & (inOutFlag.value);

            SwitchBool(inOutFlag_boxed_value);
        };

        SwitchBool_adapt_modifiable_immutable(inOutFlag);
    },     py::arg("in_out_flag"));

struct BoxedBool
{
    bool value;
    BoxedBool(bool v = false) : value(v) {}
    std::string __repr__() const { return std::string("BoxedBool(") + std::to_string(value) + ")"; }
};

Adding the modified value to the function output#

Let’s say that we have a C++ function that modifies a string, and returns a bool that indicates whether it was modified:

bool UserInputString(std::string* inOutStr);

We can ask litgen to add the modified string to the output of the function.

Look at the example below:

  • the python function returns a Tuple[bool, str]

  • the pybind11 binding adds a lambda that does the necessary transformation

cpp_code = "bool UserInputString(std::string* inOutStr);"
options = litgen.LitgenOptions()
options.fn_params_output_modifiable_immutable_to_return__regex = r".*"
litgen_demo.demo(options, cpp_code, show_pydef=True)
bool UserInputString(std::string* inOutStr);
def user_input_string(in_out_str: str) -> Tuple[bool, str]:
    pass


m.def("user_input_string",
    [](std::string inOutStr) -> std::tuple<bool, std::string>
    {
        auto UserInputString_adapt_modifiable_immutable_to_return = [](std::string inOutStr) -> std::tuple<bool, std::string>
        {
            std::string * inOutStr_adapt_modifiable = & inOutStr;

            bool r = UserInputString(inOutStr_adapt_modifiable);
            return std::make_tuple(r, inOutStr);
        };

        return UserInputString_adapt_modifiable_immutable_to_return(inOutStr);
    },     py::arg("in_out_str"));

C style function params#

Immutable C array param#

If a function uses a param whose type is const SomeType[N], then it will be translated automatically, and the pybind11 binding code will handle the necessary transformations.

cpp_code = "void foo(const int v[2]);"
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code, show_pydef=True)
void foo(const int v[2]);
def foo(v: List[int]) -> None:
    pass


m.def("foo",
    [](const std::array<int, 2>& v)
    {
        auto foo_adapt_fixed_size_c_arrays = [](const std::array<int, 2>& v)
        {
            foo(v.data());
        };

        foo_adapt_fixed_size_c_arrays(v);
    },     py::arg("v"));

Modifiable C array param#

If a function uses a param whose type is SomeType[N] v, then litgen will understand that any value inside v can be modified, and it will emit code where a C++ signature like this:

void foo(int v[2]);

is transformed into python:

def foo(v_0: BoxedInt, v_1: BoxedInt) -> None:
    pass
cpp_code = "void foo(int v[2]);"
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
void foo(int v[2]);
####################    <generated_from:BoxedTypes>    ####################
class BoxedInt:
    value: int
    def __init__(self, v: int = 0) -> None:
        pass
    def __repr__(self) -> str:
        pass
####################    </generated_from:BoxedTypes>    ####################


def foo(v_0: BoxedInt, v_1: BoxedInt) -> None:
    pass


////////////////////    <generated_from:BoxedTypes>    ////////////////////
auto pyClassBoxedInt =
    py::class_<BoxedInt>
        (m, "BoxedInt", "")
    .def_readwrite("value", &BoxedInt::value, "")
    .def(py::init<int>(),
        py::arg("v") = 0)
    .def("__repr__",
        &BoxedInt::__repr__)
    ;
////////////////////    </generated_from:BoxedTypes>    ////////////////////


m.def("foo",
    [](BoxedInt & v_0, BoxedInt & v_1)
    {
        auto foo_adapt_fixed_size_c_arrays = [](BoxedInt & v_0, BoxedInt & v_1)
        {
            int v_raw[2];
            v_raw[0] = v_0.value;
            v_raw[1] = v_1.value;

            foo(v_raw);

            v_0.value = v_raw[0];
            v_1.value = v_raw[1];
        };

        foo_adapt_fixed_size_c_arrays(v_0, v_1);
    },     py::arg("v_0"), py::arg("v_1"));

struct BoxedInt
{
    int value;
    BoxedInt(int v = 0) : value(v) {}
    std::string __repr__() const { return std::string("BoxedInt(") + std::to_string(value) + ")"; }
};

C style string list#

If a pair of function params look like const char * const items[], int item_count, it will be transformed into a python List[str]:

cpp_code = "void PrintItems(const char * const items[], int item_count);"
options = litgen.LitgenOptions()
options.fn_params_replace_c_string_list__regex = r".*"  # apply to all function names (this is the default!)
litgen_demo.demo(options, cpp_code)
void PrintItems(const char * const items[], int item_count);
def print_items(items: List[str]) -> None:
    pass


m.def("print_items",
    [](const std::vector<std::string> & items)
    {
        auto PrintItems_adapt_c_string_list = [](const std::vector<std::string> & items)
        {
            std::vector<const char *> items_ptrs;
            for (const auto& v: items)
                items_ptrs.push_back(v.c_str());
            int item_count = static_cast<int>(items.size());

            PrintItems(items_ptrs.data(), item_count);
        };

        PrintItems_adapt_c_string_list(items);
    },     py::arg("items"));

C style variadic string format#

If a function uses a pair of parameters like char const* const format, ..., then litgen will transform it into a simple python string.

cpp_code = "void Log(LogLevel level, char const* const format, ...);"
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
void Log(LogLevel level, char const* const format, ...);
def log(level: LogLevel, format: str) -> None:
    pass


m.def("log",
    [](LogLevel level, const char * const format)
    {
        auto Log_adapt_variadic_format = [](LogLevel level, const char * const format)
        {
            Log(level, "%s", format);
        };

        Log_adapt_variadic_format(level, format);
    },     py::arg("level"), py::arg("format"));

Passing numeric buffers to numpy#

Simple numeric buffers#

If a function uses a pair (a more) of parameters which look like (double *values, int count), or (const float* values, int nb) (etc.), then litgen can transform this parameter into a numpy array.

Let’s see an example with this function:

void PlotXY(const float *xValues, const float *yValues, size_t how_many);

We would like it to be published as:

def plot_xy(x_values: np.ndarray, y_values: np.ndarray) -> None:
    pass

We will need to tell litgen:

  • Which function are concerned (options.fn_params_replace_buffer_by_array__regex)

  • The name of the the “count” param if it is not a standard one (count, nb, etc)

Note: if you look at the pybind11 C++ binding code, you will see that litgen handles the transformation, and ensures that the types are correct.

cpp_code = """
void PlotXY(const float *xValues, const float *yValues, size_t how_many);
"""
options = litgen.LitgenOptions()
options.fn_params_replace_buffer_by_array__regex = r"^Plot"
options.fn_params_buffer_size_names__regex += "|how_many"
litgen_demo.demo(options, cpp_code)
void PlotXY(const float *xValues, const float *yValues, size_t how_many);
def plot_xy(x_values: np.ndarray, y_values: np.ndarray) -> None:
    pass


m.def("plot_xy",
    [](const py::array & xValues, const py::array & yValues)
    {
        auto PlotXY_adapt_c_buffers = [](const py::array & xValues, const py::array & yValues)
        {
            // Check if the array is C-contiguous
            if (!xValues.attr("flags").attr("c_contiguous").cast<bool>()) {
                throw std::runtime_error("The array must be contiguous, i.e, `a.flags.c_contiguous` must be True. Hint: use `numpy.ascontiguousarray`.");
            }

            // convert py::array to C standard buffer (const)
            const void * xValues_from_pyarray = xValues.data();
            py::ssize_t xValues_count = xValues.shape()[0];
            char xValues_type = xValues.dtype().char_();
            if (xValues_type != 'f')
                throw std::runtime_error(std::string(R"msg(
                        Bad type!  Expected a numpy array of native type:
                                    const float *
                                Which is equivalent to
                                    f
                                (using py::array::dtype().char_() as an id)
                    )msg"));

            // Check if the array is C-contiguous
            if (!yValues.attr("flags").attr("c_contiguous").cast<bool>()) {
                throw std::runtime_error("The array must be contiguous, i.e, `a.flags.c_contiguous` must be True. Hint: use `numpy.ascontiguousarray`.");
            }

            // convert py::array to C standard buffer (const)
            const void * yValues_from_pyarray = yValues.data();
            py::ssize_t yValues_count = yValues.shape()[0];
            char yValues_type = yValues.dtype().char_();
            if (yValues_type != 'f')
                throw std::runtime_error(std::string(R"msg(
                        Bad type!  Expected a numpy array of native type:
                                    const float *
                                Which is equivalent to
                                    f
                                (using py::array::dtype().char_() as an id)
                    )msg"));

            PlotXY(static_cast<const float *>(xValues_from_pyarray), static_cast<const float *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
        };

        PlotXY_adapt_c_buffers(xValues, yValues);
    },     py::arg("x_values"), py::arg("y_values"));

Template numeric buffers#

If a template function uses a pair of parameters whose signature looks like (const T* values, int count), then it can be transformed into a numpy array.

In the example below, we would like the following C++ function:

template<typename NumberType> void PlotXY(Color color, const NumberType *xValues, const NumberType *yValues, size_t count);

To be published as:

def plot_xy(color: Color, x_values: np.ndarray, y_values: np.ndarray) -> None:
    pass

For this we need to:

  • Set which function names are concerned (options.fn_params_replace_buffer_by_array__regex)

  • Optionally, add the name of the template param (options.fn_params_buffer_template_types)

Note: if you look at the generated pybind11 C++ binding code, you will see that it handles all numeric types. This is a very efficient way to transmit numeric buffers of all types to python

cpp_code = """
    template<typename NumberType> 
    void PlotXY(Color color, const NumberType *xValues, const NumberType *yValues, size_t count);
"""
options = litgen.LitgenOptions()
options.fn_params_replace_buffer_by_array__regex = r"^Plot"
options.fn_params_buffer_template_types += "|NumberType"
litgen_demo.demo(options, cpp_code, height=80)
    template<typename NumberType> 
    void PlotXY(Color color, const NumberType *xValues, const NumberType *yValues, size_t count);
def plot_xy(color: Color, x_values: np.ndarray, y_values: np.ndarray) -> None:
    pass


m.def("plot_xy",
    [](Color color, const py::array & xValues, const py::array & yValues)
    {
        auto PlotXY_adapt_c_buffers = [](Color color, const py::array & xValues, const py::array & yValues)
        {
            // Check if the array is C-contiguous
            if (!xValues.attr("flags").attr("c_contiguous").cast<bool>()) {
                throw std::runtime_error("The array must be contiguous, i.e, `a.flags.c_contiguous` must be True. Hint: use `numpy.ascontiguousarray`.");
            }

            // convert py::array to C standard buffer (const)
            const void * xValues_from_pyarray = xValues.data();
            py::ssize_t xValues_count = xValues.shape()[0];

            // Check if the array is C-contiguous
            if (!yValues.attr("flags").attr("c_contiguous").cast<bool>()) {
                throw std::runtime_error("The array must be contiguous, i.e, `a.flags.c_contiguous` must be True. Hint: use `numpy.ascontiguousarray`.");
            }

            // convert py::array to C standard buffer (const)
            const void * yValues_from_pyarray = yValues.data();
            py::ssize_t yValues_count = yValues.shape()[0];

            #ifdef _WIN32
            using np_uint_l = uint32_t;
            using np_int_l = int32_t;
            #else
            using np_uint_l = uint64_t;
            using np_int_l = int64_t;
            #endif
            // call the correct template version by casting
            char yValues_type = yValues.dtype().char_();
            if (yValues_type == 'B')
                PlotXY(color, static_cast<const uint8_t *>(xValues_from_pyarray), static_cast<const uint8_t *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'b')
                PlotXY(color, static_cast<const int8_t *>(xValues_from_pyarray), static_cast<const int8_t *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'H')
                PlotXY(color, static_cast<const uint16_t *>(xValues_from_pyarray), static_cast<const uint16_t *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'h')
                PlotXY(color, static_cast<const int16_t *>(xValues_from_pyarray), static_cast<const int16_t *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'I')
                PlotXY(color, static_cast<const uint32_t *>(xValues_from_pyarray), static_cast<const uint32_t *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'i')
                PlotXY(color, static_cast<const int32_t *>(xValues_from_pyarray), static_cast<const int32_t *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'L')
                PlotXY(color, static_cast<const np_uint_l *>(xValues_from_pyarray), static_cast<const np_uint_l *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'l')
                PlotXY(color, static_cast<const np_int_l *>(xValues_from_pyarray), static_cast<const np_int_l *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'f')
                PlotXY(color, static_cast<const float *>(xValues_from_pyarray), static_cast<const float *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'd')
                PlotXY(color, static_cast<const double *>(xValues_from_pyarray), static_cast<const double *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'g')
                PlotXY(color, static_cast<const long double *>(xValues_from_pyarray), static_cast<const long double *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            else if (yValues_type == 'q')
                PlotXY(color, static_cast<const long long *>(xValues_from_pyarray), static_cast<const long long *>(yValues_from_pyarray), static_cast<size_t>(yValues_count));
            // If we reach this point, the array type is not supported!
            else
                throw std::runtime_error(std::string("Bad array type ('") + yValues_type + "') for param yValues");
        };

        PlotXY_adapt_c_buffers(color, xValues, yValues);
    },     py::arg("color"), py::arg("x_values"), py::arg("y_values"));

Vectorize functions#

See relevant portion of the pybind11 manual.

Within litgen, you can set:

  • Which namespaces are candidates for vectorization (options.fn_namespace_vectorize__regex. Set it to r".*" for all namespaces)

  • Which function names are candidates for vectorization

  • Which optional suffix or prefix will be added to the vectorized functions

cpp_code = """
    namespace MathFunctions
    {
        double fn1(double x, double y);
        double fn2(double x);
    }
"""
options = litgen.LitgenOptions()
options.fn_namespace_vectorize__regex = "^MathFunctions$"
options.fn_vectorize__regex = r".*"
options.fn_vectorize_suffix = "_v"
litgen_demo.demo(options, cpp_code)
    namespace MathFunctions
    {
        double fn1(double x, double y);
        double fn2(double x);
    }
# <submodule math_functions>
class math_functions:  # Proxy class that introduces typings for the *submodule* math_functions
    pass  # (This corresponds to a C++ namespace. All method are static!)
    @staticmethod
    def fn1(x: float, y: float) -> float:
        pass
    @staticmethod
    def fn1_v(x: np.ndarray, y: np.ndarray) -> np.ndarray:
        pass
    @staticmethod
    def fn2(x: float) -> float:
        pass
    @staticmethod
    def fn2_v(x: np.ndarray) -> np.ndarray:
        pass

# </submodule math_functions>


{ // <namespace MathFunctions>
    py::module_ pyNsMathFunctions = m.def_submodule("math_functions", "");
    pyNsMathFunctions.def("fn1",
        MathFunctions::fn1, py::arg("x"), py::arg("y"));
    pyNsMathFunctions.def("fn1_v",
        py::vectorize(MathFunctions::fn1), py::arg("x"), py::arg("y"));

    pyNsMathFunctions.def("fn2",
        MathFunctions::fn2, py::arg("x"));
    pyNsMathFunctions.def("fn2_v",
        py::vectorize(MathFunctions::fn2), py::arg("x"));
} // </namespace MathFunctions>

Accepting args and kwargs#

Relevant portion of the pybind11 manual

litgen will automatically detect signatures with params which look like (py::args args, const py::kwargs& kwargs) and adapt the python stub signature accordingly.

cpp_code = """
 void generic(py::args args, const py::kwargs& kwargs)
    {
        /// .. do something with args
        // if (kwargs)
            /// .. do something with kwargs
    }
"""
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
 void generic(py::args args, const py::kwargs& kwargs)
    {
        /// .. do something with args
        // if (kwargs)
            /// .. do something with kwargs
    }
def generic(*args, **kwargs) -> None:
    pass


m.def("generic",
    generic);

Force overload#

Relevant portion of the pybind11 manual.

Automatic overload#

If litgen detect two overload, it will add a call to py::overload_cast automatically:

cpp_code = """
void foo(int x);
void foo(double x);
"""
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)
void foo(int x);
void foo(double x);
@overload
def foo(x: int) -> None:
    pass
@overload
def foo(x: float) -> None:
    pass


m.def("foo",
    py::overload_cast<int>(foo), py::arg("x"));

m.def("foo",
    py::overload_cast<double>(foo), py::arg("x"));

Manual overload#

However, in some cases, you might want to add it manually: use options.fn_force_overload__regex

cpp_code = """
void foo2(int x);
"""
options = litgen.LitgenOptions()
options.fn_force_overload__regex += r"|^foo2$"
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.pydef_code)
m.def("foo2",
    py::overload_cast<int>(foo2), py::arg("x"));

Force usage of a lambda function#

In some rare cases, the usage of py::overload_cast might not be sufficient to discriminate the overload. In this case, you can tell litgen to disambiguate it via a lambda function. Look at the pybind C++ binding code below:

cpp_code = """
void foo3(int x);
"""
options = litgen.LitgenOptions()
options.fn_force_lambda__regex += r"|^foo3$"
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.pydef_code)
m.def("foo3",
    [](int x)
    {
        auto foo3_adapt_force_lambda = [](int x)
        {
            foo3(x);
        };

        foo3_adapt_force_lambda(x);
    },     py::arg("x"));