{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Classes and structs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exclude members and classes\n", "\n", "Sometimes, you may want to exclude classes and/or members from the bindings. Look at the example below for instructions:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    class FooDetails // A class that we want to exclude from the bindings\n",
       "    {\n",
       "        // ...\n",
       "    };\n",
       "\n",
       "    struct Foo \n",
       "    {\n",
       "        int X = 0, Y = 1;\n",
       "\n",
       "        FooDetails mDetails = {}; // A member that we would want to exclude from the bindings\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Foo:\n",
       "    x: int = 0\n",
       "    y: int = 1\n",
       "\n",
       "    def __init__(self, x: int = 0, y: int = 1) -> None:\n",
       "        """Auto-generated default constructor with named params"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassFoo =\n",
       "    py::class_<Foo>\n",
       "        (m, "Foo", "")\n",
       "    .def(py::init<>([](\n",
       "    int X = 0, int Y = 1)\n",
       "    {\n",
       "        auto r = std::make_unique<Foo>();\n",
       "        r->X = X;\n",
       "        r->Y = Y;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("x") = 0, py::arg("y") = 1\n",
       "    )\n",
       "    .def_readwrite("x", &Foo::X, "")\n",
       "    .def_readwrite("y", &Foo::Y, "")\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import litgen\n", "from litgen.demo import litgen_demo\n", "\n", "cpp_code = \"\"\"\n", " class FooDetails // A class that we want to exclude from the bindings\n", " {\n", " // ...\n", " };\n", "\n", " struct Foo \n", " {\n", " int X = 0, Y = 1;\n", "\n", " FooDetails mDetails = {}; // A member that we would want to exclude from the bindings\n", " };\n", "\"\"\"\n", "\n", "\n", "options = litgen.LitgenOptions()\n", "options.class_exclude_by_name__regex = r\"Details$\"\n", "options.member_exclude_by_name__regex = r\"Details$\"\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Default constructor with named params\n", "\n", "litgen will automatically generate a default constructor with named params for structs. For classes, you can use `options.class_create_default_named_ctor__regex`.\n", "This constructor is generated only if the class/struct does not provide a default constructor.\n", "\n", "See example below:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    enum class Options { A, B };\n",
       "\n",
       "    // A constructor with named params will be created for FooClass, \n",
       "    // since options.class_create_default_named_ctor__regex  was filled\n",
       "    class FooClass { \n",
       "      public:\n",
       "        Options options = Options::A;\n",
       "        int a = 1;\n",
       "    };\n",
       "\n",
       "    // A constructor with named params will be created for FooStruct\n",
       "    struct FooStruct { int X = 0, Y = 1; };\n",
       "\n",
       "    // FooStruct has a default constructor, so no constructor with named params will be generated\n",
       "    struct FooStruct2 {\n",
       "        FooStruct2();\n",
       "        int X = 0, Y = 1;\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Options(enum.Enum):\n",
       "    a = enum.auto() # (= 0)\n",
       "    b = enum.auto() # (= 1)\n",
       "\n",
       "class FooClass:\n",
       "    """ A constructor with named params will be created for FooClass,\n",
       "     since options.class_create_default_named_ctor__regex  was filled\n",
       "    """\n",
       "    options: Options = Options.a\n",
       "    a: int = 1\n",
       "    def __init__(self, options: Options = Options.a, a: int = 1) -> None:\n",
       "        """Auto-generated default constructor with named params"""\n",
       "        pass\n",
       "\n",
       "class FooStruct:\n",
       "    """ A constructor with named params will be created for FooStruct"""\n",
       "    x: int = 0\n",
       "    y: int = 1\n",
       "    def __init__(self, x: int = 0, y: int = 1) -> None:\n",
       "        """Auto-generated default constructor with named params"""\n",
       "        pass\n",
       "\n",
       "class FooStruct2:\n",
       "    """ FooStruct has a default constructor, so no constructor with named params will be generated"""\n",
       "    def __init__(self) -> None:\n",
       "        pass\n",
       "    x: int = 0\n",
       "    y: int = 1\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
py::enum_<Options>(m, "Options", py::arithmetic(), "")\n",
       "    .value("a", Options::A, "")\n",
       "    .value("b", Options::B, "");\n",
       "\n",
       "\n",
       "auto pyClassFooClass =\n",
       "    py::class_<FooClass>\n",
       "        (m, "FooClass", " A constructor with named params will be created for FooClass,\\n since options.class_create_default_named_ctor__regex  was filled")\n",
       "    .def(py::init<>([](\n",
       "    Options options = Options::A, int a = 1)\n",
       "    {\n",
       "        auto r = std::make_unique<FooClass>();\n",
       "        r->options = options;\n",
       "        r->a = a;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("options") = Options::A, py::arg("a") = 1\n",
       "    )\n",
       "    .def_readwrite("options", &FooClass::options, "")\n",
       "    .def_readwrite("a", &FooClass::a, "")\n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassFooStruct =\n",
       "    py::class_<FooStruct>\n",
       "        (m, "FooStruct", "A constructor with named params will be created for FooStruct")\n",
       "    .def(py::init<>([](\n",
       "    int X = 0, int Y = 1)\n",
       "    {\n",
       "        auto r = std::make_unique<FooStruct>();\n",
       "        r->X = X;\n",
       "        r->Y = Y;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("x") = 0, py::arg("y") = 1\n",
       "    )\n",
       "    .def_readwrite("x", &FooStruct::X, "")\n",
       "    .def_readwrite("y", &FooStruct::Y, "")\n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassFooStruct2 =\n",
       "    py::class_<FooStruct2>\n",
       "        (m, "FooStruct2", "FooStruct has a default constructor, so no constructor with named params will be generated")\n",
       "    .def(py::init<>())\n",
       "    .def_readwrite("x", &FooStruct2::X, "")\n",
       "    .def_readwrite("y", &FooStruct2::Y, "")\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " enum class Options { A, B };\n", "\n", " // A constructor with named params will be created for FooClass, \n", " // since options.class_create_default_named_ctor__regex was filled\n", " class FooClass { \n", " public:\n", " Options options = Options::A;\n", " int a = 1;\n", " };\n", "\n", " // A constructor with named params will be created for FooStruct\n", " struct FooStruct { int X = 0, Y = 1; };\n", "\n", " // FooStruct has a default constructor, so no constructor with named params will be generated\n", " struct FooStruct2 {\n", " FooStruct2();\n", " int X = 0, Y = 1;\n", " };\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "# options.struct_create_default_named_ctor__regex = r\".*\"\n", "options.class_create_default_named_ctor__regex = r\".*\"\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Expose protected member functions\n", "\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#binding-protected-member-functions) of the pybind11 manual.\n", "\n", "Exposing protected member functions requires the creation of a \"Publicist\" helper class. litgen enables to automate this:\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class A {\n",
       "protected:\n",
       "    int foo() const { return 42; }\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class A:\n",
       "    def __init__(self) -> None:\n",
       "        """Autogenerated default constructor"""\n",
       "        pass\n",
       "\n",
       "    # <protected_methods>\n",
       "    def foo(self) -> int:\n",
       "        pass\n",
       "    # </protected_methods>\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassA =\n",
       "    py::class_<A>\n",
       "        (m, "A", "")\n",
       "    .def(py::init<>()) // implicit default constructor\n",
       "    .def("foo",\n",
       "        &A_publicist::foo)\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
// helper type for exposing protected functions\n",
       "class A_publicist : public A\n",
       "{\n",
       "public:\n",
       "    using A::foo;\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", "class A {\n", "protected:\n", " int foo() const { return 42; }\n", "};\n", "\"\"\"\n", "\n", "options = litgen.LitgenOptions()\n", "options.class_expose_protected_methods__regex = \"^A$\"\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overriding virtual methods in Python\n", "\n", "\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python) of the pybind11 manual.\n", "\n", "In order to override virtual methods in Python, you need to create of a trampoline class, which can be a bit cumbersome. \n", "\n", "litgen can automate this: look at the pybind11 binding code, and at the glue code below." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    class Animal {\n",
       "    public:\n",
       "        virtual ~Animal() { }\n",
       "        virtual std::string go(int n_times) = 0;\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Animal:\n",
       "    def go(self, n_times: int) -> str: # overridable (pure virtual)\n",
       "        pass\n",
       "    def __init__(self) -> None:\n",
       "        """Autogenerated default constructor"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassAnimal =\n",
       "    py::class_<Animal, Animal_trampoline>\n",
       "        (m, "Animal", "")\n",
       "    .def(py::init<>()) // implicit default constructor\n",
       "    .def("go",\n",
       "        &Animal::go, py::arg("n_times"))\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
// helper type to enable overriding virtual methods in python\n",
       "class Animal_trampoline : public Animal\n",
       "{\n",
       "public:\n",
       "    using Animal::Animal;\n",
       "\n",
       "    std::string go(int n_times) override\n",
       "    {\n",
       "        PYBIND11_OVERRIDE_PURE_NAME(\n",
       "            std::string, // return type\n",
       "            Animal, // parent class\n",
       "            "go", // function name (python)\n",
       "            go, // function name (c++)\n",
       "            n_times // params\n",
       "        );\n",
       "    }\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " class Animal {\n", " public:\n", " virtual ~Animal() { }\n", " virtual std::string go(int n_times) = 0;\n", " };\n", "\"\"\"\n", "\n", "options = litgen.LitgenOptions()\n", "options.class_override_virtual_methods_in_python__regex = \"^Animal$\"\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Combining virtual functions and inheritance\n", "\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#combining-virtual-functions-and-inheritance) of the pybind11 manual.\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    class Animal {\n",
       "    public:\n",
       "        virtual std::string go(int n_times) = 0;\n",
       "        virtual std::string name() { return "unknown"; }\n",
       "    };\n",
       "\n",
       "    class Dog : public Animal {\n",
       "    public:\n",
       "        std::string go(int n_times) override {\n",
       "            std::string result;\n",
       "            for (int i=0; i<n_times; ++i)\n",
       "                result += bark() + " ";\n",
       "            return result;\n",
       "        }\n",
       "        virtual std::string bark() { return "woof!"; }\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Animal:\n",
       "    def go(self, n_times: int) -> str: # overridable (pure virtual)\n",
       "        pass\n",
       "    def name(self) -> str:             # overridable\n",
       "        pass\n",
       "    def __init__(self) -> None:\n",
       "        """Autogenerated default constructor"""\n",
       "        pass\n",
       "\n",
       "class Dog(Animal):\n",
       "    def go(self, n_times: int) -> str: # overridable\n",
       "        pass\n",
       "    def bark(self) -> str:             # overridable\n",
       "        pass\n",
       "    def __init__(self) -> None:\n",
       "        """Autogenerated default constructor"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassAnimal =\n",
       "    py::class_<Animal, Animal_trampoline>\n",
       "        (m, "Animal", "")\n",
       "    .def(py::init<>()) // implicit default constructor\n",
       "    .def("go",\n",
       "        &Animal::go, py::arg("n_times"))\n",
       "    .def("name",\n",
       "        &Animal::name)\n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassDog =\n",
       "    py::class_<Dog, Animal, Dog_trampoline>\n",
       "        (m, "Dog", "")\n",
       "    .def(py::init<>()) // implicit default constructor\n",
       "    .def("go",\n",
       "        &Dog::go, py::arg("n_times"))\n",
       "    .def("bark",\n",
       "        &Dog::bark)\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
// helper type to enable overriding virtual methods in python\n",
       "class Animal_trampoline : public Animal\n",
       "{\n",
       "public:\n",
       "    using Animal::Animal;\n",
       "\n",
       "    std::string go(int n_times) override\n",
       "    {\n",
       "        PYBIND11_OVERRIDE_PURE_NAME(\n",
       "            std::string, // return type\n",
       "            Animal, // parent class\n",
       "            "go", // function name (python)\n",
       "            go, // function name (c++)\n",
       "            n_times // params\n",
       "        );\n",
       "    }\n",
       "    std::string name() override\n",
       "    {\n",
       "        PYBIND11_OVERRIDE_NAME(\n",
       "            std::string, // return type\n",
       "            Animal, // parent class\n",
       "            "name", // function name (python)\n",
       "            name // function name (c++)\n",
       "        );\n",
       "    }\n",
       "};\n",
       "\n",
       "\n",
       "\n",
       "// helper type to enable overriding virtual methods in python\n",
       "class Dog_trampoline : public Dog\n",
       "{\n",
       "public:\n",
       "    using Dog::Dog;\n",
       "\n",
       "    std::string go(int n_times) override\n",
       "    {\n",
       "        PYBIND11_OVERRIDE_NAME(\n",
       "            std::string, // return type\n",
       "            Dog, // parent class\n",
       "            "go", // function name (python)\n",
       "            go, // function name (c++)\n",
       "            n_times // params\n",
       "        );\n",
       "    }\n",
       "    std::string bark() override\n",
       "    {\n",
       "        PYBIND11_OVERRIDE_NAME(\n",
       "            std::string, // return type\n",
       "            Dog, // parent class\n",
       "            "bark", // function name (python)\n",
       "            bark // function name (c++)\n",
       "        );\n",
       "    }\n",
       "    std::string name() override\n",
       "    {\n",
       "        PYBIND11_OVERRIDE_NAME(\n",
       "            std::string, // return type\n",
       "            Dog, // parent class\n",
       "            "name", // function name (python)\n",
       "            name // function name (c++)\n",
       "        );\n",
       "    }\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " class Animal {\n", " public:\n", " virtual std::string go(int n_times) = 0;\n", " virtual std::string name() { return \"unknown\"; }\n", " };\n", "\n", " class Dog : public Animal {\n", " public:\n", " std::string go(int n_times) override {\n", " std::string result;\n", " for (int i=0; i\n", ".collapsible_header {\n", " background-color: #AAAAAA;\n", " color: white;\n", " cursor: pointer;\n", " padding: 3px;\n", " width: 100%;\n", " border: none;\n", " text-align: left;\n", " outline: none;\n", " font-style: italic;\n", "}\n", "\n", ".collapsible_header_opened {\n", " background-color: #555;\n", "}\n", "\n", ".collapsible_header:hover {\n", " background-color: #AAAAFF;\n", "}\n", "\n", ".collapsible_header:after {\n", " content: '\\002B';\n", " color: white;\n", " font-weight: bold;\n", " float: right;\n", " margin-left: 5px;\n", "}\n", "\n", ".collapsible_header_opened:after {\n", " content: \"\\2212\";\n", "}\n", "\n", ".collapsible_content {\n", " padding: 0 18px;\n", " max-height: 0;\n", " overflow-x: scroll;\n", " overflow-y: scroll;\n", " min-width: 100%;\n", " transition: max-height 0.2s ease-out;\n", " background-color: #f1f1f1;\n", "}\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
        struct IntWrapper\n",
       "        {\n",
       "            int value;\n",
       "            IntWrapper(int v) : value(v) {}\n",
       "\n",
       "            // arithmetic operators\n",
       "            IntWrapper operator+(IntWrapper b) { return IntWrapper{ value + b.value}; }\n",
       "            IntWrapper operator-(IntWrapper b) { return IntWrapper{ value - b.value }; }\n",
       "\n",
       "            // Unary minus operator\n",
       "            IntWrapper operator-() { return IntWrapper{ -value }; }\n",
       "\n",
       "            // Comparison operator\n",
       "            bool operator<(IntWrapper b) { return value < b.value; }\n",
       "\n",
       "            // Two overload of the += operator\n",
       "            IntWrapper operator+=(IntWrapper b) { value += b.value; return *this; }\n",
       "            IntWrapper operator+=(int b) { value += b; return *this; }\n",
       "\n",
       "            // Two overload of the call operator, with different results\n",
       "            int operator()(IntWrapper b) { return value * b.value + 2; }\n",
       "            int operator()(int b) { return value * b + 3; }\n",
       "        };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class IntWrapper:\n",
       "    value: int\n",
       "    def __init__(self, v: int) -> None:\n",
       "        pass\n",
       "\n",
       "    # arithmetic operators\n",
       "    def __add__(self, b: IntWrapper) -> IntWrapper:\n",
       "        pass\n",
       "    @overload\n",
       "    def __sub__(self, b: IntWrapper) -> IntWrapper:\n",
       "        pass\n",
       "\n",
       "    @overload\n",
       "    def __neg__(self) -> IntWrapper:\n",
       "        """ Unary minus operator"""\n",
       "        pass\n",
       "\n",
       "    def __lt__(self, b: IntWrapper) -> bool:\n",
       "        """ Comparison operator"""\n",
       "        pass\n",
       "\n",
       "    # Two overload of the += operator\n",
       "    @overload\n",
       "    def __iadd__(self, b: IntWrapper) -> IntWrapper:\n",
       "        pass\n",
       "    @overload\n",
       "    def __iadd__(self, b: int) -> IntWrapper:\n",
       "        pass\n",
       "\n",
       "    # Two overload of the call operator, with different results\n",
       "    @overload\n",
       "    def __call__(self, b: IntWrapper) -> int:\n",
       "        pass\n",
       "    @overload\n",
       "    def __call__(self, b: int) -> int:\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassIntWrapper =\n",
       "    py::class_<IntWrapper>\n",
       "        (m, "IntWrapper", "")\n",
       "    .def_readwrite("value", &IntWrapper::value, "")\n",
       "    .def(py::init<int>(),\n",
       "        py::arg("v"))\n",
       "    .def("__add__",\n",
       "        &IntWrapper::operator+, py::arg("b"))\n",
       "    .def("__sub__",\n",
       "        py::overload_cast<IntWrapper>(&IntWrapper::operator-), py::arg("b"))\n",
       "    .def("__neg__",\n",
       "        [](IntWrapper & self) { return self.operator-(); }, "Unary minus operator")\n",
       "    .def("__lt__",\n",
       "        &IntWrapper::operator<,\n",
       "        py::arg("b"),\n",
       "        "Comparison operator")\n",
       "    .def("__iadd__",\n",
       "        py::overload_cast<IntWrapper>(&IntWrapper::operator+=), py::arg("b"))\n",
       "    .def("__iadd__",\n",
       "        py::overload_cast<int>(&IntWrapper::operator+=), py::arg("b"))\n",
       "    .def("__call__",\n",
       "        py::overload_cast<IntWrapper>(&IntWrapper::operator()), py::arg("b"))\n",
       "    .def("__call__",\n",
       "        py::overload_cast<int>(&IntWrapper::operator()), py::arg("b"))\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " struct IntWrapper\n", " {\n", " int value;\n", " IntWrapper(int v) : value(v) {}\n", "\n", " // arithmetic operators\n", " IntWrapper operator+(IntWrapper b) { return IntWrapper{ value + b.value}; }\n", " IntWrapper operator-(IntWrapper b) { return IntWrapper{ value - b.value }; }\n", "\n", " // Unary minus operator\n", " IntWrapper operator-() { return IntWrapper{ -value }; }\n", "\n", " // Comparison operator\n", " bool operator<(IntWrapper b) { return value < b.value; }\n", "\n", " // Two overload of the += operator\n", " IntWrapper operator+=(IntWrapper b) { value += b.value; return *this; }\n", " IntWrapper operator+=(int b) { value += b; return *this; }\n", "\n", " // Two overload of the call operator, with different results\n", " int operator()(IntWrapper b) { return value * b.value + 2; }\n", " int operator()(int b) { return value * b + 3; }\n", " };\n", "\"\"\"\n", "\n", "options = litgen.LitgenOptions()\n", "litgen_demo.demo(options, cpp_code, height=60)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Overloading comparisons with the spaceship operator" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    struct Point\n",
       "    {\n",
       "        int x;\n",
       "        int y;\n",
       "        auto operator<=>(const Point&) const = default;\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Point:\n",
       "    x: int\n",
       "    y: int\n",
       "    def __lt__(self, param_0: Point) -> bool:\n",
       "        """\n",
       "        (C++ auto return type)\n",
       "        """\n",
       "        pass\n",
       "    def __le__(self, param_0: Point) -> bool:\n",
       "        """\n",
       "        (C++ auto return type)\n",
       "        """\n",
       "        pass\n",
       "    def __eq__(self, param_0: Point) -> bool:\n",
       "        """\n",
       "        (C++ auto return type)\n",
       "        """\n",
       "        pass\n",
       "    def __ge__(self, param_0: Point) -> bool:\n",
       "        """\n",
       "        (C++ auto return type)\n",
       "        """\n",
       "        pass\n",
       "    def __gt__(self, param_0: Point) -> bool:\n",
       "        """\n",
       "        (C++ auto return type)\n",
       "        """\n",
       "        pass\n",
       "    def __init__(self, x: int = int(), y: int = int()) -> None:\n",
       "        """Auto-generated default constructor with named params"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassPoint =\n",
       "    py::class_<Point>\n",
       "        (m, "Point", "")\n",
       "    .def(py::init<>([](\n",
       "    int x = int(), int y = int())\n",
       "    {\n",
       "        auto r = std::make_unique<Point>();\n",
       "        r->x = x;\n",
       "        r->y = y;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("x") = int(), py::arg("y") = int()\n",
       "    )\n",
       "    .def_readwrite("x", &Point::x, "")\n",
       "    .def_readwrite("y", &Point::y, "")\n",
       "    .def("__lt__",\n",
       "        [](const Point & self, const Point & param_0) -> bool\n",
       "        {\n",
       "            auto cmp = [&self](auto&& other) -> bool {\n",
       "                return self.operator<=>(other)  < 0;\n",
       "            };\n",
       "\n",
       "            return cmp(param_0);\n",
       "        },\n",
       "        py::arg("param_0"),\n",
       "        "\\n(C++ auto return type)")\n",
       "    .def("__le__",\n",
       "        [](const Point & self, const Point & param_0) -> bool\n",
       "        {\n",
       "            auto cmp = [&self](auto&& other) -> bool {\n",
       "                return self.operator<=>(other)  <= 0;\n",
       "            };\n",
       "\n",
       "            return cmp(param_0);\n",
       "        },\n",
       "        py::arg("param_0"),\n",
       "        "\\n(C++ auto return type)")\n",
       "    .def("__eq__",\n",
       "        [](const Point & self, const Point & param_0) -> bool\n",
       "        {\n",
       "            auto cmp = [&self](auto&& other) -> bool {\n",
       "                return self.operator<=>(other)  == 0;\n",
       "            };\n",
       "\n",
       "            return cmp(param_0);\n",
       "        },\n",
       "        py::arg("param_0"),\n",
       "        "\\n(C++ auto return type)")\n",
       "    .def("__ge__",\n",
       "        [](const Point & self, const Point & param_0) -> bool\n",
       "        {\n",
       "            auto cmp = [&self](auto&& other) -> bool {\n",
       "                return self.operator<=>(other)  >= 0;\n",
       "            };\n",
       "\n",
       "            return cmp(param_0);\n",
       "        },\n",
       "        py::arg("param_0"),\n",
       "        "\\n(C++ auto return type)")\n",
       "    .def("__gt__",\n",
       "        [](const Point & self, const Point & param_0) -> bool\n",
       "        {\n",
       "            auto cmp = [&self](auto&& other) -> bool {\n",
       "                return self.operator<=>(other)  > 0;\n",
       "            };\n",
       "\n",
       "            return cmp(param_0);\n",
       "        },\n",
       "        py::arg("param_0"),\n",
       "        "\\n(C++ auto return type)")\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " struct Point\n", " {\n", " int x;\n", " int y;\n", " auto operator<=>(const Point&) const = default;\n", " };\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dynamic attributes\n", "\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/classes.html?highlight=dynamic#dynamic-attributes) of the pybind11 manual.\n", "\n", "The python class `Pet` below will be able to pick up new attributes dynamically:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
struct Pet {\n",
       "    Pet(const std::string &name) : name(name) { }\n",
       "    std::string name;\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Pet:\n",
       "    def __init__(self, name: str) -> None:\n",
       "        pass\n",
       "    name: str\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassPet =\n",
       "    py::class_<Pet>\n",
       "        (m, "Pet", py::dynamic_attr(), "")\n",
       "    .def(py::init<const std::string &>(),\n",
       "        py::arg("name"))\n",
       "    .def_readwrite("name", &Pet::name, "")\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", "struct Pet {\n", " Pet(const std::string &name) : name(name) { }\n", " std::string name;\n", "};\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "options.class_dynamic_attributes__regex = \"^Pet$\"\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Copy and Deep copy\n", "\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#deepcopy-support) of the pybind11 manual\n", "\n", "See below for instructions on how to add support for copy and deepcopy." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    struct Foo { std::vector<int> values; };\n",
       "    struct Foo2 { \n",
       "        Foo foo1 = Foo();\n",
       "        Foo foo2 = Foo();\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Foo:\n",
       "    """\n",
       "    (has support for copy.copy)\n",
       "    """\n",
       "    values: List[int]\n",
       "    def __init__(self, values: List[int] = List[int]()) -> None:\n",
       "        """Auto-generated default constructor with named params"""\n",
       "        pass\n",
       "class Foo2:\n",
       "    """\n",
       "    (has support for copy.copy and copy.deepcopy)\n",
       "    """\n",
       "    foo1: Foo = Foo()\n",
       "    foo2: Foo = Foo()\n",
       "    def __init__(self, foo1: Foo = Foo(), foo2: Foo = Foo()) -> None:\n",
       "        """Auto-generated default constructor with named params"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassFoo =\n",
       "    py::class_<Foo>\n",
       "        (m, "Foo", "\\n(has support for copy.copy)")\n",
       "    .def(py::init<>([](\n",
       "    std::vector<int> values = std::vector<int>())\n",
       "    {\n",
       "        auto r = std::make_unique<Foo>();\n",
       "        r->values = values;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("values") = std::vector<int>()\n",
       "    )\n",
       "    .def_readwrite("values", &Foo::values, "")\n",
       "    .def("__copy__",  [](const Foo &self) {\n",
       "        return Foo(self);\n",
       "    })    ;\n",
       "\n",
       "\n",
       "auto pyClassFoo2 =\n",
       "    py::class_<Foo2>\n",
       "        (m, "Foo2", "\\n(has support for copy.copy and copy.deepcopy)")\n",
       "    .def(py::init<>([](\n",
       "    Foo foo1 = Foo(), Foo foo2 = Foo())\n",
       "    {\n",
       "        auto r = std::make_unique<Foo2>();\n",
       "        r->foo1 = foo1;\n",
       "        r->foo2 = foo2;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("foo1") = Foo(), py::arg("foo2") = Foo()\n",
       "    )\n",
       "    .def_readwrite("foo1", &Foo2::foo1, "")\n",
       "    .def_readwrite("foo2", &Foo2::foo2, "")\n",
       "    .def("__copy__",  [](const Foo2 &self) {\n",
       "        return Foo2(self);\n",
       "    })\n",
       "    .def("__deepcopy__",  [](const Foo2 &self, py::dict) {\n",
       "        return Foo2(self);\n",
       "    }, py::arg("memo"))    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " struct Foo { std::vector values; };\n", " struct Foo2 { \n", " Foo foo1 = Foo();\n", " Foo foo2 = Foo();\n", " };\n", "\"\"\"\n", "\n", "options = litgen.LitgenOptions()\n", "options.class_copy__regex = \"^Foo$|^Foo2$\"\n", "options.class_deep_copy__regex = \"^Foo2$\"\n", "options.class_copy_add_info_in_stub = True\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Iterable classes\n", "\n", "It is possible to make custom container classes iterable in python. See example below:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class MyContainer\n",
       "{\n",
       "public:\n",
       "    float operator[](int idx);\n",
       "\n",
       "    // We need to have defined begin(), end(), and size() to make the class iterable\n",
       "    iterator begin(); // This function is excluded from the bindings (see options.fn_exclude_by_name__regex)\n",
       "    iterator end();   // This function is excluded from the bindings (see options.fn_exclude_by_name__regex)\n",
       "    size_t size();    // This function is also published as __len__\n",
       "private:\n",
       "    std::vector<float> values;\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class MyContainer:\n",
       "    def __getitem__(self, idx: int) -> float:\n",
       "        pass\n",
       "\n",
       "    # We need to have defined begin(), end(), and size() to make the class iterable\n",
       "    def size(self) -> int:\n",
       "        """ This function is also published as __len__"""\n",
       "        pass\n",
       "    def __init__(self) -> None:\n",
       "        """Autogenerated default constructor"""\n",
       "        pass\n",
       "    def __iter__(self) -> Iterator[float]:\n",
       "        pass\n",
       "    def __len__(self) -> int:\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassMyContainer =\n",
       "    py::class_<MyContainer>\n",
       "        (m, "MyContainer", "")\n",
       "    .def(py::init<>()) // implicit default constructor\n",
       "    .def("__getitem__",\n",
       "        &MyContainer::operator[], py::arg("idx"))\n",
       "    .def("size",\n",
       "        &MyContainer::size, "This function is also published as __len__")\n",
       "    .def("__iter__", [](const MyContainer &v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>())\n",
       "    .def("__len__", [](const MyContainer &v) { return v.size(); })\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", "class MyContainer\n", "{\n", "public:\n", " float operator[](int idx);\n", "\n", " // We need to have defined begin(), end(), and size() to make the class iterable\n", " iterator begin(); // This function is excluded from the bindings (see options.fn_exclude_by_name__regex)\n", " iterator end(); // This function is excluded from the bindings (see options.fn_exclude_by_name__regex)\n", " size_t size(); // This function is also published as __len__\n", "private:\n", " std::vector values;\n", "};\n", "\"\"\"\n", "\n", "options = litgen.LitgenOptions()\n", "options.class_iterables_infos.add_iterable_class(\"^MyContainer$\", \"float\")\n", "options.fn_exclude_by_name__regex = \"^begin$|^end$\"\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Numeric C array members\n", "\n", "If a struct/class stores a numeric C array member, it will be exposed as a *modifiable* numpy array." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
struct Foo {  int values[4]; };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Foo:\n",
       "    values: np.ndarray  # ndarray[type=int, size=4]\n",
       "    def __init__(self) -> None:\n",
       "        """Auto-generated default constructor"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassFoo =\n",
       "    py::class_<Foo>\n",
       "        (m, "Foo", "")\n",
       "    .def(py::init<>()) // implicit default constructor \n",
       "    .def_property("values",\n",
       "        [](Foo &self) -> pybind11::array\n",
       "        {\n",
       "            auto dtype = pybind11::dtype(pybind11::format_descriptor<int>::format());\n",
       "            auto base = pybind11::array(dtype, {4}, {sizeof(int)});\n",
       "            return pybind11::array(dtype, {4}, {sizeof(int)}, self.values, base);\n",
       "        }, [](Foo& self) {},\n",
       "        "")\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", "struct Foo { int values[4]; };\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "# options.member_numeric_c_array_replace__regex = r\".*\" # this is the default\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inner class or enum\n", "\n", "litgen handles inner classes or enum and correctly nest them in their host class:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    struct Foo\n",
       "    {\n",
       "        enum class Choice { A, B };\n",
       "        int HandleChoice(Choice value = Choice::A) { return 0; }\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Foo:\n",
       "    class Choice(enum.Enum):\n",
       "        a = enum.auto() # (= 0)\n",
       "        b = enum.auto() # (= 1)\n",
       "    def handle_choice(self, value: Foo.Choice = Foo.Choice.a) -> int:\n",
       "        pass\n",
       "    def __init__(self) -> None:\n",
       "        """Auto-generated default constructor"""\n",
       "        pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
auto pyClassFoo =\n",
       "    py::class_<Foo>\n",
       "        (m, "Foo", "");\n",
       "\n",
       "{ // inner classes & enums of Foo\n",
       "    py::enum_<Foo::Choice>(pyClassFoo, "Choice", py::arithmetic(), "")\n",
       "        .value("a", Foo::Choice::A, "")\n",
       "        .value("b", Foo::Choice::B, "");\n",
       "} // end of inner classes & enums of Foo\n",
       "\n",
       "pyClassFoo\n",
       "    .def(py::init<>()) // implicit default constructor \n",
       "    .def("handle_choice",\n",
       "        &Foo::HandleChoice, py::arg("value") = Foo::Choice::A)\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " struct Foo\n", " {\n", " enum class Choice { A, B };\n", " int HandleChoice(Choice value = Choice::A) { return 0; }\n", " };\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "litgen_demo.demo(options, cpp_code)" ] } ], "metadata": { "kernelspec": { "display_name": "venv311", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 2 }