{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Template classes and functions\n", "\n", "litgen provides advanced support for template classes and functions. Refer to the examples below.\n", "\n", "## Template Functions\n", "\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/advanced/functions.html#binding-functions-with-template-parameters) of the pybind11 manual.\n", "\n", "litgen can instantiate template functions for a customizable range of types.\n", "\n", "### Export template functions with an @overload decorator\n", "\n", "Consider the example below. If we try to generate code from it, litgen will complain that this template function is unhandled:\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Warning: (LitgenTemplateFunctionIgnore) Ignoring template function MaxValue. You might need to set LitgenOptions.fn_template_options\n", "While parsing a \"function_decl\", corresponding to this C++ code:\n", "Position:2:5\n", " template T MaxValue(const std::vector& values);\n", " ^\n", "Warning: (LitgenTemplateFunctionIgnore) Ignoring template function MaxValue. You might need to set LitgenOptions.fn_template_options\n", "While parsing a \"function_decl\", corresponding to this C++ code:\n", "Position:2:5\n", " template T MaxValue(const std::vector& values);\n", " ^\n" ] } ], "source": [ "cpp_code = \"\"\"\n", " template T MaxValue(const std::vector& values);\n", "\"\"\"\n", "\n", "import litgen\n", "from litgen.demo import litgen_demo\n", "\n", "options = litgen.LitgenOptions()\n", "generated_code = litgen.generate_code(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we add some information about how we want to specialize the function, then litgen will correctly output the bindings, and it will add an `@overload` decorator to the python functions." ] }, { "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", "
    template<typename T> T MaxValue(const std::vector<T>& values);\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
#  ------------------------------------------------------------------------\n",
       "#      <template specializations for function MaxValue>\n",
       "@overload\n",
       "def max_value(values: List[int]) -> int:\n",
       "    pass\n",
       "\n",
       "\n",
       "@overload\n",
       "def max_value(values: List[float]) -> float:\n",
       "    pass\n",
       "#      </template specializations for function MaxValue>\n",
       "#  ------------------------------------------------------------------------\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
m.def("max_value",\n",
       "    py::overload_cast<const std::vector<int> &>(MaxValue<int>), py::arg("values"));\n",
       "m.def("max_value",\n",
       "    py::overload_cast<const std::vector<float> &>(MaxValue<float>), py::arg("values"));\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "options.fn_template_options.add_specialization(\"^MaxValue$\", [\"int\", \"float\"], add_suffix_to_function_name=False)\n", "\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Export template functions with a suffix\n", "\n", "Instead of using `@overload`, we can give different names to the python functions:" ] }, { "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", "
    template<typename T> voi LogValue(const std::string& label, const T& value);\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
#  ------------------------------------------------------------------------\n",
       "#      <template specializations for function LogValue>\n",
       "def log_value_int(label: str, value: int) -> voi:\n",
       "    pass\n",
       "\n",
       "\n",
       "def log_value_float(label: str, value: float) -> voi:\n",
       "    pass\n",
       "#      </template specializations for function LogValue>\n",
       "#  ------------------------------------------------------------------------\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
m.def("log_value_int",\n",
       "    LogValue<int>, py::arg("label"), py::arg("value"));\n",
       "m.def("log_value_float",\n",
       "    LogValue<float>, py::arg("label"), py::arg("value"));\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", " template voi LogValue(const std::string& label, const T& value);\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "options.fn_template_options.add_specialization(\"^LogValue$\", [\"int\", \"float\"], add_suffix_to_function_name=True)\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Template classes\n", "\n", "### Introduction\n", "[Relevant portion](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#binding-classes-with-template-parameters) of the pybind11 manual.\n", "\n", "litgen handles template classes instantiation in a sophisticated way. \n", "\n", "In the example below, we set the following options:\n", "\n", "**type replacements**\n", "\n", "We set an option for type name replacements, so that `ImGuiConfig` will be exposed as `Config` in python:\n", "```python\n", " options.type_replacements.add_last_replacement(r\"ImGui([A-Z][a-zA-Z0-9]*)\", r\"\\1\")\n", "```\n", "\n", "\n", "**class specialization**\n", "\n", "* We tell litgen to instantiate `ImVector` for `ImGuiConfig`, `float *`, and `int32_t`.\n", "* We also tell it to emit a synonym (`ImVector_Int32 = ImVector_int`) in the python stub.\n", "```python\n", " options.class_template_options.add_specialization(\n", " \"ImVector\", # which class do we want to specialize\n", " [\"ImGuiConfig\", \"float *\", \"int32_t\"], # for which types\n", " [\"Int32=uint32_t\"] # With which synonyms\n", " )\n", "```\n", "\n", "**Notes**\n", "* The member `Foo::Configs` will be exposed with the correct python type (`ImVector_Config`)\n", "* The member `Foo::IntValues` _will not be published_, since `ImVector` is not published\n", "* _litgen will emit a warning about the missing specialization for `int`_\n", "\n", "\n", "### Example of template class instantiation" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Warning: (Undefined) Excluding template type ImVector because its specialization for `int` is not handled\n", "While parsing a \"type\", corresponding to this C++ code:\n", "Position:16:9\n", "Warning: (Undefined) Excluding template type ImVector because its specialization for `int` is not handled\n", "While parsing a \"type\", corresponding to this C++ code:\n", "Position:16:9\n", "Warning: (Undefined) Excluding template type ImVector because its specialization for `int` is not handled\n", "While parsing a \"type\", corresponding to this C++ code:\n", "Position:16:9\n" ] }, { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
    struct ImGuiConfig { /* implementation not shown here */ };\n",
       "\n",
       "    template<typename T>\n",
       "    struct ImVector\n",
       "    {\n",
       "        // Implementation not shown here\n",
       "    private:\n",
       "        T* data;\n",
       "    };\n",
       "\n",
       "    struct Foo\n",
       "    {\n",
       "        ImVector<ImGuiConfig> Configs; // This member will be added to the bindings\n",
       "        ImVector<int> IntValues;       // This member will be excluded from the bindings, since ImVector<int> is not published!\n",
       "    };\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
class Config:\n",
       "    # implementation not shown here\n",
       "    def __init__(self) -> None:\n",
       "        """Auto-generated default constructor"""\n",
       "        pass\n",
       "\n",
       "#  ------------------------------------------------------------------------\n",
       "#      <template specializations for class ImVector>\n",
       "class ImVector_Config:  # Python specialization for ImVector<ImGuiConfig>\n",
       "    # Implementation not shown here\n",
       "    def __init__(self) -> None:\n",
       "        """Auto-generated default constructor"""\n",
       "        pass\n",
       "\n",
       "\n",
       "class ImVector_float_ptr:  # Python specialization for ImVector<float *>\n",
       "    # Implementation not shown here\n",
       "    def __init__(self) -> None:\n",
       "        """Auto-generated default constructor"""\n",
       "        pass\n",
       "\n",
       "\n",
       "class ImVector_int32_t:  # Python specialization for ImVector<int32_t>\n",
       "    # Implementation not shown here\n",
       "    def __init__(self) -> None:\n",
       "        """Auto-generated default constructor"""\n",
       "        pass\n",
       "\n",
       "ImVector_Int32 = ImVector_int\n",
       "\n",
       "#      </template specializations for class ImVector>\n",
       "#  ------------------------------------------------------------------------\n",
       "\n",
       "class Foo:\n",
       "    configs: ImVector_Config  # This member will be added to the bindings\n",
       "    def __init__(self, configs: ImVector_Config = ImVector_Config()) -> 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 pyClassImGuiConfig =\n",
       "    py::class_<ImGuiConfig>\n",
       "        (m, "Config", "")\n",
       "    .def(py::init<>()) // implicit default constructor \n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassImVector_ImGuiConfig =\n",
       "    py::class_<ImVector<ImGuiConfig>>\n",
       "        (m, "ImVector_Config", "")\n",
       "    .def(py::init<>()) // implicit default constructor \n",
       "    ;\n",
       "auto pyClassImVector_float_ptr =\n",
       "    py::class_<ImVector<float *>>\n",
       "        (m, "ImVector_float_ptr", "")\n",
       "    .def(py::init<>()) // implicit default constructor \n",
       "    ;\n",
       "auto pyClassImVector_int32_t =\n",
       "    py::class_<ImVector<int32_t>>\n",
       "        (m, "ImVector_int32_t", "")\n",
       "    .def(py::init<>()) // implicit default constructor \n",
       "    ;\n",
       "\n",
       "\n",
       "auto pyClassFoo =\n",
       "    py::class_<Foo>\n",
       "        (m, "Foo", "")\n",
       "    .def(py::init<>([](\n",
       "    ImVector<ImGuiConfig> Configs = ImVector<ImGuiConfig>())\n",
       "    {\n",
       "        auto r = std::make_unique<Foo>();\n",
       "        r->Configs = Configs;\n",
       "        return r;\n",
       "    })\n",
       "    , py::arg("configs") = ImVector<ImGuiConfig>()\n",
       "    )\n",
       "    .def_readwrite("configs", &Foo::Configs, "This member will be added to the bindings")\n",
       "    ;\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cpp_code = \"\"\"\n", "\n", " struct ImGuiConfig { /* implementation not shown here */ };\n", "\n", " template\n", " struct ImVector\n", " {\n", " // Implementation not shown here\n", " private:\n", " T* data;\n", " };\n", "\n", " struct Foo\n", " {\n", " ImVector Configs; // This member will be added to the bindings\n", " ImVector IntValues; // This member will be excluded from the bindings, since ImVector is not published!\n", " };\n", "\"\"\"\n", "\n", "options = litgen.LitgenOptions()\n", "options.type_replacements.add_last_replacement(r\"ImGui([A-Z][a-zA-Z0-9]*)\", r\"\\1\") # Remove prefix ImGui from exposed type\n", "options.class_template_options.add_specialization(\n", " \"ImVector\", # which class do we want to specialize\n", " [\"ImGuiConfig\", \"float *\", \"int32_t\"], # for which types\n", " [\"Int32=uint32_t\"], # With which synonyms\n", ")\n", "litgen_demo.demo(options, cpp_code)\n", "\n", "# Note: the warnings below are normal, since we did not specialize ImVector (they can be filtered out, see below)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Suppress template class warnings\n", "You can ask litgen to ignore the warnings concerning the missing specialization:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# tell litgen to ignore warnings that contain \"Excluding template type ImVector\"\n", "options.srcmlcpp_options.ignored_warning_parts.append(\"Excluding template type ImVector\")\n", "# the following line emits a warning that is ignored\n", "generated_code = litgen.generate_code(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 }