#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/numpy.h>

#include "mylib/mylib_main/mylib.h"

namespace py = pybind11;

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// <litgen_glue_code>  // Autogenerated code below! Do not edit!
struct BoxedBool
{
    bool value;
    BoxedBool(bool v = false) : value(v) {}
    std::string __repr__() const { return std::string("BoxedBool(") + std::to_string(value) + ")"; }
};
struct BoxedInt
{
    int value;
    BoxedInt(int v = 0) : value(v) {}
    std::string __repr__() const { return std::string("BoxedInt(") + std::to_string(value) + ")"; }
};
struct BoxedString
{
    std::string value;
    BoxedString(std::string v = "") : value(v) {}
    std::string __repr__() const { return std::string("BoxedString(") + value + ")"; }
};
struct BoxedUnsignedLong
{
    unsigned long value;
    BoxedUnsignedLong(unsigned long v = 0) : value(v) {}
    std::string __repr__() const { return std::string("BoxedUnsignedLong(") + std::to_string(value) + ")"; }
};

namespace Root { namespace Inner {
// helper type to enable overriding virtual methods in python
class MyVirtualClass_trampoline : public MyVirtualClass
{
public:
    using MyVirtualClass::MyVirtualClass;

    int foo_virtual_public_pure() const override
    {
        PYBIND11_OVERRIDE_PURE_NAME(
            int, // return type
            Root::Inner::MyVirtualClass, // parent class
            "foo_virtual_public_pure", // function name (python)
            foo_virtual_public_pure // function name (c++)
        );
    }
    int foo_virtual_protected(int x) const override
    {
        PYBIND11_OVERRIDE_NAME(
            int, // return type
            Root::Inner::MyVirtualClass, // parent class
            "foo_virtual_protected", // function name (python)
            foo_virtual_protected, // function name (c++)
            x // params
        );
    }
    std::string foo_virtual_protected_const_const(const std::string & name) const override
    {
        PYBIND11_OVERRIDE_NAME(
            std::string, // return type
            Root::Inner::MyVirtualClass, // parent class
            "foo_virtual_protected_const_const", // function name (python)
            foo_virtual_protected_const_const, // function name (c++)
            name // params
        );
    }
};
} }  // namespace Inner  // namespace Root

namespace Root { namespace Inner {
// helper type to enable overriding virtual methods in python
class MyVirtualDerivate_trampoline : public MyVirtualDerivate
{
public:
    using MyVirtualDerivate::MyVirtualDerivate;

    int foo_virtual_public_pure() const override
    {
        PYBIND11_OVERRIDE_NAME(
            int, // return type
            Root::Inner::MyVirtualDerivate, // parent class
            "foo_virtual_public_pure", // function name (python)
            foo_virtual_public_pure // function name (c++)
        );
    }
    int foo_derivate() override
    {
        PYBIND11_OVERRIDE_NAME(
            int, // return type
            Root::Inner::MyVirtualDerivate, // parent class
            "foo_derivate", // function name (python)
            foo_derivate // function name (c++)
        );
    }
    int foo_virtual_protected(int x) const override
    {
        PYBIND11_OVERRIDE_NAME(
            int, // return type
            Root::Inner::MyVirtualDerivate, // parent class
            "foo_virtual_protected", // function name (python)
            foo_virtual_protected, // function name (c++)
            x // params
        );
    }
    std::string foo_virtual_protected_const_const(const std::string & name) const override
    {
        PYBIND11_OVERRIDE_NAME(
            std::string, // return type
            Root::Inner::MyVirtualDerivate, // parent class
            "foo_virtual_protected_const_const", // function name (python)
            foo_virtual_protected_const_const, // function name (c++)
            name // params
        );
    }
};
} }  // namespace Inner  // namespace Root

namespace Root { namespace Inner {
// helper type for exposing protected functions
class MyVirtualClass_publicist : public MyVirtualClass
{
public:
    using MyVirtualClass::foo_virtual_protected;
    using MyVirtualClass::foo_virtual_protected_const_const;
};
} }  // namespace Inner  // namespace Root

// </litgen_glue_code> // Autogenerated code end
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  AUTOGENERATED CODE END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



