{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Headers processing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Filtering header content\n", "\n", "A C/C++ header can contains different zone, some of which are parts of the public API, and some of which may correspond to specific low-level options.\n", "\n", "litgen (and srcmlcpp) can filter a header based on preprocessor `#ifdef / #ifndef` occurrences.\n", "\n", "Let's look at an example header: its code is defined in the `cpp_code` variable below." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "cpp_code = \"\"\"\n", "#ifndef MY_HEADER_H // This is an inclusion guard, it should not be filtered out\n", "\n", "void Foo() {}\n", "\n", "#ifdef ARCANE_OPTION\n", " // We are entering a zone that handle arcane options that should not be included in the bindings\n", " void Foo2() {}\n", "#else\n", " // this should also not be included in the bindings\n", " void Foo3() {}\n", "#endif\n", "\n", "#ifdef COMMON_OPTION\n", " // We are entering a zone for which we would like to publish bindings\n", " void Foo4();\n", "#endif\n", "\n", "#endif // #ifndef MY_HEADER_H\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's try to generate bindings for it:" ] }, { "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", "
#ifndef MY_HEADER_H   // This is an inclusion guard, it should not be filtered out\n",
       "\n",
       "void Foo() {}\n",
       "\n",
       "#ifdef ARCANE_OPTION\n",
       "    // We are entering a zone that handle arcane options that should not be included in the bindings\n",
       "    void Foo2() {}\n",
       "#else\n",
       "    // this should also not be included in the bindings\n",
       "    void Foo3() {}\n",
       "#endif\n",
       "\n",
       "#ifdef COMMON_OPTION\n",
       "    // We are entering a zone for which we would like to publish bindings\n",
       "    void Foo4();\n",
       "#endif\n",
       "\n",
       "#endif // #ifndef MY_HEADER_H\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
# #ifndef MY_HEADER_H   \n",
       "\n",
       "def foo() -> None:\n",
       "    pass\n",
       "\n",
       "\n",
       "\n",
       "# #endif \n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
// #ifndef MY_HEADER_H   \n",
       "\n",
       "m.def("foo",\n",
       "    Foo);\n",
       "// #endif \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", "options = litgen.LitgenOptions()\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that elements inside `#ifdef ARCANE_OPTION` were not processed. However, elements inside `#ifdef COMMON_OPTION` were not processed. Let's correct this by adjusting the options:" ] }, { "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", "
#ifndef MY_HEADER_H   // This is an inclusion guard, it should not be filtered out\n",
       "\n",
       "void Foo() {}\n",
       "\n",
       "#ifdef ARCANE_OPTION\n",
       "    // We are entering a zone that handle arcane options that should not be included in the bindings\n",
       "    void Foo2() {}\n",
       "#else\n",
       "    // this should also not be included in the bindings\n",
       "    void Foo3() {}\n",
       "#endif\n",
       "\n",
       "#ifdef COMMON_OPTION\n",
       "    // We are entering a zone for which we would like to publish bindings\n",
       "    void Foo4();\n",
       "#endif\n",
       "\n",
       "#endif // #ifndef MY_HEADER_H\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
# #ifndef MY_HEADER_H   \n",
       "\n",
       "def foo() -> None:\n",
       "    pass\n",
       "\n",
       "\n",
       "# #ifdef COMMON_OPTION\n",
       "#     \n",
       "def foo4() -> None:\n",
       "    """ We are entering a zone for which we would like to publish bindings"""\n",
       "    pass\n",
       "# #endif\n",
       "# \n",
       "\n",
       "# #endif \n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
// #ifndef MY_HEADER_H   \n",
       "\n",
       "m.def("foo",\n",
       "    Foo);\n",
       "// #ifdef COMMON_OPTION\n",
       "//     \n",
       "\n",
       "m.def("foo4",\n",
       "    Foo4, "We are entering a zone for which we would like to publish bindings");\n",
       "// #endif\n",
       "// \n",
       "// #endif \n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "options = litgen.LitgenOptions()\n", "\n", "# the default value for header_filter_acceptable__regex was\n", "# \"__cplusplus|_h_$|_h$|_H$|_H_$|hpp$|HPP$|hxx$|HXX$\"\n", "# (which includes support for common header guards)\n", "# Let's add support for COMMON_OPTION\n", "options.srcmlcpp_options.header_filter_acceptable__regex = \"_H$|^COMMON_OPTION$\"\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## API Prefixes\n", "\n", "In a given header files, function can have an \"API Prefix\", that denotes whether they should be published or not in a shared library.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "cpp_code = \"\"\"\n", "// This function has an API marker and is exported in a shared library\n", "MY_API int add(int, int b);\n", "\n", "// This function does not have an API marker, and is probably private\n", "int mul(int a, int b);\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can set the API marker in the options:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
// This function has an API marker and is exported in a shared library\n",
       "MY_API int add(int, int b);\n",
       "\n",
       "// This function does not have an API marker, and is probably private\n",
       "int mul(int a, int b);\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
def add(param_0: int, b: int) -> int:\n",
       "    """ This function has an API marker and is exported in a shared library"""\n",
       "    pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
m.def("add",\n",
       "    add, \n",
       "    py::arg("param_0"), py::arg("b"), \n",
       "    "This function has an API marker and is exported in a shared library");\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "options = litgen.LitgenOptions()\n", "options.srcmlcpp_options.functions_api_prefixes = \"MY_API\"\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also decide to export non API function, with an optional comment." ] }, { "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", "
// This function has an API marker and is exported in a shared library\n",
       "MY_API int add(int, int b);\n",
       "\n",
       "// This function does not have an API marker, and is probably private\n",
       "int mul(int a, int b);\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", " \n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
def add(param_0: int, b: int) -> int:\n",
       "    """ This function has an API marker and is exported in a shared library"""\n",
       "    pass\n",
       "\n",
       "def mul(a: int, b: int) -> int:\n",
       "    """ This function does not have an API marker, and is probably private\n",
       "    Private API!\n",
       "    """\n",
       "    pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
m.def("add",\n",
       "    add, \n",
       "    py::arg("param_0"), py::arg("b"), \n",
       "    "This function has an API marker and is exported in a shared library");\n",
       "\n",
       "m.def("mul",\n",
       "    mul, \n",
       "    py::arg("a"), py::arg("b"), \n",
       "    " This function does not have an API marker, and is probably private\\nPrivate API!");\n",
       "
\n", "\n", "
\n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "options = litgen.LitgenOptions()\n", "options.srcmlcpp_options.functions_api_prefixes = \"MY_API\"\n", "options.fn_exclude_non_api = False\n", "options.fn_non_api_comment = \"Private API!\"\n", "litgen_demo.demo(options, cpp_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Header preprocessing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you need to preprocess header code before the generation, you can create a function that transforms the source code, and store it inside `options.srcmlcpp_options.code_preprocess_function`.\n", "\n", "For example:\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", " \n", " \n", "
\n", "
\n", " \n", "
\n", "
def add(param_0: Int32, b: Int32) -> Int32:\n",
       "    pass\n",
       "
\n", "\n", "
\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def preprocess_change_int(code: str) -> str:\n", " return code.replace(\"int\", \"Int32\") # This is a *very* dumb preprocessor\n", "\n", "\n", "cpp_code = \"\"\"\n", "int add(int, int b);\n", "\"\"\"\n", "options = litgen.LitgenOptions()\n", "options.srcmlcpp_options.code_preprocess_function = preprocess_change_int\n", "generated_code = litgen.generate_code(options, cpp_code)\n", "litgen_demo.show_cpp_code(generated_code.stub_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 }