{ "cells": [ { "cell_type": "markdown", "id": "7f9dfa0e", "metadata": { "code_folding": [] }, "source": [ "# First steps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generate bindings for functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using litgen is straightforward. The simplest use case is:\n", "\n", "1. import litgen\n", "2. instantiate some options\n", "3. convert some code into C++ binding code and python declarations (stubs)\n", "4. use the generated code\n", "\n", "Below we import litgen, define the C++ code we want to bind, and run `litgen.generate_code`\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "60270e1e", "metadata": {}, "outputs": [], "source": [ "# Import litgen\n", "import litgen\n", "\n", "\n", "# Instantiate some options\n", "options = litgen.LitgenOptions()\n", "# Code for which we will emit bindings\n", "cpp_code = \"\"\"\n", " // Mathematic operations\n", "\n", " // Adds two integers\n", " int Add(int x, int y = 2);\n", "\n", " int Sub(int x, int y = 2); // Substract two integers\n", " \"\"\"\n", "# Run the generator\n", "generated_code = litgen.generate_code(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The generated code contains several elements:\n", "- `stub_code` contains the python declarations, including all comments. They enable to have a flawless code navigation and completion inside IDEs.\n", "- `pydef_code` contains the C++ binding code. This code is specific to pybind11\n", "- `glue_code`: optional additional C++ code that is emitted in speficic advanced cases (more details later in this manual). Most of the time, it will be empty\n", "\n", "Let's see the python declarations corresponding to the C++ code: they should be copied into a python stub file (*.pyi) that describes the bindings offered by your library.\n", "\n", ">_Note: below, we import `litgen_demo` to be able to display code in this page. You will not need it: you can simply call `print(generated_code.stub_code)`_\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "f02211e0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class FooClass {\n",
       "    private:\n",
       "        int mPriv = 0;\n",
       "    public:\n",
       "        FooClass(int v);\n",
       "        int mPublic = 1;\n",
       "        void ShowInfo();\n",
       "};\n",
       "\n",
       "struct FooStruc {\n",
       "    int a = 1;\n",
       "    void ShowInfo();\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class FooClass:\n",
       "    def __init__(self, v: int) -> None:\n",
       "        pass\n",
       "    m_public: int = 1\n",
       "    def show_info(self) -> None:\n",
       "        pass\n",
       "\n",
       "class FooStruc:\n",
       "    a: int = 1\n",
       "    def show_info(self) -> None:\n",
       "        pass\n",
       "    def __init__(self, a: 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 pyClassFooClass =\n",
       "    py::class_<FooClass>\n",
       "        (m, "FooClass", "")\n",
       "    .def(py::init<int>(),\n",
       "        py::arg("v"))\n",
       "    .def_readwrite("m_public", &FooClass::mPublic, "")\n",
       "    .def("show_info",\n",
       "        &FooClass::ShowInfo)\n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassFooStruc =\n",
       "    py::class_<FooStruc>\n",
       "        (m, "FooStruc", "")\n",
       "    .def(py::init<>([](\n",
       "    int a = 1)\n",
       "    {\n",
       "        auto r = std::make_unique<FooStruc>();\n",
       "        r->a = a;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("a") = 1\n",
       "    )\n",
       "    .def_readwrite("a", &FooStruc::a, "")\n",
       "    .def("show_info",\n",
       "        &FooStruc::ShowInfo)\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from litgen.demo import litgen_demo\n", "\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)\n", "\n", "# Below, you will see:\n", "# - the original C++ code to the left\n", "# - stub_code (i.e. the python stubs) to the right\n", "# - pydef_code (i.e. the C++ binding code) at the bottom\n", "# In this case, the glue code is empty " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Binding a simple class or struct\n", "\n", "For a simple class, public members and methods will be exposed. For a simple struct, a constructor with named parameters will be exposed in Python." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class FooClass {\n",
       "    private:\n",
       "        int mPriv = 0;\n",
       "    public:\n",
       "        FooClass(int v);\n",
       "        int mPublic = 1;\n",
       "        void ShowInfo();\n",
       "};\n",
       "\n",
       "struct FooStruc {\n",
       "    int a = 1;\n",
       "    void ShowInfo();\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class FooClass:\n",
       "    def __init__(self, v: int) -> None:\n",
       "        pass\n",
       "    m_public: int = 1\n",
       "    def show_info(self) -> None:\n",
       "        pass\n",
       "\n",
       "class FooStruc:\n",
       "    a: int = 1\n",
       "    def show_info(self) -> None:\n",
       "        pass\n",
       "    def __init__(self, a: 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 pyClassFooClass =\n",
       "    py::class_<FooClass>\n",
       "        (m, "FooClass", "")\n",
       "    .def(py::init<int>(),\n",
       "        py::arg("v"))\n",
       "    .def_readwrite("m_public", &FooClass::mPublic, "")\n",
       "    .def("show_info",\n",
       "        &FooClass::ShowInfo)\n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassFooStruc =\n",
       "    py::class_<FooStruc>\n",
       "        (m, "FooStruc", "")\n",
       "    .def(py::init<>([](\n",
       "    int a = 1)\n",
       "    {\n",
       "        auto r = std::make_unique<FooStruc>();\n",
       "        r->a = a;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("a") = 1\n",
       "    )\n",
       "    .def_readwrite("a", &FooStruc::a, "")\n",
       "    .def("show_info",\n",
       "        &FooStruc::ShowInfo)\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "options = litgen.LitgenOptions()\n", "cpp_code = \"\"\"\n", "class FooClass {\n", " private:\n", " int mPriv = 0;\n", " public:\n", " FooClass(int v);\n", " int mPublic = 1;\n", " void ShowInfo();\n", "};\n", "\n", "struct FooStruc {\n", " int a = 1;\n", " void ShowInfo();\n", "};\n", "\"\"\"\n", "\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generator options\n", "\n", "There are numerous generations options that can be set, and they are explained in details later in this manual: See [full list](https://github.com/pthom/litgen/blob/main/src/litgen/options.py)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## About glue code\n", "\n", "In some cases, some additional \"glue code\" will be emitted. This is the case for example when we want to bind a function that wants to modify a parameter whose type is immutable in python.\n", "\n", "In this case, we will need to set one option (`options.fn_params_replace_modifiable_immutable_by_boxed__regex `)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    // changes the value of the bool parameter (passed by modifiable reference)\n",
       "    // (This function will use a BoxedBool in the python code, so that its value can be modified)\n",
       "    void SwitchBoolValue(bool &v);\n",
       "    \n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
####################    <generated_from:BoxedTypes>    ####################\n",
       "class BoxedBool:\n",
       "    value: bool\n",
       "    def __init__(self, v: bool = False) -> None:\n",
       "        pass\n",
       "    def __repr__(self) -> str:\n",
       "        pass\n",
       "####################    </generated_from:BoxedTypes>    ####################\n",
       "\n",
       "\n",
       "\n",
       "def switch_bool_value(v: BoxedBool) -> None:\n",
       "    """ changes the value of the bool parameter (passed by modifiable reference)\n",
       "     (This function will use a BoxedBool in the python code, so that its value can be modified)\n",
       "    """\n",
       "    pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
////////////////////    <generated_from:BoxedTypes>    ////////////////////\n",
       "auto pyClassBoxedBool =\n",
       "    py::class_<BoxedBool>\n",
       "        (m, "BoxedBool", "")\n",
       "    .def_readwrite("value", &BoxedBool::value, "")\n",
       "    .def(py::init<bool>(),\n",
       "        py::arg("v") = false)\n",
       "    .def("__repr__",\n",
       "        &BoxedBool::__repr__)\n",
       "    ;\n",
       "////////////////////    </generated_from:BoxedTypes>    ////////////////////\n",
       "\n",
       "\n",
       "m.def("switch_bool_value",\n",
       "    [](BoxedBool & v)\n",
       "    {\n",
       "        auto SwitchBoolValue_adapt_modifiable_immutable = [](BoxedBool & v)\n",
       "        {\n",
       "            bool & v_boxed_value = v.value;\n",
       "\n",
       "            SwitchBoolValue(v_boxed_value);\n",
       "        };\n",
       "\n",
       "        SwitchBoolValue_adapt_modifiable_immutable(v);\n",
       "    }, \n",
       "    py::arg("v"), \n",
       "    " changes the value of the bool parameter (passed by modifiable reference)\\n (This function will use a BoxedBool in the python code, so that its value can be modified)");\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
struct BoxedBool\n",
       "{\n",
       "    bool value;\n",
       "    BoxedBool(bool v = false) : value(v) {}\n",
       "    std::string __repr__() const { return std::string("BoxedBool(") + std::to_string(value) + ")"; }\n",
       "};\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "options = litgen.LitgenOptions()\n", "# This a regex which specifies that we want to adapt all functions with boxed types when needed\n", "options.fn_params_replace_modifiable_immutable_by_boxed__regex = r\".*\"\n", "cpp_code = \"\"\"\n", " // changes the value of the bool parameter (passed by modifiable reference)\n", " // (This function will use a BoxedBool in the python code, so that its value can be modified)\n", " void SwitchBoolValue(bool &v);\n", " \"\"\"\n", "# Run the generator\n", "litgen_demo.demo(options, cpp_code, show_pydef=True)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 5 }