{"id":22535881,"url":"https://github.com/kljohann/genpybind","last_synced_at":"2025-04-09T19:08:22.075Z","repository":{"id":259221538,"uuid":"362592905","full_name":"kljohann/genpybind","owner":"kljohann","description":"Autogeneration of Python bindings from manually annotated C++ headers","archived":false,"fork":false,"pushed_at":"2024-11-14T21:28:24.000Z","size":417,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-28T18:49:46.806Z","etag":null,"topics":["clang","cpp","pybind11","python","python-bindings"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kljohann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSES/LicenseRef-LLVM.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-28T20:05:11.000Z","updated_at":"2024-11-14T21:28:28.000Z","dependencies_parsed_at":"2024-10-28T00:19:12.341Z","dependency_job_id":null,"html_url":"https://github.com/kljohann/genpybind","commit_stats":null,"previous_names":["kljohann/genpybind"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kljohann%2Fgenpybind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kljohann%2Fgenpybind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kljohann%2Fgenpybind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kljohann%2Fgenpybind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kljohann","download_url":"https://codeload.github.com/kljohann/genpybind/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228587311,"owners_count":17941443,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clang","cpp","pybind11","python","python-bindings"],"created_at":"2024-12-07T10:08:53.649Z","updated_at":"2024-12-07T10:08:54.440Z","avatar_url":"https://github.com/kljohann.png","language":"C++","readme":"\u003c!--\nSPDX-FileCopyrightText: 2024 Johann Klähn \u003cjohann@jklaehn.de\u003e\n\nSPDX-License-Identifier: MIT\n--\u003e\n\n# genpybind\n\n[![PyPI](https://img.shields.io/pypi/v/genpybind.svg)](https://pypi.org/project/genpybind/)\n[![Releases](https://img.shields.io/github/v/release/kljohann/genpybind?include_prereleases\u0026label=latest%20release)](https://github.com/kljohann/genpybind/releases)\n[![Build + Tests](https://github.com/kljohann/genpybind/actions/workflows/build.yml/badge.svg)](https://github.com/kljohann/genpybind/actions/workflows/build.yml)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kljohann/genpybind/blob/main/LICENSES/MIT.txt)\n\n*Autogeneration of Python bindings from manually annotated C++ headers*\n\nGenpybind is a tool based on [clang][] that automatically generates code to\nexpose a C++ API as a Python extension module via [pybind11][].  Say goodbye to the\ntedious task of writing and updating binding code by hand!  Genpybind ensures\nthat your Python bindings always stay in sync with your C++ API, complete with\ndocstrings, parameter names, and default arguments.  This is especially valuable\nfor still-evolving APIs where manual bindings can quickly become outdated.\n\nWhile genpybind does require some manual hints in the form of unobtrusive\nannotation macros[^1], it results in a self-contained header file that concisely\ndescribes both the C++ and Python interfaces of your library.  This approach\nkeeps you in control and requires less heuristics in genpybind's implementation,\nthereby reducing complexity.  Though it does require the ability to modify the\noriginal interface declarations, so code which is not under your control needs\nto fall back on manually written bindings.\n\nBesides the main use case of exposing a C++ API to Python, genpybind has proven useful\n*during* C++ library development:\n- It enables interactive exploration of a library's API via the Python REPL.\n- This exploration can form the basis for unit tests using Python's\n  low-boilerplate testing frameworks like [pytest][].\n- And maybe most importantly, it enables hassle-free *property-based testing*\n  via [hypothesis][], which still has no C++-native equivalent.\n\n[clang]: https://clang.llvm.org/\n[hypothesis]: https://hypothesis.readthedocs.io\n[pybind11]: https://github.com/pybind/pybind11\n[pytest]: https://doc.pytest.org/\n\n## Example\n\nTo expose a C++ interface via a Python module, `GENPYBIND` annotations are added\nto the C++ declarations:\n\n```cpp\n#pragma once\n\n#include \u003cgenpybind/genpybind.h\u003e\n\nnamespace readme GENPYBIND(visible) {\n\n/// Describes how the output will taste.\nenum class Flavor {\n  /// Like you would expect.\n  bland,\n  /// It tastes different.\n  fruity,\n};\n\n/// A contrived example.\nclass Example {\npublic:\n  static constexpr int GENPYBIND(hidden) not_exposed = 10;\n\n  /// Do a complicated calculation.\n  int calculate(Flavor flavor = Flavor::fruity) const;\n\n  GENPYBIND(getter_for(something))\n  int getSomething() const;\n\n  GENPYBIND(setter_for(something))\n  void setSomething(int value);\n\nprivate:\n  int m_value = 0;\n};\n\n} // namespace readme\n```\n\nThe resulting module can then be used like this:\n\n```python\n\u003e\u003e\u003e import readme as m\n\u003e\u003e\u003e obj = m.Example()\n\u003e\u003e\u003e obj.something\n0\n\u003e\u003e\u003e obj.something = 42\n\u003e\u003e\u003e obj.something\n42\n\u003e\u003e\u003e obj.calculate()  # default argument\n-42\n\u003e\u003e\u003e obj.calculate(m.Flavor.bland)\n42\n\u003e\u003e\u003e print(m.Example.__doc__)\nA contrived example.\n\u003e\u003e\u003e print(m.Flavor.__doc__)\nDescribes how the output will taste.\n\nMembers:\n\n  bland : Like you would expect.\n\n  fruity : It tastes different.\n\u003e\u003e\u003e help(obj.calculate)\nHelp on method calculate in module readme:\n\ncalculate(...) method of readme.Example instance\n    calculate(self: readme.Example, flavor: readme.Flavor = \u003cFlavor.fruity: 1\u003e) -\u003e int\n\n    Do a complicated calculation.\n```\n\nFor the example presented above, `genpybind` will generate code equivalent to\nthe following: (Note that docstrings, argument names and default arguments work\nout of the box, without extra annotations.)\n\n```cpp\nvoid expose_context_readme_Flavor(py::enum_\u003creadme::Flavor\u003e\u0026 context);\nvoid expose_context_readme_Example(py::class_\u003creadme::Example\u003e\u0026 context);\n\nPYBIND11_MODULE(readme, root) {\n  auto context_readme_Flavor = py::enum_\u003creadme::Flavor\u003e(\n      root, \"Flavor\", \"Describes how the output will taste.\");\n  auto context_readme_Example = py::class_\u003creadme::Example\u003e(\n      root, \"Example\", \"A contrived example.\");\n\n  expose_context_readme_Flavor(context_readme_Flavor);\n  expose_context_readme_Example(context_readme_Example);\n}\n\nvoid expose_context_readme_Flavor(py::enum_\u003creadme::Flavor\u003e\u0026 context) {\n  context.value(\"bland\", readme::Flavor::bland, \"Like you would expect.\");\n  context.value(\"fruity\", readme::Flavor::fruity, \"It tastes different.\");\n}\n\nvoid expose_context_readme_Example(py::class_\u003creadme::Example\u003e\u0026 context) {\n  context.def(py::init\u003c\u003e(), \"\");\n  context.def(py::init\u003cconst readme::Example\u0026\u003e(), \"\", py::arg(\"\"));\n  context.def(\"calculate\",\n              py::overload_cast\u003creadme::Flavor\u003e(\u0026readme::Example::calculate, py::const_),\n              \"Do a complicated calculation.\",\n              py::arg(\"flavor\") = readme::Flavor::fruity);\n  context.def_property(\n      \"something\",\n      py::overload_cast\u003c\u003e(\u0026readme::Example::getSomething, py::const_),\n      py::overload_cast\u003cint\u003e(\u0026readme::Example::setSomething));\n}\n```\n\n# Getting started\n\nTo use genpybind in your project, the simplest approach is through\n[scikit-build-core][].  Since genpybind is [available on PyPI][pypi], setup is\nsimilar to a standard pybind11 extension module, except that you need to:\n\n1. add genpybind as an additional build dependency, and\n2. use `genpybind_add_module` instead of `pybind11_add_module`\n   (see [`tools/genpybind.cmake`](./tools/genpybind.cmake)).\n\n```toml\n# In pyproject.toml:\n\n[build-system]\nrequires = [\"scikit-build-core\", \"pybind11\", \"genpybind\"]\n```\n\n```cmake\n# In CMakeLists.txt:\n\nset(PYBIND11_NEWPYTHON ON)\nfind_package(pybind11 CONFIG REQUIRED)\nfind_package(genpybind CONFIG REQUIRED)\n\ngenpybind_add_module(\n  your_module MODULE\n  HEADER include/your_module.h\n  src/a.cpp src/b.cpp src/c.cpp\n)\n```\n\nSee the [example project](./example-project) for a complete implementation.\n\n\u003cdetails\u003e\n\u003csummary\u003eUsing genpybind without a separate header file\u003c/summary\u003e\n\n```cpp\n// In your_module.cpp:\n\n#include \u003cgenpybind/genpybind.h\u003e\n\ndouble square(double x) GENPYBIND(visible) { return x * x; }\n```\n\n```cmake\n# In CMakeLists.txt:\n\ngenpybind_add_module(\n  your_module MODULE\n  HEADER your_module.cpp\n  your_module.cpp\n)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eLink against existing library (which might also be consumed by other C++ targets)\u003c/summary\u003e\n\n```cmake\n# In CMakeLists.txt:\n\nadd_library(some_library SHARED src/a.cpp src/b.cpp src/c.cpp)\n# Add dependency to allow `#include \u003cgenpybind/genpybind.h\u003e`\ntarget_link_libraries(some_library PUBLIC genpybind::genpybind)\ngenpybind_add_module(\n  py_some_library MODULE\n  LINK_LIBRARIES some_library\n  NUM_BINDING_FILES 1\n  HEADER include/some_library.h\n)\n```\n\n\u003c/details\u003e\n\n[scikit-build-core]: https://scikit-build-core.readthedocs.io/\n[pypi]: https://pypi.org/project/genpybind/\n\n# Implementation\n\nThe current implementation is a prototype based on clang's `libtooling` API.\nA [previous proof-of-concept Python implementation][legacy] I developed at the\n[Electronic Vision(s) Group][visions] ran into limits of the `libclang` bindings\nand required a patched LLVM/clang build.  Still, it's used successfully in the\nexperiment software stack of their neuromorphic computing platform, i.e., the\ndescribed approach is viable for an existing code base.\n\n[legacy]: https://github.com/kljohann/genpybind-legacy\n[visions]: http://www.kip.uni-heidelberg.de/vision/\n\nThe current iteration still lacks some polishing.  Some known shortcomings\nremain, the documentation is still lacking, and build support (and automated\ntesting) on different platforms is pending.\n\n## Known defects and shortcomings\n\n- Documentation is minimal at the moment.  If you want to look at example use-cases the\n  [integration tests](./tests/integration) might provide a starting point.\n- Expressions and types in default arguments, return values, or\n  `GENPYBIND_MANUAL` instructions are not consistently expanded to their fully\n  qualified form.  As a workaround it is suggested to use the fully-qualified\n  name where necessary.\n\n## Changes compared to the Python prototype\n\nApart from the less involved build process, the current implementation comes\nwith many new features and improvements.  For example, considerably better\nerror messages.  As a small price to pay there are several breaking changes:\n\n- `opaque` is now known as `expose_here`.\n- `expose_as(__repr__)` (or `__str__`) should be used in place of `stringstream`.\n- `tag` should be replaced by `only_expose_in`.\n- `inline_base` no longer supports globs/wildcards.\n- `accessor_for` is no longer supported, use `getter_for`/`setter_for` instead.\n- `writeable` is no longer supported, use `readonly` instead.\n\n# Building from source\n\nSo far, the only tested platform is [Fedora Workstation][fedora] 40, though\nat least Debian has been tested in the past.  You should be able to adapt the\ninstructions to other distributions.\n\n[fedora]: https://fedoraproject.org/workstation/\n[direnv]: https://github.com/direnv/direnv/\n\n1. Check out the repo, the following commands should be run from the repo root.\n2. Install dependencies:\n   ```\n   dnf install \\\n     llvm-devel clang-devel gtest-devel gmock-devel cmake ninja-build \\\n     python3-devel python3-pip pybind11-devel\n   ```\n3. Set up the build:\n   ```\n   cmake -B ./build -G Ninja .\n   ```\n4. Build and install (adapt the prefix accordingly):\n   ```\n   cmake --build ./build\n   cmake --install ./build --prefix ~/.local\n   ```\n\nSee `genpybind_add_module` in [`tools/genpybind.cmake`](./tools/genpybind.cmake)\nand how it's [used in an example project](./example-project/CMakeLists.txt) for\nhow to integrate genpybind into your build.  Depending on the prefix you used\nduring installation, you might need to specify the location of\n`genpybindConfig.cmake` explicitly in downstream builds, e.g.:\n`cmake … -Dgenpybind_DIR=\"$HOME/.local/share/cmake/genpybind\" …`.\n\n## Extra steps (for development)\n1. Inside a virtual environment (e.g., via [direnv][] with `layout python`),\n   install the Python dependencies (used in tests):\n   ```\n   pip install -r requirements.txt\n   ```\n2. Set up [pre-commit][]:\n   ```\n   pip install pre-commit\n   pre-commit install\n   ```\n3. Build and run the tests:\n   ```\n   PYTHONPATH=$PWD/build/tests ninja -C build test\n   ```\n   If you use direnv, it's convenient to add `path_add PYTHONPATH build/tests`\n   to your `.envrc`.\n\n[pre-commit]: https://pre-commit.com/\n\n# Annotations\n\nTop-level declarations are only exposed via the Python bindings (“visible”) if\nthey have a `GENPYBIND(…)` annotation.  Nested declarations, such as member\nvariables and member functions inherit the visibility of their parent\nby default.\n\nThere are several possible modifiers that can be passed as arguments to\n`GENPYBIND(…)` to affect how and where a declaration is exposed or to make use\nof advanced `pybind11` features.\n\n## Where to place the `GENPYBIND(…)` annotation\n\nBehind the scenes, the `GENPYBIND` macro expands to an [attribute][attributes],\nin particular the older GNU extension syntax `__attribute__` at this time.\nConsequently, you can consult the [GCC documentation][gnu-attributes] on details\nw.r.t. attribute placement.  Here are some common examples for your convenience:\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  void hidden_method() GENPYBIND(hidden);\n\n  GENPYBIND(readonly)\n  int readonly_field = 3;\n};\nenum class GENPYBIND(visible) Enum {};\nvoid example() GENPYBIND(visible);\n\nnamespace readme GENPYBIND(visible) {}\n```\n\n[attributes]: https://en.cppreference.com/w/cpp/language/attributes\n[gnu-attributes]: https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html\n\nTODO: Describe annotation argument types and when quotes can be omitted for\nstring arguments.\n\n## General modifiers\n\n### `visible` and `hidden`\n\n`visible` and `hidden` can be used to override the default visibility of\na declaration.  By default, top-level declarations are hidden, and nested\ndeclarations inherit the visibility of their parent.  So one has to explicitly\n“opt-in” to exposing a declaration.  Any use of `GENPYBIND(…)` annotations (even\nwithout arguments) implies `visible`, unless `hidden` is used explicitly.\n\nNamespaces are a special case: By default, they have no effect on the visibility\nof contained declarations and other attributes on namespaces do not imply `visible`.\nHowever, an explicit `visible` annotation on a namespace can be used to make all\nnested declarations visible by default.  The `hidden` keyword can then be used to\nexclude individual declarations again.\n\n```cpp\nstruct GENPYBIND() A {\n  GENPYBIND(hidden)\n  int some_field;\n};\n\nstruct GENPYBIND(visible) B {};\n\n// This would not have been exposed anyways, but we can\n// include `hidden` to document our intent explicitly.\nstruct GENPYBIND(hidden) C {};\n\nnamespace example GENPYBIND(visible) {\nstruct Example {}; // Visible, even though there is no annotation.\n}\n```\n\n### `expose_as`\n\nBy default a declaration will be exposed using the name of its C++ identifier.\n`expose_as` can be used to choose a different name in the Python bindings:\n\n```cpp\nstruct GENPYBIND(expose_as(Example)) example {};\n```\n\nThis can also be used to define [special methods][] like `__repr__` or `__hash__`:\n\n```cpp\nGENPYBIND(expose_as(__hash__))\nint hash() const;\n```\n\n[special methods]: https://docs.python.org/3.13/reference/datamodel.html#special-method-names\n\n## `GENPYBIND_MANUAL` (manual bindings)\n\nYou can always fall back on hand-written bindings that is embedded in the\ngenerated binding code.  This can be a convenient escape hatch for pybind11\nfeatures that are not (yet) supported by genpybind.\n\n### Inside structs and classes\n\nInside structs and classes, `parent` can be used to refer to the corresponding\n`pybind11::class_` instance.  If you need to access members of the parent class,\nyou can use `GENPYBIND_PARENT_TYPE` instead of directly referring to its name.\nThis is necessary, as the definitions is not yet complete at the point of\nthe macro.\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  bool values[2] GENPYBIND(hidden) = {false, false};\n\n  GENPYBIND_MANUAL({\n    using Example = GENPYBIND_PARENT_TYPE;\n    parent.def(\"__getitem__\",\n               [](Example\u0026 self, bool key) { return self.values[key]; });\n    parent.def(\"__setitem__\", [](Example\u0026 self, bool key, bool value) {\n      self.values[key] = value;\n    });\n  })\n```\n\n### At the top level, as a preamble or `postamble` to the binding code\n\nIf `GENPYBIND_MANUAL` is usde at the top-level, the contained code is emitted\nbefore all auto-generated binding code.  This can be useful to, e.g., import\nanother module (see the `only_expose_in` annotation on namespaces) that is used\nin function signatures:\n\n```cpp\nGENPYBIND_MANUAL({\n  ::pybind11::module::import(\"common\");\n})\n```\n\nThe `postamble` modifier can be used to embed code *after* all auto-generated\nbinding code, e.g., to dynamically patch the generated bindings:\n\n```cpp\nGENPYBIND(postamble)\nGENPYBIND_MANUAL({\n  auto example = parent.attr(\"Example\");\n  // …patch example…\n})\n```\n\nNote that `parent` can be used to refer to the corresponding `pybind11::module`.\n\nIn general, different `GENPYBIND_MANUAL` blocks are emitted in the order in\nwhich they were defined.\n\n### Extra includes in generated bindings\n\nIf you need to include additional headers in the generated bindings, you can use\n`#pragma genpybind include`.  This is useful when you want to, e.g., enable\n[transparent conversion for STL data types][pybind11-stl] or need a specific\nheader for a `GENPYBIND_MANUAL` block.  Any included header will be parsed\nduring genpybind's analysis phase, and a corresponding include directive will be\nadded to the generated code.  (This functions like a regular include directive\ngated behind `#ifdef __GENPYBIND__`, with the added effect of emitting the\ninclude directive to the generated code.)\n\n```cpp\n// clang-format off\n#ifdef __GENPYBIND__\n#pragma genpybind include \u003cpybind11/stl.h\u003e\n#endif // __GENPYBIND__\n// clang-format on\n```\n\nUnfortunately, clang-format badly mangles angle brackets inside genpybind\ninclude pragmas by inserting unwanted whitespace, hence it's turned off\nlocally here.\n\n[pybind11-stl]: https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html\n\n## Namespaces\n\nFor all accessible headers, the annotations of a particular header have to\nmatch, as long as the namespace contains at least one annotated declaration\nexposed via the bindings:\n\n```cpp\nnamespace example GENPYBIND(module) {\nstruct GENPYBIND(visible) Example {};\n}\n\n// OK: No annotated declarations\nnamespace example {\nstruct Hidden {};\n}\n\n// OK: Same annotations\nnamespace example GENPYBIND(module) {\nstruct GENPYBIND(visible) Other {};\n}\n```\n\n### `module`\n\nNamespaces can be annotated using `module` to turn them into sub-modules of the\ngenerated Python module.  Namespaces that do not have this annotation have no\neffect on the module hierarchy of the generated Python bindings.\n\nE.g., if `readme` is the name of the top-level module, `X` in the following\nexample would be exposed as `readme.nested.X`:\n```cpp\nnamespace nested GENPYBIND(module) {\nclass GENPYBIND(visible) X {};\n} // namespace nested\n```\n\n### `only_expose_in`\n\nWhen generating multiple Python libraries, `only_expose_in` should be used to\nonly expose declarations in the corresponding module.  When used on a namespace,\nall nested declarations are only exposed if one of the arguments to\n`only_expose_in` matches the name of the top-level module, which is derived from\nthe basename of the header file passed to genpybind.  For example:\n\n```cpp\n// In common.h:\nnamespace common GENPYBIND(only_expose_in(common)) {\nstruct GENPYBIND(visible) Example {};\n}\n\n// In downstream.h:\n# include \u003c…/common.h\u003e\nnamespace downstream GENPYBIND(only_expose_in(downstream)) {\nvoid sink(common::Example input) GENPYBIND(visible);\n}\n```\n\n`Example` is only available via the `common` module, instead of being duplicated\n/ exposed twice:\n```python\nfrom common import Example\nfrom downstream import sink\nsink(Example())\n```\n\n## Enums\n\n[pybind11-classes]: https://pybind11.readthedocs.io/en/stable/classes.html\n\n### `arithmetic`\n\nThe `arithmetic` modifier can be used to expose arithmetic operations on the\ngenerated enum by passing the [`pybind11::arithmetic()`][pybind11-classes]\ntag to the `pybind11::enum_` constructor:\n\n```cpp\nenum GENPYBIND(arithmetic) Access { READ = 4, WRITE = 2, EXECUTE = 1 };\n```\n\n### `export_values`\n\nThe `export_values` modifier controls whether enumerators are available in the\nparent scope.  By default, this is only the case for unscoped enums.\n\nIn the following example defaults are overridden s.t. `RED` is only available as\n`example.Color.RED` and `HIGH` is available as `example.HIGH`:\n\n```cpp\nenum GENPYBIND(export_values(false)) Color { RED, GREEN, BLUE };\n\nenum class GENPYBIND(export_values) Level { HIGH, MEDIUM, LOW };\n```\n\n## Structs and classes\n\n### `dynamic_attr` (dynamic attributes)\n\nThe `dynamic_attr` modifier can be used to allow additional attributes to be set\nat runtime, by passing the [`pybind11::dynamic_attr()`][pybind11-classes] tag to\nthe `pybind11::class_` constructor.  I.e., in the following example,\n`thing.unknown_attribute = 5` would work on an instance `thing = Thing()`.\n\n```cpp\nstruct GENPYBIND(dynamic_attr) Thing {};\n```\n\n### `hide_base`\n\nBy default, base classes included as template parameters of `pybind11::class_`,\nwhich has the effect that the inheritance relationship is represented on the\nPython side.  If that's not what you want, you can opt out using `hide_base`:\n\n```cpp\nstruct GENPYBIND(hide_base) HideAll : common::Base, Base2, Base3 {};\nstruct GENPYBIND(hide_base(\"common::Base\")) HideOne : common::Base, Base2, Base3 {};\nstruct GENPYBIND(hide_base(\"Base2\", \"Base3\")) HideTwo : common::Base, Base2, Base3 {};\n```\n\n### `holder_type`\n\nThe `holder_type` modifier can be used to set the [holder type][pybind11-smart]\nused to manage references to objects (defaults to `std::unique_ptr\u003c…\u003e`).\n\n```cpp\nstruct GENPYBIND(holder_type(\"std::shared_ptr\u003cExample\u003e\")) Example\n    : public std::enable_shared_from_this\u003cExample\u003e {\n  std::shared_ptr\u003cExample\u003e clone();\n};\n```\n\n### `implicit_conversion` (on constructor)\n\nThe `implicit_conversion` modifier can be added to converting constructors to\ndenote that the corresponding conversion should be registered as an [implicit\nconversion][pybind11-impl-conv] via `pybind11::implicitly_convertible\u003c…\u003e`:\n\n```cpp\nstruct GENPYBIND(visible) Implicit {\n  explicit Implicit(int value) GENPYBIND(implicit_conversion);\n  Implicit(Example example) GENPYBIND(implicit_conversion);\n};\n```\n\n[pybind11-impl-conv]: https://pybind11.readthedocs.io/en/stable/advanced/classes.html#implicit-conversions\n\n### `inline_base`\n\nSimilar to `hide_base` described above, `inline_base` has the effect that the\ninheritance relationship is not represented on the Python side.  In addition,\ndeclarations nested in the base class are pulled in, as if they were defined in\nthe current class.  This is useful for mixins / [CRTP][] code.\n\n```cpp\nstruct GENPYBIND(inline_base) InlineAll : common::Base, Base2, Base3 {};\nstruct GENPYBIND(inline_base(\"common::Base\")) InlineOne : common::Base, Base2, Base3 {};\nstruct GENPYBIND(inline_base(\"Base2\", \"Base3\")) InlineTwo : common::Base, Base2, Base3 {};\n```\n\n[CRTP]: https://en.cppreference.com/w/cpp/language/crtp\n\n## Templates\n\nExplicit template instantiations have the same visibility as the corresponding\ntemplate by default.  They can be selectively exposed by adding any `GENPYBIND`\nannotation.  `expose_as` can be used to rename individual instantiations.  Else,\na fallback name is generated by replacing special characters with underscores.\nE.g., `Some\u003cint\u003e` is exposed as `Some_int_`.\n\n```cpp\ntemplate \u003ctypename T\u003e struct ExposeSome {};\nextern template struct GENPYBIND(expose_as(IntSomething))\n    ExposeSome\u003cint\u003e; // selectively exposed\nextern template struct ExposeSome\u003cdouble\u003e; // not exposed\n\ntemplate \u003ctypename T\u003e struct GENPYBIND(visible) ExposeAll {};\nextern template struct ExposeAll\u003cint\u003e;\nextern template struct GENPYBIND(expose_as(BoolEx)) ExposeAll\u003cbool\u003e;\n```\n\n## Type aliases (`using` and `typedef`)\n\nType aliases are hidden (i.e., not exposed) by default and they do not inherit\nthe default visibility.  If they are marked as `visible`, a simple alias is\ncreated in the Python bindings by assigning a reference to the alias target to\nan attribute.  I.e., `using` in the following example is equivalent to the\nassignment `X.Alias = Y` in Python.\n\n```cpp\nstruct GENPYBIND(visible) X {\n  using Alias GENPYBIND(visible) = Y;\n};\n```\n\nNote: [Using declarations][using-decl] _are not type aliases_.\n[using-decl]: https://en.cppreference.com/w/cpp/language/using_declaration\n\n### `expose_here`\n\nThe `expose_here` modifier can be used to influence where the alias _target_ is\nexposed.  This can be useful to, e.g., pull in / “transplant” declarations from\nanother module or a nested scope.  Or to selectively expose single-purpose\ntemplate instances in a particular scope.  The corresponding declarations are\nthen no longer exposed in their original declaration context.\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  using tag_type GENPYBIND(expose_here) = common::Tag\u003cExample\u003e;\n};\n```\n\n### `encourage`\n\nThe `encourage` modifier can be used to make the _target_ of a type alias\nvisible in its original scope.  This can be useful to selectively instantiate\ntemplates.  (This implies an “assignment”-style alias on the Python side, as\ndescribed above.)\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  using value_type GENPYBIND(encourage) =\n      common::Ranged\u003cint, common::Gt\u003c0\u003e, common::Lt\u003c5\u003e\u003e;\n};\n```\n\n## Functions and member functions / methods\n\n### `keep_alive`\n\nThe `keep_alive` modifier corresponds to pybind11's [call\npolicy][pybind11-keep-alive] of the same name.  It can be used to indicate the\nintended lifetime of objects passed to or returned from (member) functions:\n`keep_alive(\u003cbound\u003e, \u003cwho\u003e)` means that `\u003cwho\u003e` should be kept alive at least as\nlong as `\u003cbound\u003e`.  `\u003cwho\u003e` and `\u003cbound\u003e` can either be the name of a function\nparameter, `return` (the function's return value), or `this` (the instance\na member function is called on).  Behind the scenes this is translated into the\nindex-based notation used by pybind11.\n\n\n[pybind11-keep-alive]: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#keep-alive\n\n```cpp\nstruct GENPYBIND(visible) Container {\n  GENPYBIND(keep_alive(this, resource))\n  Container(Resource *resource);\n};\n```\n\n### `noconvert`\n\n`noconvert` can be used to [disable implicit conversion][pybind11-noconvert] for\narguments passed via certain function parameters (multiple parameter names can\nbe specified):\n\n[pybind11-noconvert]: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#non-converting-arguments\n\n```cpp\nGENPYBIND(noconvert(value))\ndouble no_ints_please(double value);\n```\n\n### `required`\n\nThe `required` modifier can be used to [prohibit `None` arguments][pybind11-none]\nfor certain function parameters (multiple parameter names can be specified).\nIt is equivalent to calling `.none(false)` on the corresponding `pybind11::arg` object.\n\n[pybind11-none]: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#allow-prohibiting-none-arguments\n\n```cpp\nGENPYBIND(required(Example))\nvoid required(Example *example)\n```\n\n## `return_value_policy`\n\nThe `return_value_policy` modifier can be used to set any [return value\npolicy][pybind11-rvp] supported by pybind11:\n\n[pybind11-rvp]: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  GENPYBIND(return_value_policy(reference_internal))\n  Thing\u0026 thing();\n};\n```\n\n### `getter_for` / `setter_for` (member functions only)\n\n`getter_for` and `setter_for` can be used to expose member function as Python properties:\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  GENPYBIND(getter_for(value))\n  int getValue() const;\n\n  GENPYBIND(setter_for(value))\n  void setValue(int value);\n\n  GENPYBIND(getter_for(readonly))\n  bool getReadonly() const;\n};\n```\n\n## Operators\n\n[Special methods][special methods] like `__eq__` are emitted for unary (`+`,\n`-`, `!`) and binary (`+`, `-`, `*`, `/`, `%`, `^`, `\u0026`, `|`, `\u003c`, `\u003e`, `\u003c\u003c`,\n`\u003e\u003e`, `==`, `!=`, `\u003c=`, `\u003e=`) operators defined on classes.  Operators can be\neither member functions or free functions in a the associated namespace of the\nclass (found via ADL).  Where necessary, operators and parameters are switched:\nE.g., `operator\u003c(int, T)` cannot be exposed as `int.__lt__` so it is exposed as\n`T.__gt__` instead.\n\n```cpp\nstruct GENPYBIND(visible) Number {\n  bool operator==(Number other) const { return value == other.value; }\n  friend bool operator\u003c(const Number \u0026lhs, const Number \u0026rhs) {\n    return lhs.value \u003c rhs.value;\n  }\n  friend bool operator\u003e(int lhs, Number rhs) { return lhs \u003e rhs.value; }\n};\n```\n\nTODO: Support for the spaceship operator is pending.\n\n### `std::ostream` operators\n\n`std::ostream` operators are only exposed when opted in via, e.g.,\n`expose_as(__repr__)`:\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  GENPYBIND(expose_as(__str__))\n  friend std::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 os, const Example\u0026 value);\n};\n```\n\n## Variables and member variables / fields\n\nVariables are exposed using `def_readonly` and `def_readwrite` (and their\n`_static` variants) according to their constness.\n\n### `readonly`\n\nThe `readonly` modifier can be used if a non-const variable should be exposed as\nread-only:\n\n```cpp\nstruct GENPYBIND(visible) Example {\n  GENPYBIND(readonly)\n  int readonly_field = 0;\n};\n```\n\n# License\n\ngenpybind is provided under the MIT license.  By using, distributing, or\ncontributing to this project, you agree to the terms and conditions of this\nlicense.  See the [license file](LICENSES/MIT.txt) for details.\n\ngenpybind links against the LLVM and clang projects, which are licensed under\nthe Apache License v2.0 with LLVM Exceptions.  For details, see the included\n[license file](LICENSES/LicenseRef-LLVM.txt).  Binary distributions of genpybind\nmay incorporate unmodified parts of LLVM and/or clang through static linking.\n\n---\n\n[^1]: During normal compilation these macros have no effect on the generated code, as they are defined\n  to be empty.  The annotation system is implemented using the `annotate` attribute specifier, which\n  is available as a GNU language extension via `__attribute__((...))`.  As the annotation macros\n  only have to be parsed by clang and are empty during normal compilation the annotated code can\n  still be compiled by any C++ compiler.  See [genpybind.h][genpybind.h] for the definition of\n  the macros.\n\n[genpybind.h]: ./public/genpybind/genpybind.h\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkljohann%2Fgenpybind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkljohann%2Fgenpybind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkljohann%2Fgenpybind/lists"}