void py_init_module_lg_mylib(py::module& m)
{
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // <litgen_pydef> // Autogenerated code below! Do not edit!
    ////////////////////    <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__)
        ;


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


    auto pyClassBoxedString =
        py::class_<BoxedString>
            (m, "BoxedString", "")
        .def_readwrite("value", &BoxedString::value, "")
        .def(py::init<std::string>(),
            py::arg("v") = "")
        .def("__repr__",
            &BoxedString::__repr__)
        ;


    auto pyClassBoxedUnsignedLong =
        py::class_<BoxedUnsignedLong>
            (m, "BoxedUnsignedLong", "")
        .def_readwrite("value", &BoxedUnsignedLong::value, "")
        .def(py::init<unsigned long>(),
            py::arg("v") = 0)
        .def("__repr__",
            &BoxedUnsignedLong::__repr__)
        ;
    ////////////////////    </generated_from:BoxedTypes>    ////////////////////


    ////////////////////    <generated_from:mylib_amalgamation.h>    ////////////////////
    m.def("my_sub",
        my_sub,
        py::arg("a"), py::arg("b"),
        "Subtracts two numbers: this will be the function's __doc__ since my_sub does not have an end-of-line comment");

    m.def("my_add",
        my_add,
        py::arg("a"), py::arg("b"),
        "Adds two numbers");

    m.def("my_mul",
        my_mul, py::arg("a"), py::arg("b"));

    m.def("my_generic_function",
        my_generic_function, "This is a generic function for python, accepting (*args, **kwargs) as arguments");

    m.def("const_array2_add",
        [](const std::array<int, 2>& values) -> int
        {
            auto const_array2_add_adapt_fixed_size_c_arrays = [](const std::array<int, 2>& values) -> int
            {
                auto lambda_result = const_array2_add(values.data());
                return lambda_result;
            };

            return const_array2_add_adapt_fixed_size_c_arrays(values);
        },
        py::arg("values"),
        " Tests with const array: since the input numbers are const, their params are published as List[int],\n and the python signature will be:\n -->    def add_c_array2(values: List[int]) -> int:\n (and the runtime will check that the list size is exactly 2)");

    m.def("array2_modify",
        [](BoxedUnsignedLong & values_0, BoxedUnsignedLong & values_1)
        {
            auto array2_modify_adapt_fixed_size_c_arrays = [](BoxedUnsignedLong & values_0, BoxedUnsignedLong & values_1)
            {
                unsigned long values_raw[2];
                values_raw[0] = values_0.value;
                values_raw[1] = values_1.value;

                array2_modify(values_raw);

                values_0.value = values_raw[0];
                values_1.value = values_raw[1];
            };

            array2_modify_adapt_fixed_size_c_arrays(values_0, values_1);
        },
        py::arg("values_0"), py::arg("values_1"),
        " Test with a modifiable array: since the input array is not const, it could be modified.\n Thus, it will be published as a function accepting Boxed values:\n -->    def array2_modify(values_0: BoxedUnsignedLong, values_1: BoxedUnsignedLong) -> None:");


    auto pyClassPoint2 =
        py::class_<Point2>
            (m, "Point2", "")
        .def(py::init<>([](
        int x = int(), int y = int())
        {
            auto r = std::make_unique<Point2>();
            r->x = x;
            r->y = y;
            return r;
        })
        , py::arg("x") = int(), py::arg("y") = int()
        )
        .def_readwrite("x", &Point2::x, "")
        .def_readwrite("y", &Point2::y, "")
        ;


    m.def("array2_modify_mutable",
        [](Point2 & out_0, Point2 & out_1)
        {
            auto array2_modify_mutable_adapt_fixed_size_c_arrays = [](Point2 & out_0, Point2 & out_1)
            {
                Point2 out_raw[2];
                out_raw[0] = out_0;
                out_raw[1] = out_1;

                array2_modify_mutable(out_raw);

                out_0 = out_raw[0];
                out_1 = out_raw[1];
            };

            array2_modify_mutable_adapt_fixed_size_c_arrays(out_0, out_1);
        },
        py::arg("out_0"), py::arg("out_1"),
        " Test with a modifiable array that uses a user defined struct.\n Since the user defined struct is mutable in python, it will not be Boxed,\n and the python signature will be:\n-->    def get_points(out_0: Point2, out_1: Point2) -> None:");

    m.def("add_inside_buffer",
        [](py::array & buffer, uint8_t number_to_add)
        {
            auto add_inside_buffer_adapt_c_buffers = [](py::array & buffer, uint8_t number_to_add)
            {
                // convert py::array to C standard buffer (mutable)
                void * buffer_from_pyarray = buffer.mutable_data();
                py::ssize_t buffer_count = buffer.shape()[0];
                char buffer_type = buffer.dtype().char_();
                if (buffer_type != 'B')
                    throw std::runtime_error(std::string(R"msg(
                            Bad type!  Expected a numpy array of native type:
                                        uint8_t *
                                    Which is equivalent to
                                        B
                                    (using py::array::dtype().char_() as an id)
                        )msg"));

                add_inside_buffer(static_cast<uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), number_to_add);
            };

            add_inside_buffer_adapt_c_buffers(buffer, number_to_add);
        },
        py::arg("buffer"), py::arg("number_to_add"),
        " add_inside_buffer: modifies a buffer by adding a value to its elements\n Will be published in python as:\n -->    def add_inside_buffer(buffer: np.ndarray, number_to_add: int) -> None:\n Warning, the python function will accept only uint8 numpy arrays, and check it at runtime!");

    m.def("buffer_sum",
        [](const py::array & buffer, int stride = -1) -> int
        {
            auto buffer_sum_adapt_c_buffers = [](const py::array & buffer, int stride = -1) -> int
            {
                // convert py::array to C standard buffer (const)
                const void * buffer_from_pyarray = buffer.data();
                py::ssize_t buffer_count = buffer.shape()[0];
                char buffer_type = buffer.dtype().char_();
                if (buffer_type != 'B')
                    throw std::runtime_error(std::string(R"msg(
                            Bad type!  Expected a numpy array of native type:
                                        const uint8_t *
                                    Which is equivalent to
                                        B
                                    (using py::array::dtype().char_() as an id)
                        )msg"));

                // process stride default value (which was a sizeof in C++)
                int buffer_stride = stride;
                if (buffer_stride == -1)
                    buffer_stride = (int)buffer.itemsize();

                auto lambda_result = buffer_sum(static_cast<const uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), static_cast<size_t>(buffer_stride));
                return lambda_result;
            };

            return buffer_sum_adapt_c_buffers(buffer, stride);
        },
        py::arg("buffer"), py::arg("stride") = -1,
        " buffer_sum: returns the sum of a *const* buffer\n Will be published in python as:\n -->    def buffer_sum(buffer: np.ndarray, stride: int = -1) -> int:");

    m.def("add_inside_two_buffers",
        [](py::array & buffer_1, py::array & buffer_2, uint8_t number_to_add)
        {
            auto add_inside_two_buffers_adapt_c_buffers = [](py::array & buffer_1, py::array & buffer_2, uint8_t number_to_add)
            {
                // convert py::array to C standard buffer (mutable)
                void * buffer_1_from_pyarray = buffer_1.mutable_data();
                py::ssize_t buffer_1_count = buffer_1.shape()[0];
                char buffer_1_type = buffer_1.dtype().char_();
                if (buffer_1_type != 'B')
                    throw std::runtime_error(std::string(R"msg(
                            Bad type!  Expected a numpy array of native type:
                                        uint8_t *
                                    Which is equivalent to
                                        B
                                    (using py::array::dtype().char_() as an id)
                        )msg"));

                // convert py::array to C standard buffer (mutable)
                void * buffer_2_from_pyarray = buffer_2.mutable_data();
                py::ssize_t buffer_2_count = buffer_2.shape()[0];
                char buffer_2_type = buffer_2.dtype().char_();
                if (buffer_2_type != 'B')
                    throw std::runtime_error(std::string(R"msg(
                            Bad type!  Expected a numpy array of native type:
                                        uint8_t *
                                    Which is equivalent to
                                        B
                                    (using py::array::dtype().char_() as an id)
                        )msg"));

                add_inside_two_buffers(static_cast<uint8_t *>(buffer_1_from_pyarray), static_cast<uint8_t *>(buffer_2_from_pyarray), static_cast<size_t>(buffer_2_count), number_to_add);
            };

            add_inside_two_buffers_adapt_c_buffers(buffer_1, buffer_2, number_to_add);
        },
        py::arg("buffer_1"), py::arg("buffer_2"), py::arg("number_to_add"),
        " add_inside_two_buffers: modifies two mutable buffers\n litgen will detect that this function uses two buffers of same size.\n Will be published in python as:\n -->    def add_inside_two_buffers(buffer_1: np.ndarray, buffer_2: np.ndarray, number_to_add: int) -> None:");

    m.def("templated_mul_inside_buffer",
        [](py::array & buffer, double factor)
        {
            auto templated_mul_inside_buffer_adapt_c_buffers = [](py::array & buffer, double factor)
            {
                // convert py::array to C standard buffer (mutable)
                void * buffer_from_pyarray = buffer.mutable_data();
                py::ssize_t buffer_count = buffer.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 buffer_type = buffer.dtype().char_();
                if (buffer_type == 'B')
                    templated_mul_inside_buffer(static_cast<uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'b')
                    templated_mul_inside_buffer(static_cast<int8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'H')
                    templated_mul_inside_buffer(static_cast<uint16_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'h')
                    templated_mul_inside_buffer(static_cast<int16_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'I')
                    templated_mul_inside_buffer(static_cast<uint32_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'i')
                    templated_mul_inside_buffer(static_cast<int32_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'L')
                    templated_mul_inside_buffer(static_cast<np_uint_l *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'l')
                    templated_mul_inside_buffer(static_cast<np_int_l *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'f')
                    templated_mul_inside_buffer(static_cast<float *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'd')
                    templated_mul_inside_buffer(static_cast<double *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'g')
                    templated_mul_inside_buffer(static_cast<long double *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                else if (buffer_type == 'q')
                    templated_mul_inside_buffer(static_cast<long long *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                // If we reach this point, the array type is not supported!
                else
                    throw std::runtime_error(std::string("Bad array type ('") + buffer_type + "') for param buffer");
            };

            templated_mul_inside_buffer_adapt_c_buffers(buffer, factor);
        },
        py::arg("buffer"), py::arg("factor"),
        " templated_mul_inside_buffer: template function that modifies an array by multiplying its elements by a given factor\n litgen will detect that this function can be published as using a numpy array.\n It will be published in python as:\n -->    def mul_inside_buffer(buffer: np.ndarray, factor: float) -> None:\n\n The type will be detected at runtime and the correct template version will be called accordingly!\n An error will be thrown if the numpy array numeric type is not supported.");

    m.def("c_string_list_total_size",
        [](const std::vector<std::string> & items, BoxedInt & output_0, BoxedInt & output_1) -> size_t
        {
            auto c_string_list_total_size_adapt_fixed_size_c_arrays = [](const char * const items[], int items_count, BoxedInt & output_0, BoxedInt & output_1) -> size_t
            {
                int output_raw[2];
                output_raw[0] = output_0.value;
                output_raw[1] = output_1.value;

                auto lambda_result = c_string_list_total_size(items, items_count, output_raw);

                output_0.value = output_raw[0];
                output_1.value = output_raw[1];
                return lambda_result;
            };
            auto c_string_list_total_size_adapt_c_string_list = [&c_string_list_total_size_adapt_fixed_size_c_arrays](const std::vector<std::string> & items, BoxedInt & output_0, BoxedInt & output_1) -> size_t
            {
                std::vector<const char *> items_ptrs;
                for (const auto& v: items)
                    items_ptrs.push_back(v.c_str());
                int items_count = static_cast<int>(items.size());

                auto lambda_result = c_string_list_total_size_adapt_fixed_size_c_arrays(items_ptrs.data(), items_count, output_0, output_1);
                return lambda_result;
            };

            return c_string_list_total_size_adapt_c_string_list(items, output_0, output_1);
        },
        py::arg("items"), py::arg("output_0"), py::arg("output_1"),
        "\n C String lists tests:\n   Two consecutive params (const char *, int | size_t) are exported as List[str]\n\n The following function will be exported with the following python signature:\n -->    def c_string_list_total_size(items: List[str], output_0: BoxedInt, output_1: BoxedInt) -> int:\n");

    m.def("toggle_bool_pointer",
        [](BoxedBool & v)
        {
            auto ToggleBoolPointer_adapt_modifiable_immutable = [](BoxedBool & v)
            {
                bool * v_boxed_value = & (v.value);

                ToggleBoolPointer(v_boxed_value);
            };

            ToggleBoolPointer_adapt_modifiable_immutable(v);
        },
        py::arg("v"),
        " Test with pointer:\n Will be published in python as:\n -->    def toggle_bool_pointer(v: BoxedBool) -> None:");

    m.def("toggle_bool_nullable",
        [](BoxedBool * v = nullptr)
        {
            auto ToggleBoolNullable_adapt_modifiable_immutable = [](BoxedBool * v = nullptr)
            {
                bool * v_boxed_value = nullptr;
                if (v != nullptr)
                    v_boxed_value = & (v->value);

                ToggleBoolNullable(v_boxed_value);
            };

            ToggleBoolNullable_adapt_modifiable_immutable(v);
        },
        py::arg("v") = py::none(),
        " Test with nullable pointer\n Will be published in python as:\n -->    def toggle_bool_nullable(v: BoxedBool = None) -> None:");

    m.def("toggle_bool_reference",
        [](BoxedBool & v)
        {
            auto ToggleBoolReference_adapt_modifiable_immutable = [](BoxedBool & v)
            {
                bool & v_boxed_value = v.value;

                ToggleBoolReference(v_boxed_value);
            };

            ToggleBoolReference_adapt_modifiable_immutable(v);
        },
        py::arg("v"),
        " Test with reference\n Will be published in python as:\n -->    def toggle_bool_reference(v: BoxedBool) -> None:");

    m.def("modify_string",
        [](BoxedString & s)
        {
            auto ModifyString_adapt_modifiable_immutable = [](BoxedString & s)
            {
                std::string * s_boxed_value = & (s.value);

                ModifyString(s_boxed_value);
            };

            ModifyString_adapt_modifiable_immutable(s);
        },
        py::arg("s"),
        " Test modifiable String\n Will be published in python as:\n -->    def modify_string(s: BoxedString) -> None:");

    m.def("change_bool_int",
        [](const char * label, int value) -> std::tuple<bool, int>
        {
            auto ChangeBoolInt_adapt_modifiable_immutable_to_return = [](const char * label, int value) -> std::tuple<bool, int>
            {
                int * value_adapt_modifiable = & value;

                bool r = ChangeBoolInt(label, value_adapt_modifiable);
                return std::make_tuple(r, value);
            };

            return ChangeBoolInt_adapt_modifiable_immutable_to_return(label, value);
        },
        py::arg("label"), py::arg("value"),
        " Test with int param + int return type\n Will be published in python as:\n --> def change_bool_int(label: str, value: int) -> Tuple[bool, int]:");

    m.def("change_void_int",
        [](const char * label, int value) -> int
        {
            auto ChangeVoidInt_adapt_modifiable_immutable_to_return = [](const char * label, int value) -> int
            {
                int * value_adapt_modifiable = & value;

                ChangeVoidInt(label, value_adapt_modifiable);
                return value;
            };

            return ChangeVoidInt_adapt_modifiable_immutable_to_return(label, value);
        },
        py::arg("label"), py::arg("value"),
        " Will be published in python as:\n -->    def change_void_int(label: str, value: int) -> int:");

    m.def("change_bool_int2",
        [](const char * label, int value1, int value2) -> std::tuple<bool, int, int>
        {
            auto ChangeBoolInt2_adapt_modifiable_immutable_to_return = [](const char * label, int value1, int value2) -> std::tuple<bool, int, int>
            {
                int * value1_adapt_modifiable = & value1;
                int * value2_adapt_modifiable = & value2;

                bool r = ChangeBoolInt2(label, value1_adapt_modifiable, value2_adapt_modifiable);
                return std::make_tuple(r, value1, value2);
            };

            return ChangeBoolInt2_adapt_modifiable_immutable_to_return(label, value1, value2);
        },
        py::arg("label"), py::arg("value1"), py::arg("value2"),
        " Will be published in python as:\n -->    def change_bool_int2(label: str, value1: int, value2: int) -> Tuple[bool, int, int]:");

    m.def("change_void_int_default_null",
        [](const char * label, std::optional<int> value = std::nullopt) -> std::tuple<bool, std::optional<int>>
        {
            auto ChangeVoidIntDefaultNull_adapt_modifiable_immutable_to_return = [](const char * label, std::optional<int> value = std::nullopt) -> std::tuple<bool, std::optional<int>>
            {
                int * value_adapt_modifiable = nullptr;
                if (value.has_value())
                    value_adapt_modifiable = & (*value);

                bool r = ChangeVoidIntDefaultNull(label, value_adapt_modifiable);
                return std::make_tuple(r, value);
            };

            return ChangeVoidIntDefaultNull_adapt_modifiable_immutable_to_return(label, value);
        },
        py::arg("label"), py::arg("value") = py::none(),
        " Will be published in python as:\n -->    def change_void_int_default_null(label: str, value: Optional[int] = None) -> Tuple[bool, Optional[int]]:");

    m.def("change_void_int_array",
        [](const char * label, std::array<int, 3> value) -> std::tuple<bool, std::array<int, 3>>
        {
            auto ChangeVoidIntArray_adapt_modifiable_immutable_to_return = [](const char * label, std::array<int, 3> value) -> std::tuple<bool, std::array<int, 3>>
            {
                int * value_adapt_modifiable = value.data();

                bool r = ChangeVoidIntArray(label, value_adapt_modifiable);
                return std::make_tuple(r, value);
            };

            return ChangeVoidIntArray_adapt_modifiable_immutable_to_return(label, value);
        },
        py::arg("label"), py::arg("value"),
        " Will be published in python as:\n -->    def change_void_int_array(label: str, value: List[int]) -> Tuple[bool, List[int]]:");

    m.def("add_overload",
        py::overload_cast<int, int>(add_overload), py::arg("a"), py::arg("b"));

    m.def("add_overload",
        py::overload_cast<int, int, int>(add_overload), py::arg("a"), py::arg("b"), py::arg("c"));


    auto pyClassFooOverload =
        py::class_<FooOverload>
            (m, "FooOverload", "")
        .def(py::init<>()) // implicit default constructor
        .def("add_overload",
            py::overload_cast<int, int>(&FooOverload::add_overload), py::arg("a"), py::arg("b"))
        .def("add_overload",
            py::overload_cast<int, int, int>(&FooOverload::add_overload), py::arg("a"), py::arg("b"), py::arg("c"))
        ;


    py::enum_<BasicEnum>(m, "BasicEnum", py::arithmetic(), "BasicEnum: a simple C-style enum")
        .value("a", BasicEnum_a, "This will be exported as BasicEnum.a")
        .value("aa", BasicEnum_aa, "This will be exported as BasicEnum.aa")
        .value("aaa", BasicEnum_aaa, "This will be exported as BasicEnum.aaa")
        .value("b", BasicEnum_b, "This is value b");


    py::enum_<ClassEnum>(m, "ClassEnum", py::arithmetic(), "ClassEnum: a class enum that should be published")
        .value("on", ClassEnum::On, "")
        .value("off", ClassEnum::Off, "")
        .value("unknown", ClassEnum::Unknown, "");


    auto pyClassMyClass =
        py::class_<MyClass>
            (m, "MyClass", "This is the class doc. It will be published as MyClass.__doc__")
        .def(py::init<int, const std::string &>(),
            py::arg("factor") = 10, py::arg("message") = "hello")
        .def_readwrite("factor", &MyClass::factor, "")
        .def_readwrite("delta", &MyClass::delta, "")
        .def_readwrite("message", &MyClass::message, "")
        .def_readwrite("numbers", &MyClass::numbers, " By default, modifications from python are not propagated to C++ for stl containers\n (see https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html)")
        .def("append_number_from_cpp",
            &MyClass::append_number_from_cpp,
            py::arg("v"),
            "However you can call dedicated modifying methods")
        .def_property("values",
            [](MyClass &self) -> pybind11::array
            {
                auto dtype = pybind11::dtype(pybind11::format_descriptor<int>::format());
                auto base = pybind11::array(dtype, {2}, {sizeof(int)});
                return pybind11::array(dtype, {2}, {sizeof(int)}, self.values, base);
            }, [](MyClass& self) {},
            "")
        .def_property("flags",
            [](MyClass &self) -> pybind11::array
            {
                auto dtype = pybind11::dtype(pybind11::format_descriptor<bool>::format());
                auto base = pybind11::array(dtype, {3}, {sizeof(bool)});
                return pybind11::array(dtype, {3}, {sizeof(bool)}, self.flags, base);
            }, [](MyClass& self) {},
            "")
        .def_readonly_static("const_static_value", &MyClass::const_static_value, "")
        .def_readwrite_static("static_value", &MyClass::static_value, "")
        .def("calc",
            &MyClass::calc,
            py::arg("x"),
            "calc: example of simple method")
        .def("set_message",
            &MyClass::set_message,
            py::arg("m"),
            "set_message: another example of simple method")
        .def_static("static_message",
            &MyClass::static_message, "Returns a static message")
        ;


    auto pyClassMySingletonClass =
        py::class_<MySingletonClass, std::unique_ptr<MySingletonClass, py::nodelete>>
            (m, "MySingletonClass", " MySingletonClass: demonstrate how to instantiate a singleton\n - The instance method shall return with return_value_policy::reference\n - The destructor may be private")
        .def(py::init<>()) // implicit default constructor
        .def_readwrite("value", &MySingletonClass::value, "")
        .def_static("instance",
            &MySingletonClass::instance,
            "return_value_policy::reference",
            pybind11::return_value_policy::reference)
        ;


    auto pyClassMyFinalClass =
        py::class_<MyFinalClass>
            (m, "MyFinalClass", py::is_final(), " This struct is final, and thus cannot be inherited from python\n(final class)")
        .def(py::init<>()) // implicit default constructor
        .def("foo",
            &MyFinalClass::foo)
        ;


    auto pyClassMyStructDynamic =
        py::class_<MyStructDynamic>
            (m, "MyStructDynamic", py::dynamic_attr(), " This class accepts dynamic attributes\n see autogenerate_mylib.py:\n     options.class_dynamic_attributes__regex = r\"Dynamic$\"")
        .def(py::init<>([](
        int cpp_member = 1)
        {
            auto r = std::make_unique<MyStructDynamic>();
            r->cpp_member = cpp_member;
            return r;
        })
        , py::arg("cpp_member") = 1
        )
        .def_readwrite("cpp_member", &MyStructDynamic::cpp_member, "")
        ;


    auto pyClassMyStructWithNestedEnum =
        py::class_<MyStructWithNestedEnum>
            (m, "MyStructWithNestedEnum", "");

    { // inner classes & enums of MyStructWithNestedEnum
        py::enum_<MyStructWithNestedEnum::Choice>(pyClassMyStructWithNestedEnum, "Choice", py::arithmetic(), "")
            .value("a", MyStructWithNestedEnum::Choice::A, "");
    } // end of inner classes & enums of MyStructWithNestedEnum

    pyClassMyStructWithNestedEnum
        .def(py::init<>()) // implicit default constructor
        .def("handle_choice",
            &MyStructWithNestedEnum::HandleChoice,
            py::arg("value") = MyStructWithNestedEnum::Choice::A,
            " The first param of this function uses the inner scope of this class!\n When building the bindings, we need to add MyStructWithNestedEnum::")
        ;


    m.def("make_dog",
        make_dog, "Test that downcasting works: the return type is Animal, but it should bark!");


    auto pyClassColor4 =
        py::class_<Color4>
            (m, "Color4", "")
        .def(py::init(
            [](const std::array<uint8_t, 4>& _rgba) -> std::unique_ptr<Color4>
            {
                auto ctor_wrapper = [](const uint8_t _rgba[4]) ->  std::unique_ptr<Color4>
                {
                    return std::make_unique<Color4>(_rgba);
                };
                auto ctor_wrapper_adapt_fixed_size_c_arrays = [&ctor_wrapper](const std::array<uint8_t, 4>& _rgba) -> std::unique_ptr<Color4>
                {
                    auto lambda_result = ctor_wrapper(_rgba.data());
                    return lambda_result;
                };

                return ctor_wrapper_adapt_fixed_size_c_arrays(_rgba);
            }),
            py::arg("_rgba"),
            "The constructor params will automatically be \"adapted\" into std::array<uint8_t, 4>")
        .def_property("rgba",
            [](Color4 &self) -> pybind11::array
            {
                auto dtype = pybind11::dtype(pybind11::format_descriptor<uint8_t>::format());
                auto base = pybind11::array(dtype, {4}, {sizeof(uint8_t)});
                return pybind11::array(dtype, {4}, {sizeof(uint8_t)}, self.rgba, base);
            }, [](Color4& self) {},
            "This member will be stored as a modifiable numpy array")
        ;


    auto pyClassCopyable_ImplicitCopyCtor =
        py::class_<Copyable_ImplicitCopyCtor>
            (m, "Copyable_ImplicitCopyCtor", "")
        .def(py::init<>([](
        int a = 1)
        {
            auto r = std::make_unique<Copyable_ImplicitCopyCtor>();
            r->a = a;
            return r;
        })
        , py::arg("a") = 1
        )
        .def_readwrite("a", &Copyable_ImplicitCopyCtor::a, "")
        .def("__copy__",  [](const Copyable_ImplicitCopyCtor &self) {
            return Copyable_ImplicitCopyCtor(self);
        })
        .def("__deepcopy__",  [](const Copyable_ImplicitCopyCtor &self, py::dict) {
            return Copyable_ImplicitCopyCtor(self);
        }, py::arg("memo"))    ;


    auto pyClassCopyable_ExplicitCopyCtor =
        py::class_<Copyable_ExplicitCopyCtor>
            (m, "Copyable_ExplicitCopyCtor", "")
        .def(py::init<>())
        .def(py::init<const Copyable_ExplicitCopyCtor &>(),
            py::arg("other"))
        .def_readwrite("a", &Copyable_ExplicitCopyCtor::a, "")
        .def("__copy__",  [](const Copyable_ExplicitCopyCtor &self) {
            return Copyable_ExplicitCopyCtor(self);
        })
        .def("__deepcopy__",  [](const Copyable_ExplicitCopyCtor &self, py::dict) {
            return Copyable_ExplicitCopyCtor(self);
        }, py::arg("memo"))    ;


    auto pyClassCopyable_ExplicitPrivateCopyCtor =
        py::class_<Copyable_ExplicitPrivateCopyCtor>
            (m, "Copyable_ExplicitPrivateCopyCtor", "")
        .def(py::init<>())
        .def_readwrite("a", &Copyable_ExplicitPrivateCopyCtor::a, "")
        ;


    auto pyClassCopyable_DeletedCopyCtor =
        py::class_<Copyable_DeletedCopyCtor>
            (m, "Copyable_DeletedCopyCtor", "")
        .def_readwrite("a", &Copyable_DeletedCopyCtor::a, "")
        .def(py::init<>())
        ;


    auto pyClassMyConfig =
        py::class_<MyConfig>
            (m, "MyConfig", "")
        .def(py::init<>([](
        int value = 0)
        {
            auto r = std::make_unique<MyConfig>();
            r->value = value;
            return r;
        })
        , py::arg("value") = 0
        )
        .def_static("instance",
            &MyConfig::Instance,
            "return_value_policy::reference",
            pybind11::return_value_policy::reference)
        .def_readwrite("value", &MyConfig::value, "")
        ;


    m.def("my_config_instance",
        MyConfigInstance,
        "return_value_policy::reference",
        pybind11::return_value_policy::reference);

    m.def("foo_root",
        FooRoot);


    auto pyClassIntWrapper =
        py::class_<IntWrapper>
            (m, "IntWrapper", "")
        .def_readwrite("value", &IntWrapper::value, "")
        .def(py::init<int>(),
            py::arg("v"))
        .def("__add__",
            &IntWrapper::operator+, py::arg("b"))
        .def("__sub__",
            py::overload_cast<IntWrapper>(&IntWrapper::operator-), py::arg("b"))
        .def("__neg__",
            [](IntWrapper & self) { return self.operator-(); }, "Unary minus operator")
        .def("__lt__",
            &IntWrapper::operator<,
            py::arg("b"),
            "Comparison operator")
        .def("__iadd__",
            py::overload_cast<IntWrapper>(&IntWrapper::operator+=), py::arg("b"))
        .def("__iadd__",
            py::overload_cast<int>(&IntWrapper::operator+=), py::arg("b"))
        .def("__call__",
            py::overload_cast<IntWrapper>(&IntWrapper::operator()), py::arg("b"))
        .def("__call__",
            py::overload_cast<int>(&IntWrapper::operator()), py::arg("b"))
        ;


    auto pyClassIntWrapperSpaceship =
        py::class_<IntWrapperSpaceship>
            (m, "IntWrapperSpaceship", "")
        .def_readwrite("value", &IntWrapperSpaceship::value, "")
        .def(py::init<int>(),
            py::arg("v"))
        .def("__lt__",
            [](IntWrapperSpaceship & self, IntWrapperSpaceship & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  < 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__le__",
            [](IntWrapperSpaceship & self, IntWrapperSpaceship & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  <= 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__eq__",
            [](IntWrapperSpaceship & self, IntWrapperSpaceship & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  == 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__ge__",
            [](IntWrapperSpaceship & self, IntWrapperSpaceship & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  >= 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__gt__",
            [](IntWrapperSpaceship & self, IntWrapperSpaceship & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  > 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__lt__",
            [](IntWrapperSpaceship & self, int & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  < 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__le__",
            [](IntWrapperSpaceship & self, int & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  <= 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__eq__",
            [](IntWrapperSpaceship & self, int & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  == 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__ge__",
            [](IntWrapperSpaceship & self, int & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  >= 0;
                };

                return cmp(o);
            },     py::arg("o"))
        .def("__gt__",
            [](IntWrapperSpaceship & self, int & o) -> bool
            {
                auto cmp = [&self](auto&& other) -> bool {
                    return self.operator<=>(other)  > 0;
                };

                return cmp(o);
            },     py::arg("o"))
        ;


    m.def("call_guard_tester",
        call_guard_tester,
        "// py::call_guard<CallGuardLogger>()",
        py::call_guard<CallGuardLogger>());


    auto pyClassCallGuardLogger =
        py::class_<CallGuardLogger>
            (m, "CallGuardLogger", " ============================================================================\n CallGuardLogger: dummy call guard for the tests\n ============================================================================")
        .def(py::init<>())
        .def_readwrite_static("nb_construct", &CallGuardLogger::nb_construct, "")
        .def_readwrite_static("nb_destroy", &CallGuardLogger::nb_destroy, "")
        ;


    m.def("add_templated",
        py::overload_cast<int, int>(AddTemplated<int>), py::arg("a"), py::arg("b"));
    m.def("add_templated",
        py::overload_cast<double, double>(AddTemplated<double>), py::arg("a"), py::arg("b"));
    m.def("add_templated",
        py::overload_cast<std::string, std::string>(AddTemplated<std::string>), py::arg("a"), py::arg("b"));

    m.def("sum_vector_and_c_array",
        [](std::vector<int> xs, const std::array<int, 2>& other_values) -> int
        {
            auto SumVectorAndCArray_adapt_fixed_size_c_arrays = [](std::vector<int> xs, const std::array<int, 2>& other_values) -> int
            {
                auto lambda_result = SumVectorAndCArray<int>(xs, other_values.data());
                return lambda_result;
            };

            return SumVectorAndCArray_adapt_fixed_size_c_arrays(xs, other_values);
        },     py::arg("xs"), py::arg("other_values"));
    m.def("sum_vector_and_c_array",
        [](std::vector<std::string> xs, const std::array<std::string, 2>& other_values) -> std::string
        {
            auto SumVectorAndCArray_adapt_fixed_size_c_arrays = [](std::vector<std::string> xs, const std::array<std::string, 2>& other_values) -> std::string
            {
                auto lambda_result = SumVectorAndCArray<std::string>(xs, other_values.data());
                return lambda_result;
            };

            return SumVectorAndCArray_adapt_fixed_size_c_arrays(xs, other_values);
        },     py::arg("xs"), py::arg("other_values"));


    auto pyClassFooTemplateFunctionTest =
        py::class_<FooTemplateFunctionTest>
            (m, "FooTemplateFunctionTest", "")
        .def(py::init<>()) // implicit default constructor
        .def("sum_vector_and_c_array",
            [](FooTemplateFunctionTest & self, std::vector<int> xs, const std::array<int, 2>& other_values) -> int
            {
                auto SumVectorAndCArray_adapt_fixed_size_c_arrays = [&self](std::vector<int> xs, const std::array<int, 2>& other_values) -> int
                {
                    auto lambda_result = self.SumVectorAndCArray<int>(xs, other_values.data());
                    return lambda_result;
                };

                return SumVectorAndCArray_adapt_fixed_size_c_arrays(xs, other_values);
            },     py::arg("xs"), py::arg("other_values"))
        .def("sum_vector_and_c_array",
            [](FooTemplateFunctionTest & self, std::vector<std::string> xs, const std::array<std::string, 2>& other_values) -> std::string
            {
                auto SumVectorAndCArray_adapt_fixed_size_c_arrays = [&self](std::vector<std::string> xs, const std::array<std::string, 2>& other_values) -> std::string
                {
                    auto lambda_result = self.SumVectorAndCArray<std::string>(xs, other_values.data());
                    return lambda_result;
                };

                return SumVectorAndCArray_adapt_fixed_size_c_arrays(xs, other_values);
            },     py::arg("xs"), py::arg("other_values"))
        ;


    auto pyClassMyTemplateClass_int =
        py::class_<MyTemplateClass<int>>
            (m, "MyTemplateClass_int", "")
        .def_readwrite("values", &MyTemplateClass<int>::values, "")
        .def(py::init<>(),
            "Standard constructor")
        .def(py::init(
            [](const std::array<int, 2>& v) -> std::unique_ptr<MyTemplateClass<int>>
            {
                auto ctor_wrapper = [](const int v[2]) ->  std::unique_ptr<MyTemplateClass<int>>
                {
                    return std::make_unique<MyTemplateClass<int>>(v);
                };
                auto ctor_wrapper_adapt_fixed_size_c_arrays = [&ctor_wrapper](const std::array<int, 2>& v) -> std::unique_ptr<MyTemplateClass<int>>
                {
                    auto lambda_result = ctor_wrapper(v.data());
                    return lambda_result;
                };

                return ctor_wrapper_adapt_fixed_size_c_arrays(v);
            }),
            py::arg("v"),
            "Constructor that will need a parameter adaptation")
        .def("sum",
            &MyTemplateClass<int>::sum, "Standard method")
        .def("sum2",
            [](MyTemplateClass<int> & self, const std::array<int, 2>& v) -> int
            {
                auto sum2_adapt_fixed_size_c_arrays = [&self](const std::array<int, 2>& v) -> int
                {
                    auto lambda_result = self.sum2(v.data());
                    return lambda_result;
                };

                return sum2_adapt_fixed_size_c_arrays(v);
            },
            py::arg("v"),
            "Method that requires a parameter adaptation")
        ;
    auto pyClassMyTemplateClass_string =
        py::class_<MyTemplateClass<std::string>>
            (m, "MyTemplateClass_string", "")
        .def_readwrite("values", &MyTemplateClass<std::string>::values, "")
        .def(py::init<>(),
            "Standard constructor")
        .def(py::init(
            [](const std::array<std::string, 2>& v) -> std::unique_ptr<MyTemplateClass<std::string>>
            {
                auto ctor_wrapper = [](const std::string v[2]) ->  std::unique_ptr<MyTemplateClass<std::string>>
                {
                    return std::make_unique<MyTemplateClass<std::string>>(v);
                };
                auto ctor_wrapper_adapt_fixed_size_c_arrays = [&ctor_wrapper](const std::array<std::string, 2>& v) -> std::unique_ptr<MyTemplateClass<std::string>>
                {
                    auto lambda_result = ctor_wrapper(v.data());
                    return lambda_result;
                };

                return ctor_wrapper_adapt_fixed_size_c_arrays(v);
            }),
            py::arg("v"),
            "Constructor that will need a parameter adaptation")
        .def("sum",
            &MyTemplateClass<std::string>::sum, "Standard method")
        .def("sum2",
            [](MyTemplateClass<std::string> & self, const std::array<std::string, 2>& v) -> std::string
            {
                auto sum2_adapt_fixed_size_c_arrays = [&self](const std::array<std::string, 2>& v) -> std::string
                {
                    auto lambda_result = self.sum2(v.data());
                    return lambda_result;
                };

                return sum2_adapt_fixed_size_c_arrays(v);
            },
            py::arg("v"),
            "Method that requires a parameter adaptation")
        ;
    // #ifdef __cplusplus
    //
    // #endif
    //

    m.def("extern_c_add",
        extern_c_add, py::arg("a"), py::arg("b"));

    m.def("foo_void_param",
        foo_void_param);

    m.def("foo_unnamed_param",
        foo_unnamed_param, py::arg("param_0"), py::arg("param_1"), py::arg("param_2"));
    m.attr("ANSWER_ZERO_COMMENTED") = 0;
    m.attr("ANSWER_ONE_COMMENTED") = 1;
    m.attr("HEXVALUE") = 0x43242;
    m.attr("OCTALVALUE") = 043242;
    m.attr("STRING") = "Hello";
    m.attr("FLOAT") = 3.14;
    // #ifdef __cplusplus
    //
    // #endif
    //


    auto pyClassFooBrace =
        py::class_<FooBrace>
            (m, "FooBrace", "")
        .def(py::init<>([](
        std::vector<int> int_values = {1, 2, 3})
        {
            auto r = std::make_unique<FooBrace>();
            r->int_values = int_values;
            return r;
        })
        , py::arg("int_values") = std::vector<int>{1, 2, 3}
        )
        .def_readwrite("int_values", &FooBrace::int_values, "")
        .def_readwrite("dict_string_int", &FooBrace::dict_string_int, "")
        ;


    m.def("fn_brace",
        FnBrace, py::arg("foo_brace") = FooBrace{}, py::arg("ints") = std::vector<int>{1, 2, 3});

    { // <namespace MathFunctions>
        py::module_ pyNsMathFunctions = m.def_submodule("math_functions", " Vectorizable functions example\n    Numeric functions (i.e. function accepting and returning only numeric params or py::array), can be vectorized\n    i.e. they will accept numpy arrays as an input.\n\n Auto-vectorization is enabled via the following options:\n     options.fn_namespace_vectorize__regex: str = r\"^MathFunctions$\"\n     options.fn_vectorize__regex = r\".*\"\n");
        pyNsMathFunctions.def("vectorizable_sum",
            MathFunctions::vectorizable_sum, py::arg("x"), py::arg("y"));
        pyNsMathFunctions.def("vectorizable_sum",
            py::vectorize(MathFunctions::vectorizable_sum), py::arg("x"), py::arg("y"));
    } // </namespace MathFunctions>

    { // <namespace Animals>
        py::module_ pyNsAnimals = m.def_submodule("animals", "");
        auto pyNsAnimals_ClassAnimal =
            py::class_<Animals::Animal>
                (pyNsAnimals, "Animal", "")
            .def(py::init<const std::string &>(),
                py::arg("name"))
            .def_readwrite("name", &Animals::Animal::name, "")
            ;


        auto pyNsAnimals_ClassDog =
            py::class_<Animals::Dog, Animals::Animal>
                (pyNsAnimals, "Dog", "")
            .def(py::init<const std::string &>(),
                py::arg("name"))
            .def("bark",
                &Animals::Dog::bark)
            ;
    } // </namespace Animals>

    { // <namespace Home>
        py::module_ pyNsHome = m.def_submodule("home", "");
        auto pyNsHome_ClassPet =
            py::class_<Home::Pet>
                (pyNsHome, "Pet", "")
            .def(py::init<>()) // implicit default constructor
            .def("is_pet",
                &Home::Pet::is_pet)
            ;


        auto pyNsHome_ClassPetDog =
            py::class_<Home::PetDog, Animals::Dog, Home::Pet>
                (pyNsHome, "PetDog", "")
            .def(py::init<const std::string &>(),
                py::arg("name"))
            .def("bark",
                &Home::PetDog::bark)
            ;
    } // </namespace Home>

    { // <namespace AAA>
        py::module_ pyNsAAA = m.def_submodule("aaa", "");
        auto pyNsAAA_ClassCopyable_Template_int =
            py::class_<AAA::Copyable_Template<int>>
                (pyNsAAA, "Copyable_Template_int", "")
            .def(py::init<>([](
            int value = int())
            {
                auto r = std::make_unique<AAA::Copyable_Template<int>>();
                r->value = value;
                return r;
            })
            , py::arg("value") = int()
            )
            .def_readwrite("value", &AAA::Copyable_Template<int>::value, "")
            .def("__copy__",  [](const AAA::Copyable_Template<int> &self) {
                return AAA::Copyable_Template<int>(self);
            })
            .def("__deepcopy__",  [](const AAA::Copyable_Template<int> &self, py::dict) {
                return AAA::Copyable_Template<int>(self);
            }, py::arg("memo"))    ;
    } // </namespace AAA>

    { // <namespace Root>
        py::module_ pyNsRoot = m.def_submodule("root", "");
        { // <namespace Inner>
            py::module_ pyNsRoot_NsInner = pyNsRoot.def_submodule("inner", "");
            auto pyNsRoot_NsInner_ClassMyVirtualClass =
                py::class_<Root::Inner::MyVirtualClass, Root::Inner::MyVirtualClass_trampoline>
                    (pyNsRoot_NsInner, "MyVirtualClass", "")
                .def(py::init<>()) // implicit default constructor
                .def("foo_concrete",
                    &Root::Inner::MyVirtualClass::foo_concrete, py::arg("x"), py::arg("name"))
                .def("foo_virtual_public_pure",
                    &Root::Inner::MyVirtualClass::foo_virtual_public_pure)
                .def("foo_virtual_protected",
                    &Root::Inner::MyVirtualClass_publicist::foo_virtual_protected, py::arg("x"))
                .def("foo_virtual_protected_const_const",
                    &Root::Inner::MyVirtualClass_publicist::foo_virtual_protected_const_const, py::arg("name"))
                ;


            auto pyNsRoot_NsInner_ClassMyVirtualDerivate =
                py::class_<Root::Inner::MyVirtualDerivate, Root::Inner::MyVirtualClass, Root::Inner::MyVirtualDerivate_trampoline>
                    (pyNsRoot_NsInner, "MyVirtualDerivate", " Here, we test Combining virtual functions and inheritance\n See https://pybind11.readthedocs.io/en/stable/advanced/classes.html#combining-virtual-functions-and-inheritance")
                .def(py::init<>())
                .def("foo_virtual_public_pure",
                    &Root::Inner::MyVirtualDerivate::foo_virtual_public_pure)
                .def("foo_derivate",
                    &Root::Inner::MyVirtualDerivate::foo_derivate)
                ;
        } // </namespace Inner>

    } // </namespace Root>

    { // <namespace SomeNamespace>
        py::module_ pyNsSomeNamespace = m.def_submodule("some_namespace", "namespace SomeNamespace");
        auto pyNsSomeNamespace_ClassParentStruct =
            py::class_<SomeNamespace::ParentStruct>
                (pyNsSomeNamespace, "ParentStruct", "");

        { // inner classes & enums of ParentStruct
            auto pyNsSomeNamespace_ClassParentStruct_ClassInnerStruct =
                py::class_<SomeNamespace::ParentStruct::InnerStruct>
                    (pyNsSomeNamespace_ClassParentStruct, "InnerStruct", "")
                .def_readwrite("value", &SomeNamespace::ParentStruct::InnerStruct::value, "")
                .def(py::init<int>(),
                    py::arg("value") = 10)
                .def("add",
                    &SomeNamespace::ParentStruct::InnerStruct::add, py::arg("a"), py::arg("b"))
                ;
            py::enum_<SomeNamespace::ParentStruct::InnerEnum>(pyNsSomeNamespace_ClassParentStruct, "InnerEnum", py::arithmetic(), "")
                .value("zero", SomeNamespace::ParentStruct::InnerEnum::Zero, "")
                .value("one", SomeNamespace::ParentStruct::InnerEnum::One, "")
                .value("two", SomeNamespace::ParentStruct::InnerEnum::Two, "")
                .value("three", SomeNamespace::ParentStruct::InnerEnum::Three, "");
        } // end of inner classes & enums of ParentStruct

        pyNsSomeNamespace_ClassParentStruct
            .def(py::init<>([](
            SomeNamespace::ParentStruct::InnerStruct inner_struct = SomeNamespace::ParentStruct::InnerStruct(), SomeNamespace::ParentStruct::InnerEnum inner_enum = SomeNamespace::ParentStruct::InnerEnum::Three)
            {
                auto r = std::make_unique<SomeNamespace::ParentStruct>();
                r->inner_struct = inner_struct;
                r->inner_enum = inner_enum;
                return r;
            })
            , py::arg("inner_struct") = SomeNamespace::ParentStruct::InnerStruct(), py::arg("inner_enum") = SomeNamespace::ParentStruct::InnerEnum::Three
            )
            .def_readwrite("inner_struct", &SomeNamespace::ParentStruct::inner_struct, "")
            .def_readwrite("inner_enum", &SomeNamespace::ParentStruct::inner_enum, "")
            ;
        auto pyNsSomeNamespace_ClassBlah =
            py::class_<SomeNamespace::Blah>
                (pyNsSomeNamespace, "Blah", "struct Blah")
            .def(py::init<>()) // implicit default constructor
            .def("toggle_bool_pointer",
                [](SomeNamespace::Blah & self, BoxedBool & v)
                {
                    auto ToggleBoolPointer_adapt_modifiable_immutable = [&self](BoxedBool & v)
                    {
                        bool * v_boxed_value = & (v.value);

                        self.ToggleBoolPointer(v_boxed_value);
                    };

                    ToggleBoolPointer_adapt_modifiable_immutable(v);
                },
                py::arg("v"),
                "//, int vv[2])")
            .def("toggle_bool_pointer_get_points",
                [](SomeNamespace::Blah & self, BoxedBool & v, BoxedInt & vv_0, BoxedInt & vv_1)
                {
                    auto ToggleBoolPointerGetPoints_adapt_fixed_size_c_arrays = [&self](bool * v, BoxedInt & vv_0, BoxedInt & vv_1)
                    {
                        int vv_raw[2];
                        vv_raw[0] = vv_0.value;
                        vv_raw[1] = vv_1.value;

                        self.ToggleBoolPointerGetPoints(v, vv_raw);

                        vv_0.value = vv_raw[0];
                        vv_1.value = vv_raw[1];
                    };
                    auto ToggleBoolPointerGetPoints_adapt_modifiable_immutable = [&ToggleBoolPointerGetPoints_adapt_fixed_size_c_arrays](BoxedBool & v, BoxedInt & vv_0, BoxedInt & vv_1)
                    {
                        bool * v_boxed_value = & (v.value);

                        ToggleBoolPointerGetPoints_adapt_fixed_size_c_arrays(v_boxed_value, vv_0, vv_1);
                    };

                    ToggleBoolPointerGetPoints_adapt_modifiable_immutable(v, vv_0, vv_1);
                },     py::arg("v"), py::arg("vv_0"), py::arg("vv_1"))
            .def("modify_string",
                [](SomeNamespace::Blah & self, BoxedString & s)
                {
                    auto ModifyString_adapt_modifiable_immutable = [&self](BoxedString & s)
                    {
                        std::string * s_boxed_value = & (s.value);

                        self.ModifyString(s_boxed_value);
                    };

                    ModifyString_adapt_modifiable_immutable(s);
                },     py::arg("s"))
            .def("change_bool_int",
                [](SomeNamespace::Blah & self, const char * label, int value) -> std::tuple<bool, int>
                {
                    auto ChangeBoolInt_adapt_modifiable_immutable_to_return = [&self](const char * label, int value) -> std::tuple<bool, int>
                    {
                        int * value_adapt_modifiable = & value;

                        bool r = self.ChangeBoolInt(label, value_adapt_modifiable);
                        return std::make_tuple(r, value);
                    };

                    return ChangeBoolInt_adapt_modifiable_immutable_to_return(label, value);
                },     py::arg("label"), py::arg("value"))
            .def("add_inside_buffer",
                [](SomeNamespace::Blah & self, py::array & buffer, uint8_t number_to_add)
                {
                    auto add_inside_buffer_adapt_c_buffers = [&self](py::array & buffer, uint8_t number_to_add)
                    {
                        // convert py::array to C standard buffer (mutable)
                        void * buffer_from_pyarray = buffer.mutable_data();
                        py::ssize_t buffer_count = buffer.shape()[0];
                        char buffer_type = buffer.dtype().char_();
                        if (buffer_type != 'B')
                            throw std::runtime_error(std::string(R"msg(
                                    Bad type!  Expected a numpy array of native type:
                                                uint8_t *
                                            Which is equivalent to
                                                B
                                            (using py::array::dtype().char_() as an id)
                                )msg"));

                        self.add_inside_buffer(static_cast<uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), number_to_add);
                    };

                    add_inside_buffer_adapt_c_buffers(buffer, number_to_add);
                },     py::arg("buffer"), py::arg("number_to_add"))
            .def("templated_mul_inside_buffer",
                [](SomeNamespace::Blah & self, py::array & buffer, double factor)
                {
                    auto templated_mul_inside_buffer_adapt_c_buffers = [&self](py::array & buffer, double factor)
                    {
                        // convert py::array to C standard buffer (mutable)
                        void * buffer_from_pyarray = buffer.mutable_data();
                        py::ssize_t buffer_count = buffer.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 buffer_type = buffer.dtype().char_();
                        if (buffer_type == 'B')
                            self.templated_mul_inside_buffer(static_cast<uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'b')
                            self.templated_mul_inside_buffer(static_cast<int8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'H')
                            self.templated_mul_inside_buffer(static_cast<uint16_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'h')
                            self.templated_mul_inside_buffer(static_cast<int16_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'I')
                            self.templated_mul_inside_buffer(static_cast<uint32_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'i')
                            self.templated_mul_inside_buffer(static_cast<int32_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'L')
                            self.templated_mul_inside_buffer(static_cast<np_uint_l *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'l')
                            self.templated_mul_inside_buffer(static_cast<np_int_l *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'f')
                            self.templated_mul_inside_buffer(static_cast<float *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'd')
                            self.templated_mul_inside_buffer(static_cast<double *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'g')
                            self.templated_mul_inside_buffer(static_cast<long double *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'q')
                            self.templated_mul_inside_buffer(static_cast<long long *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        // If we reach this point, the array type is not supported!
                        else
                            throw std::runtime_error(std::string("Bad array type ('") + buffer_type + "') for param buffer");
                    };

                    templated_mul_inside_buffer_adapt_c_buffers(buffer, factor);
                },     py::arg("buffer"), py::arg("factor"))
            .def("const_array2_add",
                [](SomeNamespace::Blah & self, const std::array<int, 2>& values) -> int
                {
                    auto const_array2_add_adapt_fixed_size_c_arrays = [&self](const std::array<int, 2>& values) -> int
                    {
                        auto lambda_result = self.const_array2_add(values.data());
                        return lambda_result;
                    };

                    return const_array2_add_adapt_fixed_size_c_arrays(values);
                },     py::arg("values"))
            .def("c_string_list_total_size",
                [](SomeNamespace::Blah & self, const std::vector<std::string> & items, BoxedInt & output_0, BoxedInt & output_1) -> size_t
                {
                    auto c_string_list_total_size_adapt_fixed_size_c_arrays = [&self](const char * const items[], int items_count, BoxedInt & output_0, BoxedInt & output_1) -> size_t
                    {
                        int output_raw[2];
                        output_raw[0] = output_0.value;
                        output_raw[1] = output_1.value;

                        auto lambda_result = self.c_string_list_total_size(items, items_count, output_raw);

                        output_0.value = output_raw[0];
                        output_1.value = output_raw[1];
                        return lambda_result;
                    };
                    auto c_string_list_total_size_adapt_c_string_list = [&c_string_list_total_size_adapt_fixed_size_c_arrays](const std::vector<std::string> & items, BoxedInt & output_0, BoxedInt & output_1) -> size_t
                    {
                        std::vector<const char *> items_ptrs;
                        for (const auto& v: items)
                            items_ptrs.push_back(v.c_str());
                        int items_count = static_cast<int>(items.size());

                        auto lambda_result = c_string_list_total_size_adapt_fixed_size_c_arrays(items_ptrs.data(), items_count, output_0, output_1);
                        return lambda_result;
                    };

                    return c_string_list_total_size_adapt_c_string_list(items, output_0, output_1);
                },     py::arg("items"), py::arg("output_0"), py::arg("output_1"))
            ;
        { // <namespace SomeInnerNamespace>
            py::module_ pyNsSomeNamespace_NsSomeInnerNamespace = pyNsSomeNamespace.def_submodule("some_inner_namespace", "namespace SomeInnerNamespace");
            pyNsSomeNamespace_NsSomeInnerNamespace.def("toggle_bool_pointer",
                [](BoxedBool & v)
                {
                    auto ToggleBoolPointer_adapt_modifiable_immutable = [](BoxedBool & v)
                    {
                        bool * v_boxed_value = & (v.value);

                        SomeNamespace::SomeInnerNamespace::ToggleBoolPointer(v_boxed_value);
                    };

                    ToggleBoolPointer_adapt_modifiable_immutable(v);
                },
                py::arg("v"),
                "//, int vv[2])");

            pyNsSomeNamespace_NsSomeInnerNamespace.def("toggle_bool_pointer_get_points",
                [](BoxedBool & v, BoxedInt & vv_0, BoxedInt & vv_1)
                {
                    auto ToggleBoolPointerGetPoints_adapt_fixed_size_c_arrays = [](bool * v, BoxedInt & vv_0, BoxedInt & vv_1)
                    {
                        int vv_raw[2];
                        vv_raw[0] = vv_0.value;
                        vv_raw[1] = vv_1.value;

                        SomeNamespace::SomeInnerNamespace::ToggleBoolPointerGetPoints(v, vv_raw);

                        vv_0.value = vv_raw[0];
                        vv_1.value = vv_raw[1];
                    };
                    auto ToggleBoolPointerGetPoints_adapt_modifiable_immutable = [&ToggleBoolPointerGetPoints_adapt_fixed_size_c_arrays](BoxedBool & v, BoxedInt & vv_0, BoxedInt & vv_1)
                    {
                        bool * v_boxed_value = & (v.value);

                        ToggleBoolPointerGetPoints_adapt_fixed_size_c_arrays(v_boxed_value, vv_0, vv_1);
                    };

                    ToggleBoolPointerGetPoints_adapt_modifiable_immutable(v, vv_0, vv_1);
                },     py::arg("v"), py::arg("vv_0"), py::arg("vv_1"));

            pyNsSomeNamespace_NsSomeInnerNamespace.def("modify_string",
                [](BoxedString & s)
                {
                    auto ModifyString_adapt_modifiable_immutable = [](BoxedString & s)
                    {
                        std::string * s_boxed_value = & (s.value);

                        SomeNamespace::SomeInnerNamespace::ModifyString(s_boxed_value);
                    };

                    ModifyString_adapt_modifiable_immutable(s);
                },     py::arg("s"));

            pyNsSomeNamespace_NsSomeInnerNamespace.def("change_bool_int",
                [](const char * label, int value) -> std::tuple<bool, int>
                {
                    auto ChangeBoolInt_adapt_modifiable_immutable_to_return = [](const char * label, int value) -> std::tuple<bool, int>
                    {
                        int * value_adapt_modifiable = & value;

                        bool r = SomeNamespace::SomeInnerNamespace::ChangeBoolInt(label, value_adapt_modifiable);
                        return std::make_tuple(r, value);
                    };

                    return ChangeBoolInt_adapt_modifiable_immutable_to_return(label, value);
                },     py::arg("label"), py::arg("value"));

            pyNsSomeNamespace_NsSomeInnerNamespace.def("add_inside_buffer",
                [](py::array & buffer, uint8_t number_to_add)
                {
                    auto add_inside_buffer_adapt_c_buffers = [](py::array & buffer, uint8_t number_to_add)
                    {
                        // convert py::array to C standard buffer (mutable)
                        void * buffer_from_pyarray = buffer.mutable_data();
                        py::ssize_t buffer_count = buffer.shape()[0];
                        char buffer_type = buffer.dtype().char_();
                        if (buffer_type != 'B')
                            throw std::runtime_error(std::string(R"msg(
                                    Bad type!  Expected a numpy array of native type:
                                                uint8_t *
                                            Which is equivalent to
                                                B
                                            (using py::array::dtype().char_() as an id)
                                )msg"));

                        SomeNamespace::SomeInnerNamespace::add_inside_buffer(static_cast<uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), number_to_add);
                    };

                    add_inside_buffer_adapt_c_buffers(buffer, number_to_add);
                },     py::arg("buffer"), py::arg("number_to_add"));

            pyNsSomeNamespace_NsSomeInnerNamespace.def("templated_mul_inside_buffer",
                [](py::array & buffer, double factor)
                {
                    auto templated_mul_inside_buffer_adapt_c_buffers = [](py::array & buffer, double factor)
                    {
                        // convert py::array to C standard buffer (mutable)
                        void * buffer_from_pyarray = buffer.mutable_data();
                        py::ssize_t buffer_count = buffer.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 buffer_type = buffer.dtype().char_();
                        if (buffer_type == 'B')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<uint8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'b')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<int8_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'H')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<uint16_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'h')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<int16_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'I')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<uint32_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'i')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<int32_t *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'L')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<np_uint_l *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'l')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<np_int_l *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'f')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<float *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'd')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<double *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'g')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<long double *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        else if (buffer_type == 'q')
                            SomeNamespace::SomeInnerNamespace::templated_mul_inside_buffer(static_cast<long long *>(buffer_from_pyarray), static_cast<size_t>(buffer_count), factor);
                        // If we reach this point, the array type is not supported!
                        else
                            throw std::runtime_error(std::string("Bad array type ('") + buffer_type + "') for param buffer");
                    };

                    templated_mul_inside_buffer_adapt_c_buffers(buffer, factor);
                },     py::arg("buffer"), py::arg("factor"));

            pyNsSomeNamespace_NsSomeInnerNamespace.def("const_array2_add",
                [](const std::array<int, 2>& values) -> int
                {
                    auto const_array2_add_adapt_fixed_size_c_arrays = [](const std::array<int, 2>& values) -> int
                    {
                        auto lambda_result = SomeNamespace::SomeInnerNamespace::const_array2_add(values.data());
                        return lambda_result;
                    };

                    return const_array2_add_adapt_fixed_size_c_arrays(values);
                },     py::arg("values"));

            pyNsSomeNamespace_NsSomeInnerNamespace.def("c_string_list_total_size",
                [](const std::vector<std::string> & items, BoxedInt & output_0, BoxedInt & output_1) -> size_t
                {
                    auto c_string_list_total_size_adapt_fixed_size_c_arrays = [](const char * const items[], int items_count, BoxedInt & output_0, BoxedInt & output_1) -> size_t
                    {
                        int output_raw[2];
                        output_raw[0] = output_0.value;
                        output_raw[1] = output_1.value;

                        auto lambda_result = SomeNamespace::SomeInnerNamespace::c_string_list_total_size(items, items_count, output_raw);

                        output_0.value = output_raw[0];
                        output_1.value = output_raw[1];
                        return lambda_result;
                    };
                    auto c_string_list_total_size_adapt_c_string_list = [&c_string_list_total_size_adapt_fixed_size_c_arrays](const std::vector<std::string> & items, BoxedInt & output_0, BoxedInt & output_1) -> size_t
                    {
                        std::vector<const char *> items_ptrs;
                        for (const auto& v: items)
                            items_ptrs.push_back(v.c_str());
                        int items_count = static_cast<int>(items.size());

                        auto lambda_result = c_string_list_total_size_adapt_fixed_size_c_arrays(items_ptrs.data(), items_count, output_0, output_1);
                        return lambda_result;
                    };

                    return c_string_list_total_size_adapt_c_string_list(items, output_0, output_1);
                },     py::arg("items"), py::arg("output_0"), py::arg("output_1"));
        } // </namespace SomeInnerNamespace>

    } // </namespace SomeNamespace>

    { // <namespace Inner>
        py::module_ pyNsInner = m.def_submodule("inner", "this is an inner namespace (this comment should become the namespace doc)");
        pyNsInner.def("foo_inner",
            Mylib::Inner::FooInner);
        pyNsInner.def("foo_inner2",
            Mylib::Inner::FooInner2);
    } // </namespace Inner>

    { // <namespace N>
        py::module_ pyNsN = m.def_submodule("n", "");
        auto pyNsN_ClassS =
            py::class_<N::S>
                (pyNsN, "S", "")
            .def(py::init<>()) // implicit default constructor
            ;


        py::enum_<N::EC>(pyNsN, "EC", py::arithmetic(), "")
            .value("a", N::EC::a, "");


        py::enum_<N::E>(pyNsN, "E", py::arithmetic(), "")
            .value("a", N::E_a, "");


        pyNsN.def("foo",
            py::overload_cast<N::EC>(N::Foo), py::arg("e") = N::EC::a);

        pyNsN.def("foo",
            py::overload_cast<N::E>(N::Foo), py::arg("e") = N::E_a);

        pyNsN.def("foo",
            py::overload_cast<N::E, N::S>(N::Foo), py::arg("e") = N::E_a, py::arg("s") = N::S());
    } // </namespace N>

    { // <namespace A>
        py::module_ pyNsA = m.def_submodule("a", "");
        py::enum_<A::Foo>(pyNsA, "Foo", py::arithmetic(), "")
            .value("foo1", A::Foo::Foo1, "")
            .value("foo2", A::Foo::Foo2, "")
            .value("foo3", A::Foo::Foo3, "");


        auto pyNsA_ClassClassNoDefaultCtor =
            py::class_<A::ClassNoDefaultCtor>
                (pyNsA, "ClassNoDefaultCtor", " This struct has no default constructor, so a default named constructor\n will be provided for python")
            .def(py::init<>([](
            bool b = true, int a = int(), int c = 3, A::Foo foo = A::Foo::Foo1)
            {
                auto r = std::make_unique<A::ClassNoDefaultCtor>();
                r->b = b;
                r->a = a;
                r->c = c;
                r->foo = foo;
                return r;
            })
            , py::arg("b") = true, py::arg("a") = int(), py::arg("c") = 3, py::arg("foo") = A::Foo::Foo1
            )
            .def_readwrite("b", &A::ClassNoDefaultCtor::b, "")
            .def_readwrite("a", &A::ClassNoDefaultCtor::a, "")
            .def_readwrite("c", &A::ClassNoDefaultCtor::c, "")
            .def_readwrite("foo", &A::ClassNoDefaultCtor::foo, "")
            .def_readonly("s", &A::ClassNoDefaultCtor::s, "")
            ;
    } // </namespace A>
    ////////////////////    </generated_from:mylib_amalgamation.h>    ////////////////////

    // </litgen_pydef> // Autogenerated code end
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  AUTOGENERATED CODE END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
