├── .gitignore ├── .gitmodules ├── .readthedocs.yml ├── CMakeLists.txt ├── LICENSE ├── MANIFEST.in ├── NOTICE ├── PYB11Generator ├── PYB11ClassAttribute.py ├── PYB11Decorators.py ├── PYB11Publicist.py ├── PYB11STLmethods.py ├── PYB11Trampoline.py ├── PYB11attr.py ├── PYB11class.py ├── PYB11enum.py ├── PYB11function.py ├── PYB11property.py ├── PYB11utils.py └── __init__.py ├── README.md ├── cmake ├── FindSphinx.cmake ├── PYB11Generator.cmake ├── add_sphinx_target.cmake ├── generate_cpp.py └── moduleCheck.py ├── docs ├── .gitignore ├── CMakeLists.txt ├── Makefile ├── PYB11decorators.rst ├── PYB11functions.rst ├── PYB11variables.rst ├── classes.rst ├── cmake.rst ├── complications.rst ├── conf.py ├── enums.rst ├── functions.rst ├── index.rst ├── intro.rst ├── make.bat ├── memory.rst ├── pybind11_objects.inv ├── pybind11_objects.txt ├── requirements.txt └── stl.rst ├── pyproject.toml ├── setup.py └── tests ├── arrays ├── CMakeLists.txt ├── MyArray.hh └── arrays_PYB11.py ├── constexpr ├── CMakeLists.txt └── constexpr_PYB11.py ├── cross_module ├── A.hh ├── A_PYB11.py ├── B.hh ├── B_PYB11.py └── CMakeLists.txt ├── inheritance ├── CMakeLists.txt ├── change_templates_PYB11.py ├── inherit_base_virtual_methods_PYB11.py └── nontemplate_inherit_from_template_PYB11.py ├── inject ├── CMakeLists.txt └── inject_test_PYB11.py ├── numpy ├── CMakeLists.txt ├── my_array.hh └── test_np_array_PYB11.py └── protected ├── A.py ├── B.py ├── CMakeLists.txt ├── protected.hh └── protected_PYB11.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | build/* 4 | dist/* 5 | PYB11Generator.egg-info 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/pybind11"] 2 | path = extern/pybind11 3 | url = https://github.com/pybind/pybind11 4 | branch = stable 5 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the OS, Python version and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.12" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/conf.py 17 | 18 | # Explicitly set the version of Python and its requirements 19 | python: 20 | install: 21 | - requirements: docs/requirements.txt 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------------------------- 2 | # This is the main PYB11Generator CMake file. PYB11Generator is pure python, and 3 | # therefore does not really need to be built. This example does show how to use 4 | # the provided CMake functions (like PYB11Generator_add_module) in order to build 5 | # pybind11 projects bound using PYB11Generator. In this case we're just building 6 | # some example/test modules. 7 | # 8 | # In order to install PYB11Generator it is simplest to use pip, as described at 9 | # https://pyb11generator.readthedocs.io/ 10 | #-------------------------------------------------------------------------------- 11 | 12 | cmake_minimum_required(VERSION 3.10) 13 | project(PYB11Generator LANGUAGES CXX) 14 | set(CMAKE_CXX_STANDARD 17) 15 | 16 | set(PYB11GENERATOR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 17 | set(CMAKE_MODULE_PATH ${PYB11GENERATOR_ROOT_DIR}/cmake) 18 | include(${PYB11GENERATOR_ROOT_DIR}/cmake/PYB11Generator.cmake) 19 | 20 | # Documentation 21 | set(ENABLE_DOCS OFF CACHE BOOL "enable sphinx PYB11Generator documentation") 22 | if (ENABLE_DOCS) 23 | if (NOT DEFINED SPHINX_EXECUTABLE) 24 | find_package(Sphinx) 25 | else() 26 | message("-- forcing SPHINX_EXECUTABLE: ${SPHINX_EXECUTABLE}") 27 | endif() 28 | add_subdirectory(docs) 29 | endif() 30 | 31 | # Tests 32 | add_subdirectory(tests/cross_module) 33 | add_subdirectory(tests/inheritance) 34 | add_subdirectory(tests/protected) 35 | add_subdirectory(tests/inject) 36 | add_subdirectory(tests/constexpr) 37 | add_subdirectory(tests/arrays) 38 | # add_subdirectory(tests/numpy) # Not ready yet -- need to augment PYB11 to support the buffer protocol to do correctly 39 | 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | BSD 3-Clause License 3 | 4 | Copyright (c) 2019, Lawrence Livermore National Security, LLC 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include cmake *.cmake 2 | recursive-include cmake *.py 3 | include LICENSE README.md pyproject.toml setup.py 4 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This work was produced under the auspices of the U.S. Department of 2 | Energy by Lawrence Livermore National Laboratory under Contract 3 | DE-AC52-07NA27344. 4 | 5 | This work was prepared as an account of work sponsored by an agency of 6 | the United States Government. Neither the United States Government nor 7 | Lawrence Livermore National Security, LLC, nor any of their employees 8 | makes any warranty, expressed or implied, or assumes any legal liability 9 | or responsibility for the accuracy, completeness, or usefulness of any 10 | information, apparatus, product, or process disclosed, or represents that 11 | its use would not infringe privately owned rights. 12 | 13 | Reference herein to any specific commercial product, process, or service 14 | by trade name, trademark, manufacturer, or otherwise does not necessarily 15 | constitute or imply its endorsement, recommendation, or favoring by the 16 | United States Government or Lawrence Livermore National Security, LLC. 17 | 18 | The views and opinions of authors expressed herein do not necessarily 19 | state or reflect those of the United States Government or Lawrence 20 | Livermore National Security, LLC, and shall not be used for advertising 21 | or product endorsement purposes. 22 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11ClassAttribute.py: -------------------------------------------------------------------------------- 1 | from .PYB11utils import * 2 | import sys, inspect 3 | 4 | #------------------------------------------------------------------------------- 5 | # PYB11generateClassAttributes 6 | # 7 | # Bind the class attributes 8 | #------------------------------------------------------------------------------- 9 | def PYB11GenerateClassAttributes(klass, klassinst, klassattrs, ss): 10 | 11 | globs, locs = globals(), locals() 12 | PYB11attrs = [x for x in dir(klass) if isinstance(eval("klass.%s" % x, globs, locs), PYB11ClassAttribute) and x in klass.__dict__] 13 | if PYB11attrs: 14 | ss("\n // Properties\n") 15 | 16 | for attrname in PYB11attrs: 17 | exec('klassinst.%s("%s", klassattrs, ss)' % (attrname, attrname)) 18 | ss("\n") 19 | 20 | return 21 | 22 | #------------------------------------------------------------------------------- 23 | # The base class for attributes, most of the implementation 24 | #------------------------------------------------------------------------------- 25 | class PYB11ClassAttribute: 26 | 27 | def __init__(self, 28 | static, 29 | pyname, 30 | cppname, 31 | returnpolicy, 32 | doc, 33 | deftype): 34 | self.static = static 35 | self.pyname = pyname 36 | self.cppname = cppname 37 | self.returnpolicy = returnpolicy 38 | self.doc = doc 39 | self.deftype = deftype 40 | return 41 | 42 | def __call__(self, 43 | name, 44 | klassattrs, 45 | ss): 46 | if self.pyname: 47 | pyname = self.pyname 48 | else: 49 | pyname = name 50 | if self.cppname: 51 | cppname = self.cppname 52 | else: 53 | cppname = name 54 | if self.static: 55 | ss(' obj.def_%s_static("%s", ' % (self.deftype, pyname)) 56 | else: 57 | ss(' obj.def_%s("%s", ' % (self.deftype, pyname)) 58 | ss(("&%(namespace)s%(cppname)s::" % klassattrs) + cppname) 59 | if self.returnpolicy: 60 | ss(', py::return_value_policy::%s' % self.returnpolicy) 61 | if self.doc: 62 | ss(', ') 63 | PYB11docstring(self.doc, ss) 64 | ss(");\n") 65 | return 66 | 67 | #------------------------------------------------------------------------------- 68 | # readwrite 69 | #------------------------------------------------------------------------------- 70 | class PYB11readwrite(PYB11ClassAttribute): 71 | 72 | def __init__(self, 73 | static = False, 74 | pyname = None, 75 | cppname = None, 76 | returnpolicy = None, 77 | doc = None): 78 | PYB11ClassAttribute.__init__(self, static, pyname, cppname, returnpolicy, doc, "readwrite") 79 | 80 | #------------------------------------------------------------------------------- 81 | # readonly 82 | #------------------------------------------------------------------------------- 83 | class PYB11readonly(PYB11ClassAttribute): 84 | 85 | def __init__(self, 86 | static = False, 87 | pyname = None, 88 | cppname = None, 89 | returnpolicy = None, 90 | doc = None): 91 | PYB11ClassAttribute.__init__(self, static, pyname, cppname, returnpolicy, doc, "readonly") 92 | 93 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11Decorators.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Decorators for PYB11 generation. 3 | #------------------------------------------------------------------------------- 4 | from functools import wraps as PYB11wraps # Add PYB11 to screen out in generation 5 | import decorator as PYB11decorator # To preserve wrapped functions args 6 | import types 7 | 8 | #------------------------------------------------------------------------------- 9 | # ignore 10 | #------------------------------------------------------------------------------- 11 | def PYB11ignore(thing): 12 | thing.PYB11ignore = True 13 | return thing 14 | 15 | #------------------------------------------------------------------------------- 16 | # template 17 | #------------------------------------------------------------------------------- 18 | class PYB11template: 19 | def __init__(self, *args): 20 | self.template = [] 21 | for t in args: 22 | assert len(t.split()) in (1,2) 23 | if len(t.split()) == 1: 24 | self.template.append("typename %s" % t) 25 | else: 26 | self.template.append(t) 27 | return 28 | def __call__(self, thing): 29 | if self.template: 30 | thing.PYB11ignore = True 31 | else: 32 | thing.PYB11ignore = False 33 | thing.PYB11template = self.template 34 | return thing 35 | 36 | #------------------------------------------------------------------------------- 37 | # template_dict 38 | # Take direct control of the template dictionary, not common 39 | #------------------------------------------------------------------------------- 40 | class PYB11template_dict: 41 | def __init__(self, val): 42 | assert isinstance(val, dict) 43 | self.val = val 44 | return 45 | def __call__(self, thing): 46 | thing.PYB11template_dict = self.val 47 | return thing 48 | 49 | #------------------------------------------------------------------------------- 50 | # Singleton (class) 51 | #------------------------------------------------------------------------------- 52 | def PYB11singleton(cls): 53 | cls.PYB11singleton = True 54 | return cls 55 | 56 | #------------------------------------------------------------------------------- 57 | # Holder (class) 58 | #------------------------------------------------------------------------------- 59 | class PYB11holder: 60 | def __init__(self, x): 61 | self.val = x 62 | return 63 | def __call__(self, thing): 64 | thing.PYB11holder = self.val 65 | return thing 66 | 67 | #------------------------------------------------------------------------------- 68 | # dynamic_attr (class) 69 | #------------------------------------------------------------------------------- 70 | def PYB11dynamic_attr(cls): 71 | cls.PYB11dynamic_attr = True 72 | return cls 73 | 74 | #------------------------------------------------------------------------------- 75 | # namespace (class or method) 76 | #------------------------------------------------------------------------------- 77 | class PYB11namespace: 78 | def __init__(self, x): 79 | self.namespace = x 80 | if self.namespace[:-2] != "::": 81 | self.namespace += "::" 82 | return 83 | def __call__(self, thing): 84 | thing.PYB11namespace = self.namespace 85 | return thing 86 | 87 | #------------------------------------------------------------------------------- 88 | # pycppname (class or method) 89 | #------------------------------------------------------------------------------- 90 | class PYB11pycppname: 91 | def __init__(self, x): 92 | self.x = x 93 | return 94 | def __call__(self, thing): 95 | thing.PYB11cppname = self.x 96 | thing.PYB11pyname = self.x 97 | return thing 98 | 99 | #------------------------------------------------------------------------------- 100 | # cppname (class or method) 101 | #------------------------------------------------------------------------------- 102 | class PYB11cppname: 103 | def __init__(self, x): 104 | self.cppname = x 105 | return 106 | def __call__(self, thing): 107 | thing.PYB11cppname = self.cppname 108 | return thing 109 | 110 | #------------------------------------------------------------------------------- 111 | # pyname (class or method) 112 | #------------------------------------------------------------------------------- 113 | class PYB11pyname: 114 | def __init__(self, x): 115 | self.pyname = x 116 | return 117 | def __call__(self, thing): 118 | thing.PYB11pyname = self.pyname 119 | return thing 120 | 121 | #------------------------------------------------------------------------------- 122 | # Virtual (method) 123 | #------------------------------------------------------------------------------- 124 | def PYB11virtual(f): 125 | f.PYB11virtual = True 126 | return f 127 | 128 | #------------------------------------------------------------------------------- 129 | # Pure virtual (method) 130 | #------------------------------------------------------------------------------- 131 | def PYB11pure_virtual(f): 132 | f.PYB11pure_virtual = True 133 | return f 134 | 135 | #------------------------------------------------------------------------------- 136 | # Protected (method) 137 | #------------------------------------------------------------------------------- 138 | def PYB11protected(f): 139 | f.PYB11protected = True 140 | return f 141 | 142 | #------------------------------------------------------------------------------- 143 | # const (method) 144 | #------------------------------------------------------------------------------- 145 | def PYB11const(f): 146 | f.PYB11const = True 147 | return f 148 | 149 | #------------------------------------------------------------------------------- 150 | # static (method) 151 | #------------------------------------------------------------------------------- 152 | def PYB11static(f): 153 | f.PYB11static = True 154 | return f 155 | 156 | #------------------------------------------------------------------------------- 157 | # noconvert (applies to arguments of method) 158 | #------------------------------------------------------------------------------- 159 | def PYB11noconvert(f): 160 | f.PYB11noconvert = True 161 | return f 162 | 163 | #------------------------------------------------------------------------------- 164 | # implementation -- provide an inline implementation in C++ (only for experts!) 165 | #------------------------------------------------------------------------------- 166 | class PYB11implementation: 167 | def __init__(self, x): 168 | self.val = x 169 | return 170 | def __call__(self, thing): 171 | thing.PYB11implementation = self.val 172 | return thing 173 | 174 | #------------------------------------------------------------------------------- 175 | # returnpolicy 176 | #------------------------------------------------------------------------------- 177 | class PYB11returnpolicy: 178 | def __init__(self, x): 179 | self.val = x 180 | return 181 | def __call__(self, thing): 182 | thing.PYB11returnpolicy = self.val 183 | return thing 184 | 185 | #------------------------------------------------------------------------------- 186 | # keepalive 187 | #------------------------------------------------------------------------------- 188 | class PYB11keepalive: 189 | def __init__(self, *args): 190 | self.val = tuple(args) 191 | assert len(self.val) == 2 192 | return 193 | def __call__(self, thing): 194 | thing.PYB11keepalive = self.val 195 | return thing 196 | 197 | #------------------------------------------------------------------------------- 198 | # call_guard 199 | #------------------------------------------------------------------------------- 200 | class PYB11call_guard: 201 | def __init__(self, x): 202 | self.val = x 203 | return 204 | def __call__(self, thing): 205 | thing.PYB11call_guard = self.val 206 | return thing 207 | 208 | #------------------------------------------------------------------------------- 209 | # module 210 | #------------------------------------------------------------------------------- 211 | class PYB11module: 212 | def __init__(self, x): 213 | self.val = x 214 | return 215 | def __call__(self, thing): 216 | if not hasattr(thing, "PYB11module"): 217 | thing.PYB11module = {} 218 | thing.PYB11module[thing] = self.val 219 | return thing 220 | 221 | #------------------------------------------------------------------------------- 222 | # operator (method) 223 | #------------------------------------------------------------------------------- 224 | def PYB11operator(f): 225 | f.PYB11operator = True 226 | return f 227 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11Publicist.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PYB11Publicist 3 | #------------------------------------------------------------------------------- 4 | import inspect 5 | import sys 6 | import io 7 | 8 | from .PYB11utils import * 9 | 10 | #------------------------------------------------------------------------------- 11 | # PYB11generateModulePublicists 12 | # 13 | # Generate publicists for any classes with protected methods. 14 | #------------------------------------------------------------------------------- 15 | def PYB11generateModulePublicists(modobj, ss): 16 | klasses = PYB11classes(modobj) 17 | known_publicists = [] 18 | for kname, klass in klasses: 19 | klassattrs = PYB11attrs(klass) 20 | template_klass = len(klassattrs["template"]) > 0 21 | mods = klassattrs["module"] 22 | if (PYB11protectedClass(klass) and 23 | ((template_klass or not klassattrs["ignore"]) and # ignore flag (except for template class)? 24 | (klassattrs["pyname"] not in known_publicists) and # has this trampoline been generated? 25 | ((klass not in mods) or mods[klass] == modobj.PYB11modulename))): # is this class imported from another mod? 26 | PYB11generatePublicist(klass, ss) 27 | return 28 | 29 | #------------------------------------------------------------------------------- 30 | # PYB11generatePublicist 31 | # 32 | # Generate the publicist class, including pure virtual hooks. 33 | #------------------------------------------------------------------------------- 34 | def PYB11generatePublicist(klass, ssout): 35 | 36 | klassattrs = PYB11attrs(klass) 37 | template_klass = len(klassattrs["template"]) > 0 38 | if klassattrs["ignore"] and not template_klass: 39 | return 40 | 41 | # Prepare in case there are templates lurking in here. 42 | fs = io.StringIO() 43 | ss = fs.write 44 | 45 | # Build the dictionary of template substitutions. 46 | Tdict = PYB11parseTemplates(klassattrs, inspect.getmro(klass)) 47 | 48 | # Compiler guard. 49 | ss("""//------------------------------------------------------------------------------ 50 | // Publicist class for %(cppname)s 51 | //------------------------------------------------------------------------------ 52 | #ifndef __publicist_%(pyname)s__ 53 | #define __publicist_%(pyname)s__ 54 | 55 | """ % klassattrs) 56 | 57 | # Namespaces 58 | for ns in klassattrs["namespace"].split("::")[:-1]: 59 | ss("namespace " + ns + " {\n") 60 | 61 | if template_klass: 62 | ss("template<") 63 | for i, name in enumerate(klassattrs["template"]): 64 | if i < len(klassattrs["template"]) - 1: 65 | ss("%s, " % name) 66 | else: 67 | ss("%s>\n" % name) 68 | 69 | # Class name 70 | ss("""class PYB11Publicist%(cppname)s: public %(full_cppname)s { 71 | public: 72 | """ % klassattrs) 73 | 74 | # Any typedefs? 75 | if hasattr(klass, "PYB11typedefs"): 76 | ss(klass.PYB11typedefs + "\n") 77 | 78 | # Publish the virtual methods of this class. 79 | methods = [(mname, meth) for (mname, meth) in PYB11ClassMethods(klass) 80 | if (PYB11attrs(meth)["protected"] and mname in klass.__dict__)] 81 | 82 | boundmeths = [] 83 | for mname, meth in methods: 84 | methattrs = PYB11attrs(meth) 85 | if methattrs["cppname"] not in boundmeths: 86 | boundmeths.append(methattrs["cppname"]) 87 | ss(" using %(full_cppname)s::" % klassattrs) 88 | ss("%(cppname)s;\n" % methattrs) 89 | 90 | # Closing 91 | ss("};\n\n") 92 | for ns in klassattrs["namespace"].split("::")[:-1]: 93 | ss("}\n") 94 | ss("\n#endif\n") 95 | 96 | # Sub any template parameters. 97 | ssout(fs.getvalue() % Tdict) 98 | 99 | return 100 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11STLmethods.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PYB11STLmethods 3 | # 4 | # Thin wrappers to generate the pybind11 STL functions. 5 | #------------------------------------------------------------------------------- 6 | import inspect 7 | from .PYB11utils import * 8 | 9 | #------------------------------------------------------------------------------- 10 | # std::vector 11 | #------------------------------------------------------------------------------- 12 | class PYB11_bind_vector: 13 | 14 | def __init__(self, 15 | element, # template element type for vector 16 | opaque = False, # should we make the type opaque? 17 | local = None): # should the opaque choice be module local? 18 | self.element = element 19 | self.opaque = opaque 20 | self.local = local 21 | return 22 | 23 | def preamble(self, modobj, ss, name): 24 | if self.opaque: 25 | ss('PYBIND11_MAKE_OPAQUE(std::vector<' + PYB11CPPsafe(self.element) + '>)\n') 26 | return 27 | 28 | def __call__(self, modobj, ss, name): 29 | ss('py::bind_vector>(m, "' + name + '"') 30 | if not self.local is None: 31 | ss(', py::module_local(') 32 | if self.local: 33 | ss('true)') 34 | else: 35 | ss('false)') 36 | ss(');\n') 37 | return 38 | 39 | #------------------------------------------------------------------------------- 40 | # std::map 41 | #------------------------------------------------------------------------------- 42 | class PYB11_bind_map: 43 | 44 | def __init__(self, 45 | key, # template key type 46 | value, # template value type 47 | opaque = False, # should we make the container opaque 48 | local = None): # should the opaque choice be module local? 49 | self.key = key 50 | self.value = value 51 | self.opaque = opaque 52 | self.local = local 53 | return 54 | 55 | def preamble(self, modobj, ss, name): 56 | if self.opaque: 57 | cppname = "std::map<" + self.key + "," + self.value + ">" 58 | ss("PYBIND11_MAKE_OPAQUE(" + PYB11CPPsafe(cppname) + ")\n") 59 | return 60 | 61 | def __call__(self, modobj, ss, name): 62 | ss('py::bind_map>(m, "' + name + '"') 63 | if not self.local is None: 64 | ss(', py::module_local(') 65 | if self.local: 66 | ss('true)') 67 | else: 68 | ss('false)') 69 | ss(');\n') 70 | return 71 | 72 | #------------------------------------------------------------------------------- 73 | # PYB11STLobjs 74 | # 75 | # Get the STL objects to bind from a module 76 | #------------------------------------------------------------------------------- 77 | def PYB11STLobjs(modobj): 78 | return [(name, obj) for (name, obj) in inspect.getmembers(modobj) 79 | if name[:5] != "PYB11" and 80 | (isinstance(obj, PYB11_bind_vector) or 81 | isinstance(obj, PYB11_bind_map))] 82 | 83 | #------------------------------------------------------------------------------- 84 | # PYB11generateModuleSTL 85 | # 86 | # Bind the STL containers in the module 87 | #------------------------------------------------------------------------------- 88 | def PYB11generateModuleSTL(modobj, ss): 89 | stuff = PYB11STLobjs(modobj) 90 | for (name, obj) in stuff: 91 | ss(" ") 92 | obj(modobj, ss, name) 93 | ss("\n") 94 | return 95 | 96 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11Trampoline.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PYB11Trampoline 3 | #------------------------------------------------------------------------------- 4 | import inspect 5 | import sys 6 | import io 7 | 8 | from .PYB11utils import * 9 | 10 | #------------------------------------------------------------------------------- 11 | # PYB11generateModuleTrampolines 12 | # 13 | # Generate trampolines for any classes with virtual methods. 14 | #------------------------------------------------------------------------------- 15 | def PYB11generateModuleTrampolines(modobj, ss): 16 | klasses = PYB11classes(modobj) 17 | 18 | # Cull to just classes with virtual methods. 19 | klasses = [(name, klass) for (name, klass) in klasses if PYB11virtualClass(klass)] 20 | 21 | # Cull for things we're ignoring 22 | newklasses = [] 23 | known_trampolines = [] 24 | for name, klass in klasses: 25 | klassattrs = PYB11attrs(klass) 26 | template_klass = len(klassattrs["template"]) > 0 27 | mods = klassattrs["module"] 28 | if ((template_klass or not klassattrs["ignore"]) and # ignore flag (except for template class)? 29 | (klassattrs["pyname"] not in known_trampolines) and # has this trampoline been generated? 30 | ((klass not in mods) or mods[klass] == modobj.PYB11modulename)): # is this class imported from another mod? 31 | newklasses.append((name, klass)) 32 | known_trampolines.append(klassattrs["pyname"]) 33 | klasses = newklasses 34 | 35 | # Generate trampolines 36 | for kname, klass in klasses: 37 | PYB11generateTrampoline(klass, ss) 38 | return 39 | 40 | #------------------------------------------------------------------------------- 41 | # PYB11generateTrampoline 42 | # 43 | # Generate the trampoline class, including pure virtual hooks. 44 | #------------------------------------------------------------------------------- 45 | def PYB11generateTrampoline(klass, ssout): 46 | 47 | klassattrs = PYB11attrs(klass) 48 | template_klass = len(klassattrs["template"]) > 0 49 | # if klassattrs["ignore"] and not template_klass: 50 | # return 51 | 52 | # # This is a bit of trickery to let us use inheritance without regenerating trampolines 53 | # if"__known_trampolines" not in PYB11generateTrampoline.__dict__: 54 | # PYB11generateTrampoline.__known_trampolines = [] 55 | # if klassattrs["pyname"] in PYB11generateTrampoline.__known_trampolines: 56 | # return 57 | # PYB11generateTrampoline.__known_trampolines.append(klassattrs["pyname"]) 58 | 59 | # # We also screen out classes imported from other modules. 60 | # mods = klassattrs["module"] 61 | # if klass in mods: 62 | # return 63 | 64 | # Prepare in case there are templates lurking in here. 65 | fs = io.StringIO() 66 | ss = fs.write 67 | 68 | # Build the dictionary of template substitutions. 69 | Tdict = PYB11parseTemplates(klassattrs, inspect.getmro(klass)) 70 | 71 | # Compiler guard. 72 | ss("""//------------------------------------------------------------------------------ 73 | // Trampoline class for %(cppname)s 74 | //------------------------------------------------------------------------------ 75 | #ifndef __trampoline_%(pyname)s__ 76 | #define __trampoline_%(pyname)s__ 77 | 78 | """ % klassattrs) 79 | 80 | # Namespaces 81 | for ns in klassattrs["namespace"].split("::")[:-1]: 82 | ss("namespace " + ns + " {\n") 83 | 84 | if template_klass: 85 | ss("template<") 86 | for i, name in enumerate(klassattrs["template"]): 87 | if i < len(klassattrs["template"]) - 1: 88 | ss("%s, " % name) 89 | else: 90 | ss("%s>\n" % name) 91 | 92 | # Build the base class hierarchy names 93 | bklassnames = [] 94 | for bklass in inspect.getmro(klass): 95 | bklassattrs = PYB11attrs(bklass) 96 | bklassname = "%(namespace)s%(cppname)s" % bklassattrs 97 | if len(bklassattrs["template"]) > 0: 98 | bklassname += "<" 99 | for i, name in enumerate(bklassattrs["template"]): 100 | assert len(name.split()) == 2 101 | nameval = name.split()[1] 102 | if name in klassattrs["template"]: 103 | bklassname += nameval 104 | else: 105 | if not nameval in Tdict: 106 | raise RuntimeError("Trampoline template base class error: %s is missing from specified template parameters %s\n (class, base) = (%s, %s)" % (nameval, Tdict, klass, bklass)) 107 | bklassname += Tdict[nameval] 108 | if i < len(bklassattrs["template"]) - 1: 109 | bklassname += ", " 110 | bklassname += ">" 111 | bklassnames.append(bklassname) 112 | assert len(bklassnames) == len(inspect.getmro(klass)) 113 | bklassnames[0] = "PYB11self" 114 | 115 | # Class name 116 | ss("""class PYB11Trampoline%(cppname)s: public %(full_cppname)s { 117 | public: 118 | using %(full_cppname)s::%(cppname)s; // inherit constructors 119 | typedef %(full_cppname)s PYB11self; // Necessary to protect macros below from names with commas in them 120 | """ % klassattrs) 121 | for bklassname in bklassnames[1:]: 122 | if bklassname != PYB11mangle(bklassname): 123 | ss(" typedef %s %s;\n" % (bklassname, PYB11mangle(bklassname))) 124 | 125 | # # Use any nested class definitions 126 | # klasses = [(x, eval("klass.%s" % x)) for x in dir(klass) if (inspect.isclass(eval("klass.%s" % x)) and x in klass.__dict__)] 127 | # for (kname, nklass) in klasses: 128 | # nklassattrs = PYB11attrs(nklass) 129 | # ss(" typedef typename %(full_cppname)s::" % klassattrs) 130 | # ss("%(cppname)s %(cppname)s;\n" % nklassattrs) 131 | 132 | # Any typedefs? 133 | if hasattr(klass, "PYB11typedefs"): 134 | typedefs = str(klass.PYB11typedefs) 135 | else: 136 | typedefs = "" 137 | 138 | # Bind the (unique) virtual methods for all classes up the inheritance tree. 139 | # We use an independent StringIO object for this, since we may have some new typedefs that 140 | # need to be added before this stuff is output to the source. 141 | methfms = io.StringIO() 142 | boundMethods = [] 143 | for (bklass, bklassname) in zip(inspect.getmro(klass), bklassnames): 144 | 145 | bklassinst = bklass() 146 | bklassattrs = PYB11attrs(bklass) 147 | methods = [(mname, meth) for (mname, meth) in PYB11ClassMethods(bklass) 148 | if (not PYB11attrs(meth)["ignore"] and 149 | (PYB11attrs(meth)["virtual"] or PYB11attrs(meth)["pure_virtual"]) and 150 | mname in bklass.__dict__)] 151 | 152 | # Look for any template parameters of the base not shared by the class in question 153 | bklasssubs = {} 154 | for name in bklassattrs["template"]: 155 | if not name in klassattrs["template"]: 156 | nameval = name.split()[1] 157 | assert nameval in Tdict 158 | bklasssubs[name] = Tdict[nameval] 159 | 160 | for mname, meth in methods: 161 | 162 | # We build this method string up independent of the output stream 163 | # until we determine if it's already been generated. 164 | fms = io.StringIO() 165 | 166 | methattrs = PYB11attrs(meth) 167 | methattrs["returnType"] = eval("bklassinst." + mname + "()") 168 | assert methattrs["returnType"] # We require the full spec for virtual methods 169 | fms.write(" virtual %(returnType)s %(cppname)s(" % methattrs) 170 | 171 | # Fill out the argument list for this method 172 | args = PYB11parseArgs(meth) 173 | for i, (argType, argName, default) in enumerate(args): 174 | fms.write("%s %s" % (argType, argName)) 175 | if i < len(args) - 1: 176 | fms.write(", ") 177 | if methattrs["const"]: 178 | fms.write(") const override { ") 179 | else: 180 | fms.write(") override { ") 181 | 182 | # At this point we can make the call of whether this is a new method. 183 | try: 184 | thpt = fms.getvalue() % Tdict 185 | except: 186 | raise RuntimeError("Unable to generate call descriptor for %s in %s->%s" % (mname, str(klass), bklassname)) 187 | if not thpt in boundMethods: 188 | boundMethods.append(fms.getvalue() % Tdict) 189 | 190 | # Check if the returnType C++ name will choke PYBIND11_OVERLOAD* 191 | returnType = methattrs["returnType"] 192 | if PYB11badchars(returnType): 193 | returnType = PYB11mangle(returnType) 194 | typedefstring = " typedef %s %s;\n" % (methattrs["returnType"], returnType) 195 | if typedefstring not in typedefs: 196 | typedefs += typedefstring 197 | methattrs["returnType"] = returnType 198 | 199 | # Check if we need to work around any reference arguments due to the pybind11 bug discussed in 200 | # https://stackoverflow.com/questions/59330279/problems-passing-a-stdvector-by-reference-though-virtual-functions-using-pybin/59331026?noredirect=1#comment104861677_59331026 201 | altered = False 202 | for i, (argType, argName, default) in enumerate(args): 203 | if "&" in argType: 204 | altered = True 205 | fms.write("\n py::object dummy%i = py::cast(&%s);\n" % (i, argName)) 206 | if altered: 207 | fms.write(" ") 208 | 209 | if methattrs["pure_virtual"]: 210 | if methattrs["cppname"] == methattrs["pyname"]: 211 | fms.write("PYBIND11_OVERLOAD_PURE(%(returnType)s, PYB11self, %(cppname)s, " % methattrs) 212 | else: 213 | fms.write('PYBIND11_OVERLOAD_PURE_NAME(%(returnType)s, PYB11self, "%(pyname)s", %(cppname)s, ' % methattrs) 214 | else: 215 | # HACK! To workaround what appears to be a bug in overloading virtual method callbacks 216 | # in pybind11 (see https://github.com/pybind/pybind11/issues/1547), we have to give 217 | # the address of the object that actually implements it. This is clealy not how a human 218 | # should have to handle this, but since we're code generating this we can do this explicit 219 | # workaround. 220 | #fms.write("PYBIND11_OVERLOAD(%(returnType)s, PYB11self, %(cppname)s, " % methattrs) 221 | if methattrs["cppname"] == methattrs["pyname"]: 222 | fms.write("PYBIND11_OVERLOAD(%(returnType)s, PYB11self, %(cppname)s, " % methattrs) 223 | else: 224 | fms.write('PYBIND11_OVERLOAD_NAME(%(returnType)s, PYB11self, "%(pyname)s", %(cppname)s, ' % methattrs) 225 | #fms.write(PYB11mangle(bklassname) + ", ") 226 | 227 | for i, (argType, argName, default) in enumerate(args): 228 | if i < len(args) - 1: 229 | fms.write(argName + ", ") 230 | else: 231 | fms.write(argName) 232 | if altered: 233 | fms.write(");\n }\n") 234 | else: 235 | fms.write("); }\n") 236 | 237 | # Write to the method overloading stream. 238 | methfms.write(fms.getvalue()) 239 | fms.close() 240 | 241 | # Write the full typdefs 242 | ss(typedefs + "\n") 243 | 244 | # Write the method overloads 245 | ss(methfms.getvalue()) 246 | methfms.close() 247 | 248 | # Closing 249 | ss("};\n\n") 250 | for ns in klassattrs["namespace"].split("::")[:-1]: 251 | ss("}\n") 252 | ss("\n#endif\n") 253 | 254 | # Sub any template parameters. 255 | ssout(fs.getvalue() % Tdict) 256 | 257 | return 258 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11attr.py: -------------------------------------------------------------------------------- 1 | from .PYB11utils import * 2 | 3 | import sys, inspect 4 | 5 | #------------------------------------------------------------------------------- 6 | # PYB11generateModuleEnums 7 | # 8 | # Bind the classes in the module 9 | #------------------------------------------------------------------------------- 10 | def PYB11generateModuleAttrs(modobj, ss): 11 | 12 | # Module attrs 13 | globs, locs = globals(), locals() 14 | stuff = [x for x in dir(modobj) if isinstance(eval("modobj.%s" % x, globs, locs), PYB11attr)] 15 | if stuff: 16 | ss('\n // module attributes\n') 17 | for pyname in stuff: 18 | inst = eval("modobj.%s" % pyname) 19 | inst(pyname, ss) 20 | 21 | return 22 | 23 | #------------------------------------------------------------------------------- 24 | # Generate an attr 25 | #------------------------------------------------------------------------------- 26 | class PYB11attr: 27 | 28 | def __init__(self, 29 | value = None, 30 | pyname = None): 31 | self.value = value 32 | self.pyname = pyname 33 | return 34 | 35 | def __call__(self, 36 | pyname, 37 | ss): 38 | if self.pyname: 39 | self.__name__ = self.pyname 40 | else: 41 | self.__name__ = pyname 42 | attrattrs = PYB11attrs(self) 43 | if self.value: 44 | attrattrs["cppname"] = self.value 45 | else: 46 | attrattrs["cppname"] = pyname 47 | ss(' m.attr("%(pyname)s") = %(cppname)s;\n' % attrattrs) 48 | return 49 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11enum.py: -------------------------------------------------------------------------------- 1 | from .PYB11utils import * 2 | 3 | import sys, inspect 4 | 5 | #------------------------------------------------------------------------------- 6 | # PYB11generateModuleEnums 7 | # 8 | # Bind the classes in the module 9 | #------------------------------------------------------------------------------- 10 | def PYB11generateModuleEnums(modobj, ss): 11 | 12 | # Module enums 13 | globs, locs = globals(), locals() 14 | enums = [x for x in dir(modobj) if isinstance(eval("modobj.%s" % x, globs, locs), PYB11enum)] 15 | if enums: 16 | ss(" //..............................................................................\n") 17 | ss(" // enum types\n") 18 | for name in enums: 19 | inst = eval("modobj.%s" % name) 20 | inst(modobj, ss) 21 | if enums: 22 | ss("\n") 23 | 24 | return 25 | 26 | #------------------------------------------------------------------------------- 27 | # Generate an enum 28 | #------------------------------------------------------------------------------- 29 | class PYB11enum: 30 | 31 | def __init__(self, 32 | values, 33 | name = None, 34 | namespace = "", 35 | cppname = None, 36 | export_values = False, 37 | doc = None): 38 | self.values = values 39 | self.name = name 40 | self.namespace = namespace 41 | if self.namespace and self.namespace[:-2] != "::": 42 | self.namespace += "::" 43 | self.cppname = cppname 44 | self.export_values = export_values 45 | self.doc = doc 46 | return 47 | 48 | def __call__(self, 49 | scope, 50 | ss, 51 | scopeattrs=None): 52 | if self.name: 53 | self.__name__ = self.name 54 | else: 55 | self.__name__ = self.getInstanceName(scope) 56 | if self.cppname is None: 57 | self.cppname = self.__name__ 58 | enumattrs = PYB11attrs(self) 59 | enumattrs["namespace"] = self.namespace 60 | enumattrs["cppname"] = self.cppname 61 | if inspect.isclass(scope): 62 | klass = scope 63 | klassattrs = scopeattrs 64 | else: 65 | klass = False 66 | 67 | if klass: 68 | ss(' py::enum_<%(namespace)s%(cppname)s::' % klassattrs) 69 | ss('%(cppname)s>(obj, "%(pyname)s"' % enumattrs) 70 | else: 71 | ss(' py::enum_<%(namespace)s%(cppname)s>(m, "%(pyname)s"' % enumattrs) 72 | 73 | if self.doc: 74 | ss(', "%s")\n' % self.doc) 75 | else: 76 | ss(')\n') 77 | 78 | for value in self.values: 79 | ss(' .value("%s", ' % value) 80 | if klass: 81 | ss('%(namespace)s%(cppname)s::' % klassattrs) 82 | ss('%(namespace)s%(cppname)s::' % enumattrs + value + ')\n') 83 | 84 | if self.export_values: 85 | ss(' .export_values();\n\n') 86 | else: 87 | ss(' ;\n\n') 88 | 89 | return 90 | 91 | def getInstanceName(self, scope): 92 | for name in dir(scope): 93 | thing = eval("scope.%s" % name) 94 | if isinstance(thing, PYB11enum) and thing == self: 95 | return name 96 | raise RuntimeError("PYB11enum: unable to find myself!") 97 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11function.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PYB11function 3 | # 4 | # Handle functions in pybind11 5 | #------------------------------------------------------------------------------- 6 | from .PYB11utils import * 7 | from .PYB11property import * 8 | import copy, io 9 | 10 | #------------------------------------------------------------------------------- 11 | # PYB11generateModuleFunctions 12 | # 13 | # Bind the methods in the module 14 | #------------------------------------------------------------------------------- 15 | def PYB11generateModuleFunctions(modobj, ss): 16 | methods = PYB11functions(modobj) 17 | if methods: 18 | ss(" //...........................................................................\n") 19 | ss(" // Methods\n") 20 | for name, meth in methods: 21 | methattrs = PYB11attrs(meth) 22 | if not methattrs["ignore"]: 23 | PYB11generateFunction(meth, methattrs, ss) 24 | 25 | # Now look for any template function instantiations. 26 | globs, locs = globals(), locals() 27 | func_templates = [x for x in dir(modobj) if isinstance(eval("modobj.%s" % x, globs, locs), PYB11TemplateFunction)] 28 | for ftname in func_templates: 29 | func_template = eval("modobj.%s" % ftname) 30 | func_template(ftname, ss) 31 | return 32 | 33 | #-------------------------------------------------------------------------------- 34 | # Make a function template instantiation 35 | #-------------------------------------------------------------------------------- 36 | class PYB11TemplateFunction: 37 | 38 | def __init__(self, 39 | func_template, 40 | template_parameters, 41 | cppname = None, 42 | pyname = None, 43 | docext = ""): 44 | self.func_template = func_template 45 | self.cppname = cppname 46 | self.pyname = pyname 47 | self.docext = docext 48 | 49 | # Create the template parameter dictionary 50 | self.template_parameters = {} 51 | funcattrs = PYB11attrs(self.func_template) 52 | if isinstance(template_parameters, str): 53 | assert len(funcattrs["template"]) == 1 54 | self.template_parameters[funcattrs["template"][0].split()[1]] = template_parameters 55 | elif isinstance(template_parameters, tuple): 56 | assert len(funcattrs["template"]) == len(template_parameters) 57 | for name, val in zip(funcattrs["template"], template_parameters): 58 | self.template_parameters[name.split()[1]] = val 59 | else: 60 | assert isinstance(template_parameters, dict) 61 | for arg in funcattrs["template"]: 62 | key = arg.split()[1] 63 | if not key in template_parameters: 64 | raise RuntimeError("Template parameter dictionary spec error: %s is missing from %s" % (key, template_parameters)) 65 | self.template_parameters = template_parameters 66 | 67 | # Check for any explicit template dictionaries 68 | if funcattrs["template_dict"]: 69 | for key in funcattrs["template_dict"]: 70 | if not key in self.template_parameters: 71 | self.template_parameters[key] = funcattrs["template_dict"][key] 72 | 73 | self.template_parameters = PYB11recurseTemplateDict(self.template_parameters) 74 | return 75 | 76 | def __call__(self, pyname, ss): 77 | funcattrs = PYB11attrs(self.func_template) 78 | 79 | # Do some template mangling (and magically put the template parameters in scope). 80 | template_ext = "<" 81 | doc_ext = "" 82 | for name in funcattrs["template"]: 83 | name = name.split()[1] 84 | val = self.template_parameters[name] 85 | exec("%s = '%s'" % (name, val)) 86 | template_ext += "%s, " % val 87 | doc_ext += "_%s_" % val.replace("::", "_").replace("<", "_").replace(">", "_") 88 | template_ext = template_ext[:-2] + ">" 89 | 90 | if self.cppname: 91 | funcattrs["cppname"] = self.cppname 92 | else: 93 | funcattrs["cppname"] += template_ext 94 | if self.pyname: 95 | funcattrs["pyname"] = self.pyname 96 | else: 97 | funcattrs["pyname"] = pyname 98 | 99 | funcattrs["template_dict"] = {} 100 | for name, val in self.template_parameters.items(): 101 | funcattrs["template_dict"][name] = val 102 | 103 | if self.func_template.__doc__: 104 | doc0 = copy.deepcopy(self.func_template.__doc__) 105 | self.func_template.__doc__ += self.docext 106 | PYB11generateFunction(self.func_template, funcattrs, ss) 107 | if self.func_template.__doc__: 108 | self.func_template.__doc__ = doc0 109 | return 110 | 111 | #------------------------------------------------------------------------------- 112 | # Generate a function definition 113 | #------------------------------------------------------------------------------- 114 | def PYB11generateFunction(meth, methattrs, ssout): 115 | fs = io.StringIO() 116 | ss = fs.write 117 | 118 | # Arguments 119 | stuff = inspect.getfullargspec(meth) 120 | nargs = len(stuff.args) 121 | argNames = stuff.args 122 | argTypes, argDefaults = [], [] 123 | if nargs > 0: 124 | for thing in stuff.defaults: 125 | if isinstance(thing, tuple): 126 | assert len(thing) == 2 127 | argTypes.append(thing[0]) 128 | argDefaults.append(thing[1]) 129 | else: 130 | argTypes.append(thing) 131 | argDefaults.append(None) 132 | assert len(argNames) == nargs 133 | assert len(argTypes) == nargs 134 | assert len(argDefaults) == nargs 135 | 136 | # Return type 137 | returnType = meth(*tuple(stuff.args)) 138 | methattrs["returnType"] = returnType 139 | 140 | # Build the argument string if any 141 | argString = "" 142 | for argType, argName, default in zip(argTypes, argNames, argDefaults): 143 | argString += (', "%s"_a' % argName) 144 | if methattrs["noconvert"]: 145 | argString += '.noconvert()' 146 | if not default is None: 147 | argString += ("=" + default) 148 | 149 | # Write the binding 150 | ss(' m.def("%(pyname)s", ' % methattrs) 151 | 152 | # If there is an implementation, short-circuit the rest of our work. 153 | if methattrs["implementation"]: 154 | ss(methattrs["implementation"] + argString) 155 | 156 | elif returnType: 157 | ss("(%s (*)(" % returnType) 158 | for i, argType in enumerate(argTypes): 159 | ss(argType) 160 | if i < nargs - 1: 161 | ss(", ") 162 | ss(")) &%(namespace)s%(cppname)s" % methattrs + argString) 163 | else: 164 | ss("&%(namespace)s%(cppname)s" % methattrs) 165 | 166 | # Is there a return value policy? 167 | if methattrs["returnpolicy"]: 168 | ss(", py::return_value_policy::%s" % methattrs["returnpolicy"]) 169 | 170 | # Is there a call guard? 171 | if methattrs["call_guard"]: 172 | ss(", py::call_guard<%s>()" % methattrs["call_guard"]) 173 | 174 | # Is there a keep_alive policy? 175 | if methattrs["keepalive"]: 176 | assert isinstance(methattrs["keepalive"], tuple) 177 | assert len(methattrs["keepalive"]) == 2 178 | ss(", py::keep_alive<%i, %i>()" % methattrs["keepalive"]) 179 | 180 | # Write the doc string 181 | doc = inspect.getdoc(meth) 182 | if doc: 183 | ss(", ") 184 | PYB11docstring(doc, ss) 185 | ss(");\n") 186 | 187 | ssout(fs.getvalue() % methattrs["template_dict"]) 188 | fs.close() 189 | return 190 | 191 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11property.py: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------------------------- 2 | # PYB11property 3 | # 4 | # Stuff for handling properties in pybind11 5 | #-------------------------------------------------------------------------------- 6 | from .PYB11Decorators import * 7 | from .PYB11utils import * 8 | import inspect, types 9 | 10 | #-------------------------------------------------------------------------------- 11 | # Class to help making a property, in cases where python's ordinary "property" 12 | # method isn't what we want. 13 | #-------------------------------------------------------------------------------- 14 | class PYB11property: 15 | 16 | def __init__(self, 17 | returnType = None, 18 | getter = None, 19 | setter = None, 20 | doc = None, 21 | getterraw = None, 22 | setterraw = None, 23 | getterconst = True, 24 | setterconst = False, 25 | static = None, 26 | constexpr = False, 27 | returnpolicy = None): 28 | self.returnType = returnType 29 | self.getter = getter 30 | self.setter = setter 31 | self.doc = doc 32 | self.getterraw = getterraw 33 | self.setterraw = setterraw 34 | self.getterconst = getterconst 35 | self.setterconst = setterconst 36 | self.static = static 37 | self.constexpr = constexpr 38 | self.returnpolicy = returnpolicy 39 | 40 | assert self.getter is None or self.getterraw is None, "PYB11property: cannot specify both getter and getterraw" 41 | assert self.setter is None or self.setterraw is None, "PYB11property: cannot specify both setter and setterraw" 42 | assert (not self.constexpr) or (self.setter is None and self.setterraw is None), "PYB11property: cannot have a setter for constexpr" 43 | return 44 | 45 | def __call__(self, propname, klassattrs, ss): 46 | if not self.getterraw and self.getter is None: 47 | self.getter = propname 48 | 49 | if self.static: 50 | if self.setter: 51 | proptype = "_static" 52 | else: 53 | proptype = "_readonly_static" 54 | else: 55 | if self.setter or self.setterraw: 56 | proptype = "" 57 | else: 58 | proptype = "_readonly" 59 | 60 | ss(' obj.def_property%s("%s", ' % (proptype, propname)) 61 | 62 | # getter code 63 | if self.getterraw: 64 | ss(self.getterraw) 65 | else: 66 | 67 | # If this is a constexpr, just hardwire a lambda function accessor 68 | if self.constexpr: 69 | if self.static: 70 | ss(('[](py::object)' % klassattrs)) 71 | else: 72 | ss(('[](const %(namespace)s%(cppname)s& self)' % klassattrs)) 73 | if self.returnType: 74 | ss(' -> ' + self.returnType) 75 | if self.static: 76 | ss((' { return %(namespace)s%(cppname)s::' % klassattrs) + self.getter + '; }') 77 | else: 78 | ss((' { return self.' % klassattrs) + self.getter + '; }') 79 | 80 | else: 81 | if self.returnType: 82 | ss('(%s ' % self.returnType) 83 | if self.static: 84 | ss('(%(namespace)s*)()' % klassattrs) 85 | else: 86 | ss('(%(namespace)s%(cppname)s::*)()' % klassattrs) 87 | if self.getterconst: 88 | ss(' const') 89 | ss(') ') 90 | ss('&%(namespace)s%(cppname)s::' % klassattrs + self.getter) 91 | 92 | # setter, if any 93 | if self.setterraw: 94 | ss(', %s' % self.setterraw) 95 | else: 96 | if self.setter: 97 | ss(', ') 98 | if self.returnType: 99 | if self.static: 100 | ss('(void (%(namespace)s*)' % klassattrs) 101 | else: 102 | ss('(void (%(namespace)s%(cppname)s::*)' % klassattrs) 103 | ss('(%s) ' % self.returnType) 104 | if self.setterconst: 105 | ss(' const') 106 | ss(')') 107 | ss(' &%(namespace)s%(cppname)s::' % klassattrs + self.setter) 108 | 109 | # Is there a return policy? 110 | if self.returnpolicy: 111 | ss(", py::return_value_policy::" + self.returnpolicy) 112 | 113 | # Is there a docstring? 114 | if self.doc: 115 | ss(", ") 116 | PYB11docstring(self.doc, ss) 117 | ss(');\n') 118 | return 119 | 120 | #-------------------------------------------------------------------------------- 121 | # Make a property 122 | #-------------------------------------------------------------------------------- 123 | def PYB11GenerateProperty(propname, 124 | prop, 125 | klassinst, 126 | klassattrs, 127 | ss): 128 | 129 | getter = prop.fget 130 | setter = prop.fset 131 | doc = prop.__doc__ 132 | assert not getter is None 133 | 134 | # Write the getter property formula. 135 | returnType = eval("klassinst." + getter.__name__ + "()") 136 | getterattrs = PYB11attrs(getter) 137 | getterattrs["returnType"] = returnType 138 | getterattrs["namespace"] = "%(namespace)s" % klassattrs 139 | getterattrs["classcppname"] = "%(cppname)s" % klassattrs 140 | if getterattrs["protected"]: 141 | getterattrs["classcppname"] = "PYB11Publicist" + getterattrs["classcppname"] 142 | 143 | # What kind of property do we have (readwrite, readonly, static, etc.)? 144 | if getterattrs["static"]: 145 | if setter: 146 | proptype = "_static" 147 | else: 148 | proptype = "_readonly_static" 149 | else: 150 | if setter: 151 | proptype = "" 152 | else: 153 | proptype = "_readonly" 154 | 155 | ss(' obj.def_property%s("%s", (%s ' % (proptype, propname, returnType)) 156 | 157 | if getterattrs["static"]: 158 | ss('(%(namespace)s*)()' % klassattrs) 159 | else: 160 | ss('(%(namespace)s%(cppname)s::*)()' % klassattrs) 161 | if getterattrs["const"]: 162 | ss('const)') 163 | else: 164 | ss(')') 165 | ss(' &%(namespace)s%(classcppname)s::%(cppname)s' % getterattrs) 166 | 167 | # setter, if any 168 | if setter: 169 | setterattrs = PYB11attrs(setter) 170 | setterattrs["namespace"] = "%(namespace)s" % klassattrs 171 | setterattrs["classcppname"] = "%(cppname)s" % klassattrs 172 | if setterattrs["protected"]: 173 | setterattrs["classcppname"] = "PYB11Publicist" + setterattrs["classcppname"] 174 | args = PYB11parseArgs(setter) 175 | assert len(args) == 1, "Bad number of arguments to property %s" % propname 176 | if setterattrs["static"]: 177 | ss(', (void (%(namespace)s*)' % klassattrs) 178 | else: 179 | ss(', (void (%(namespace)s%(cppname)s::*)' % klassattrs) 180 | ss('(%s) ' % args[0][0]) 181 | if setterattrs["const"]: 182 | ss(' const)') 183 | else: 184 | ss(')') 185 | ss(' &%(namespace)s%(classcppname)s::%(cppname)s' % setterattrs) 186 | 187 | # Is there a return policy? 188 | if getterattrs["returnpolicy"]: 189 | ss(", py::return_value_policy::%(returnpolicy)s" % getterattrs) 190 | 191 | # Is there a docstring? 192 | if doc: 193 | ss(", ") 194 | PYB11docstring(doc, ss) 195 | ss(');\n') 196 | return 197 | 198 | #------------------------------------------------------------------------------- 199 | # PYB11generateClassProperties 200 | # 201 | # Bind the class properties 202 | #------------------------------------------------------------------------------- 203 | def PYB11GenerateClassProperties(klass, klassinst, klassattrs, ss): 204 | 205 | globs, locs = globals(), locals() 206 | props = [x for x in dir(klass) if isinstance(eval("klass.%s" % x, globs, locs), property) and x in klass.__dict__] 207 | PYB11props = [x for x in dir(klass) if isinstance(eval("klass.%s" % x, globs, locs), PYB11property) and x in klass.__dict__] 208 | if props or PYB11props: 209 | ss("\n // Properties\n") 210 | 211 | for propname in props: 212 | prop = eval("klass.%s" % propname) 213 | PYB11GenerateProperty(propname, prop, klassinst, klassattrs, ss) 214 | for propname in PYB11props: 215 | exec('klassinst.%s("%s", klassattrs, ss)' % (propname, propname)) 216 | ss("\n") 217 | 218 | return 219 | -------------------------------------------------------------------------------- /PYB11Generator/PYB11utils.py: -------------------------------------------------------------------------------- 1 | from .PYB11Decorators import * 2 | import inspect, io, types, itertools 3 | 4 | #------------------------------------------------------------------------------- 5 | # PYB11inject 6 | # 7 | # Add methods defined in fromclass to tocls 8 | #------------------------------------------------------------------------------- 9 | # We need this bit of trickery from stackoverflow to make a new copy of the method 10 | # in question. Necessary to avoid corrupting attributes of the original classes 11 | # methods. 12 | def PYB11copy_func(f, name=None): 13 | ''' 14 | return a function with same code, globals, defaults, closure, and 15 | name (or provide a new name) 16 | ''' 17 | fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__, 18 | f.__defaults__, f.__closure__) 19 | # in case f was given attrs (note this dict is a shallow copy): 20 | fn.__dict__.update(f.__dict__) 21 | return fn 22 | 23 | def PYB11inject(fromcls, tocls, 24 | virtual = None, 25 | pure_virtual = None): 26 | assert not (virtual and pure_virtual), "PYB11inject: cannot specify both virtual and pure_virtual as True!" 27 | 28 | # Methods 29 | globs, locs = globals(), locals() 30 | names = [x for x in dir(fromcls) if (inspect.isfunction(eval('fromcls.%s' % x, globs, locs)))] # Replaced ismethod -> isfunction in Python 3 conversion 31 | for name in names: 32 | exec('''tocls.%(name)s = PYB11copy_func(fromcls.%(name)s)''' % {"name": name}) 33 | #exec('''tocls.%(name)s = copy_func(eval("fromcls.__dict__['%(name)s']"))''' % {"name": name}) 34 | if not virtual is None: 35 | exec('tocls.%s.__dict__["PYB11virtual"] = %s' % (name, virtual)) 36 | if not pure_virtual is None: 37 | exec('tocls.%s.__dict__["PYB11pure_virtual"] = %s' % (name, pure_virtual)) 38 | 39 | # Properties 40 | from .PYB11class import PYB11TemplateMethod 41 | names = [x for x in dir(fromcls) if isinstance(eval('fromcls.%s' % x, globs, locs), PYB11TemplateMethod)] 42 | for name in names: 43 | exec('''tocls.%(name)s = PYB11TemplateMethod(func_template = fromcls.%(name)s.func_template, 44 | template_parameters = [x[1] for x in fromcls.%(name)s.template_parameters], 45 | cppname = fromcls.%(name)s.cppname, 46 | pyname = fromcls.%(name)s.pyname, 47 | docext = fromcls.%(name)s.docext)''' % {"name": name}) 48 | 49 | # Properties 50 | from .PYB11property import PYB11property 51 | names = [x for x in dir(fromcls) if isinstance(eval('fromcls.%s' % x, globs, locs), PYB11property)] 52 | for name in names: 53 | exec('''tocls.%(name)s = PYB11property(returnType = fromcls.%(name)s.returnType, 54 | getter = fromcls.%(name)s.getter, 55 | setter = fromcls.%(name)s.setter, 56 | doc = fromcls.%(name)s.doc, 57 | getterraw = fromcls.%(name)s.getterraw, 58 | setterraw = fromcls.%(name)s.setterraw, 59 | getterconst = fromcls.%(name)s.getterconst, 60 | setterconst = fromcls.%(name)s.setterconst, 61 | static = fromcls.%(name)s.static, 62 | returnpolicy = fromcls.%(name)s.returnpolicy)''' % {"name": name}) 63 | 64 | # Attributes 65 | from .PYB11ClassAttribute import PYB11ClassAttribute 66 | names = [x for x in dir(fromcls) if isinstance(eval('fromcls.%s' % x, globs, locs), PYB11ClassAttribute)] 67 | for name in names: 68 | exec('''tocls.%(name)s = PYB11ClassAttribute(static = fromcls.%(name)s.static, 69 | pyname = fromcls.%(name)s.pyname, 70 | cppname = fromcls.%(name)s.cppname, 71 | returnpolicy = fromcls.%(name)s.returnpolicy, 72 | doc = fromcls.%(name)s.doc, 73 | deftype = fromcls.%(name)s.deftype)''' % {"name": name}) 74 | 75 | return 76 | 77 | #------------------------------------------------------------------------------- 78 | # Return the base classes of a class 79 | # 80 | # Computes a dictionary giving the direct bases for all classes in the 81 | # inheritance hierarchy of a class. 82 | #------------------------------------------------------------------------------- 83 | def PYB11getBaseClasses(klass): 84 | stuff = inspect.getclasstree(inspect.getmro(klass), unique=True) 85 | def flatten(s, result): 86 | if type(s) is list: 87 | for val in s: 88 | s = flatten(val, result) 89 | else: 90 | assert len(s) == 2 # pair 91 | if not s[0] is object: 92 | result.append((s[0], tuple([x for x in s[1] if not x is object]))) 93 | flatstuff = [] 94 | flatten(stuff, flatstuff) 95 | result = { k[0] : k[1] for k in flatstuff } 96 | return result 97 | 98 | #------------------------------------------------------------------------------- 99 | # Key function to sort lists by source code order. 100 | #------------------------------------------------------------------------------- 101 | def PYB11sort_by_line(stuff): 102 | from .PYB11class import PYB11TemplateClass 103 | name, obj = stuff 104 | if isinstance(obj, PYB11TemplateClass): 105 | #return obj.order + 0 106 | try: 107 | source, lineno = inspect.findsource(obj.klass_template) 108 | except: 109 | raise RuntimeError("Cannot find source for %s?" % name) 110 | #print " **> ", name, lineno 111 | return lineno 112 | else: 113 | try: 114 | source, lineno = inspect.findsource(obj) 115 | except: 116 | raise RuntimeError("Cannot find source for %s?" % name) 117 | #print " ==> ", name, lineno 118 | return lineno 119 | 120 | #------------------------------------------------------------------------------- 121 | # PYB11sort_by_inheritance 122 | # 123 | # Key sorting function to put base classes first. 124 | #------------------------------------------------------------------------------- 125 | class PYB11sort_by_inheritance: 126 | def __init__(self, klasses): 127 | from .PYB11class import PYB11TemplateClass 128 | 129 | # First pass, order by line number 130 | self.keys = {} 131 | for (name, obj) in klasses: 132 | if isinstance(obj, PYB11TemplateClass): 133 | klass = obj.klass_template 134 | else: 135 | klass = obj 136 | self.keys[klass] = PYB11sort_by_line((name, klass)) 137 | 138 | # Now make sure classes come after any of their bases 139 | changed = True 140 | while changed: 141 | changed = False 142 | for (name, obj) in klasses: 143 | if isinstance(obj, PYB11TemplateClass): 144 | klass = obj.klass_template 145 | else: 146 | klass = obj 147 | for bklass in inspect.getmro(klass)[1:]: 148 | if (bklass in self.keys) and (self.keys[klass] <= self.keys[bklass]): 149 | self.keys[klass] = self.keys[bklass] + 1 150 | changed = True 151 | 152 | def __call__(self, stuff): 153 | from .PYB11class import PYB11TemplateClass 154 | obj = stuff[1] 155 | if isinstance(obj, PYB11TemplateClass): 156 | klass = obj.klass_template 157 | else: 158 | klass = obj 159 | return self.keys[klass] 160 | 161 | #------------------------------------------------------------------------------- 162 | # PYB11classes 163 | # 164 | # Get the classes to bind from a module 165 | #------------------------------------------------------------------------------- 166 | def PYB11classes(modobj): 167 | result = [(name, cls) for (name, cls) in inspect.getmembers(modobj, predicate=inspect.isclass) 168 | if name[:5] != "PYB11"] 169 | return sorted(result, key = PYB11sort_by_line) 170 | 171 | #------------------------------------------------------------------------------- 172 | # PYB11othermods 173 | # 174 | # Get the modules we should import if any 175 | #------------------------------------------------------------------------------- 176 | def PYB11othermods(modobj): 177 | if hasattr(modobj, "import_modules"): 178 | return modobj.import_modules 179 | else: 180 | return [] 181 | 182 | #------------------------------------------------------------------------------- 183 | # PYB11classTemplateInsts 184 | # 185 | # Get the template class instantiations to bind from a module 186 | #------------------------------------------------------------------------------- 187 | def PYB11classTemplateInsts(modobj): 188 | from .PYB11class import PYB11TemplateClass 189 | globs, locs = globals(), locals() 190 | result = [x for x in dir(modobj) if isinstance(eval("modobj.%s" % x, globs, locs), PYB11TemplateClass)] 191 | result = [(x, eval("modobj.%s" % x, globs, locs)) for x in result] 192 | return sorted(result, key = PYB11sort_by_line) 193 | 194 | #------------------------------------------------------------------------------- 195 | # PYB11ClassMethods 196 | # 197 | # Get the methods to bind from a class 198 | #------------------------------------------------------------------------------- 199 | def PYB11ClassMethods(obj): 200 | result = inspect.getmembers(obj, predicate=inspect.isfunction) 201 | #result = inspect.getmembers(obj, predicate=inspect.ismethod) 202 | # It's nice to sort in the same order the user created, but not necessary 203 | try: 204 | result.sort(key = PYB11sort_by_line) 205 | except: 206 | pass 207 | return result 208 | 209 | #------------------------------------------------------------------------------- 210 | # PYB11ThisClassMethods 211 | # 212 | # Cull the methods found in PYB11ClassMethods to just those defined locally 213 | # in obj. 214 | #------------------------------------------------------------------------------- 215 | def PYB11ThisClassMethods(obj): 216 | result = PYB11ClassMethods(obj) 217 | return [(name, meth) for (name, meth) in result if name in obj.__dict__] 218 | 219 | #------------------------------------------------------------------------------- 220 | # PYB11functions 221 | # 222 | # Get the functions to bind from a module 223 | #------------------------------------------------------------------------------- 224 | def PYB11functions(modobj): 225 | result = [(name, meth) for (name, meth) in inspect.getmembers(modobj, predicate=inspect.isfunction) 226 | if name[:5] != "PYB11"] 227 | # It's nice to sort in the same order the user created, but not necessary 228 | try: 229 | result.sort(key = PYB11sort_by_line) 230 | except: 231 | pass 232 | return result 233 | 234 | #------------------------------------------------------------------------------- 235 | # PYB11parseArgs 236 | # 237 | # Return (argType, argName, ) 238 | #------------------------------------------------------------------------------- 239 | def PYB11parseArgs(meth): 240 | stuff = inspect.getfullargspec(meth) 241 | result = [] 242 | if stuff.defaults: 243 | nargs = len(stuff.defaults) 244 | for argName, val in zip(stuff.args[-nargs:], stuff.defaults): 245 | if isinstance(val, tuple): 246 | assert len(val) == 2 247 | argType, default = val 248 | else: 249 | argType, default = val, None 250 | result.append((argType, argName, default)) 251 | return result 252 | 253 | #------------------------------------------------------------------------------- 254 | # PYB11recurseTemplateDict 255 | # 256 | # Recursively iterate over a dictionary of template parameters until all value 257 | # parameters have been resolved. 258 | #------------------------------------------------------------------------------- 259 | def PYB11recurseTemplateDict(Tdict): 260 | done = False 261 | itcount = 0 262 | while not done and itcount < 100: 263 | done = True 264 | itcount += 1 265 | for key, val in Tdict.items(): 266 | if "%(" in val: 267 | done = False 268 | Tdict[key] = val % Tdict 269 | if itcount == 100: 270 | raise RuntimeError("PYB11recurseTemplate failed to resolve all values in %s" % Tdict) 271 | return Tdict 272 | 273 | #------------------------------------------------------------------------------- 274 | # PYB11parseTemplates 275 | # 276 | # Examine the attributes dictionary and return the template arg information. 277 | #------------------------------------------------------------------------------- 278 | def PYB11parseTemplates(attrs, bklasses = None): 279 | # Get original attributes 280 | Tdict = {key.split()[1]:key.split()[1] for key in attrs["template"]} 281 | 282 | # Add user-specified attributes 283 | if attrs["template_dict"]: 284 | for key, value in list(attrs["template_dict"].items()): 285 | if not key in Tdict: 286 | Tdict[key] = value 287 | 288 | # Add user-specified attributes from base classes 289 | if bklasses is not None: 290 | for bklass in bklasses: 291 | bklassattrs = PYB11attrs(bklass) 292 | if bklassattrs["template_dict"]: 293 | for key, value in list(bklassattrs["template_dict"].items()): 294 | if key not in Tdict: 295 | Tdict[key] = value 296 | 297 | # Recursively apply the Tdict to itself until all %()s patterns have been resolved 298 | Tdict = PYB11recurseTemplateDict(Tdict) 299 | 300 | return Tdict 301 | 302 | #------------------------------------------------------------------------------- 303 | # PYB11virtualClass 304 | # 305 | # Test if the given class has virtual methods. 306 | #------------------------------------------------------------------------------- 307 | def PYB11virtualClass(klass): 308 | klassattrs = PYB11attrs(klass) 309 | allmethods = PYB11ClassMethods(klass) 310 | virtual = False 311 | for mname, meth in allmethods: 312 | methattrs = PYB11attrs(meth) 313 | if methattrs["virtual"] or methattrs["pure_virtual"]: 314 | virtual = True 315 | return virtual 316 | 317 | #------------------------------------------------------------------------------- 318 | # PYB11protectedClass 319 | # 320 | # Test if the given class has protected methods. 321 | #------------------------------------------------------------------------------- 322 | def PYB11protectedClass(klass): 323 | klassattrs = PYB11attrs(klass) 324 | allmethods = PYB11ThisClassMethods(klass) 325 | protected = False 326 | for mname, meth in allmethods: 327 | methattrs = PYB11attrs(meth) 328 | if methattrs["protected"]: 329 | protected = True 330 | return protected 331 | 332 | #------------------------------------------------------------------------------- 333 | # PYB11badchars 334 | # 335 | # Check if any of the forbidden characters for PYBIND11_OVERLOAD* are present. 336 | #------------------------------------------------------------------------------- 337 | def PYB11badchars(name): 338 | return any((c in ("<", ">", ",")) for c in name) 339 | 340 | #------------------------------------------------------------------------------- 341 | # PYB11mangle 342 | # 343 | # Mangle a string to a safe C++ variable name. 344 | #------------------------------------------------------------------------------- 345 | def PYB11mangle(name): 346 | result = name.replace("<", "__").replace(">", "__").replace("::", "_").replace(", ", "_").replace(",", "_").replace("*", "_ptr_").replace("&", "_ampsnd_").replace(" ", "_") 347 | return result 348 | 349 | #------------------------------------------------------------------------------- 350 | # Union of dictionarys. 351 | #------------------------------------------------------------------------------- 352 | def PYB11union_dict(*args): 353 | result = {} 354 | for d in args: 355 | for key in d: 356 | result[key] = d[key] 357 | return result 358 | 359 | #------------------------------------------------------------------------------- 360 | # PYB11CPPsafe 361 | # 362 | # Mangle a string to make commas safe for CPP directives. 363 | #------------------------------------------------------------------------------- 364 | def PYB11CPPsafe(string): 365 | return string.replace(",", " PYB11COMMA ") 366 | 367 | #------------------------------------------------------------------------------- 368 | # PYB11cppname_exts 369 | # 370 | # Return the C++ template <...> description, and a mangled string thereof. 371 | #------------------------------------------------------------------------------- 372 | def PYB11cppname_exts(templateargs): 373 | tt, mt = "", "" 374 | if templateargs: 375 | tt = "<" 376 | for i, arg in enumerate(templateargs): 377 | arg = arg.split()[1] 378 | if i < len(templateargs) - 1: 379 | tt += "%s," % arg 380 | else: 381 | tt += "%s>" % arg 382 | mt = PYB11mangle(tt) 383 | return tt, mt 384 | 385 | #------------------------------------------------------------------------------- 386 | # PYB11indentedIO 387 | # 388 | # Add extra indentation to an output stream. 389 | #------------------------------------------------------------------------------- 390 | class PYB11indentedIO: 391 | def __init__(self, prefix): 392 | self.prefix = prefix 393 | self.fs = io.StringIO() 394 | return 395 | def __call__(self, stuff): 396 | newstuff = stuff.replace("\n", "\n" + self.prefix) 397 | self.fs.write(newstuff) 398 | return 399 | def getvalue(self): 400 | return self.fs.getvalue() 401 | def close(self): 402 | self.fs.close() 403 | 404 | #------------------------------------------------------------------------------- 405 | # PYB11docstring 406 | # 407 | # Generate a reasonably formatted doc string 408 | #------------------------------------------------------------------------------- 409 | def PYB11docstring(doc, ss): 410 | if doc: 411 | stuff = doc.split("\n") 412 | if len(stuff) == 1: 413 | ss('"%s"' % doc.replace('"', '\\"')) 414 | else: 415 | ss("\n") 416 | for i, line in enumerate(doc.split('\n')): 417 | ss(' "%s\\n"' % line.replace('"', '\\"')); 418 | if i < len(stuff) - 1: 419 | ss("\n") 420 | return 421 | 422 | #------------------------------------------------------------------------------- 423 | # PYB11attrs 424 | # 425 | # Read the possible PYB11 generation attributes from the obj 426 | #------------------------------------------------------------------------------- 427 | def PYB11attrs(obj): 428 | d = {"pyname" : obj.__name__, 429 | "cppname" : obj.__name__, 430 | "ignore" : False, 431 | "namespace" : "", 432 | "singleton" : False, 433 | "holder" : None, 434 | "exposeBaseOverloads" : True, 435 | "dynamic_attr" : None, 436 | "virtual" : False, 437 | "pure_virtual" : False, 438 | "protected" : False, 439 | "const" : False, 440 | "static" : False, 441 | "noconvert" : False, 442 | "implementation" : None, 443 | "returnpolicy" : None, 444 | "keepalive" : None, 445 | "call_guard" : None, 446 | "template" : (), 447 | "template_dict" : {}, 448 | "module" : {}, 449 | "operator" : False} 450 | 451 | # A few special funtion mappings 452 | if d["pyname"] == "__call__": 453 | d["cppname"] = "operator()" 454 | 455 | for key in d: 456 | if hasattr(obj, "PYB11" + key): 457 | d[key] = eval("obj.PYB11%s" % key) 458 | safeexts= PYB11cppname_exts(d["template"]) 459 | d["full_cppname"] = d["cppname"] + safeexts[0] 460 | d["mangle_cppname"] = d["cppname"] + safeexts[1] 461 | return d 462 | -------------------------------------------------------------------------------- /PYB11Generator/__init__.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PYB11Generator 3 | #------------------------------------------------------------------------------- 4 | import inspect 5 | import sys 6 | from .PYB11utils import * 7 | from .PYB11Decorators import * 8 | from .PYB11STLmethods import * 9 | from .PYB11function import * 10 | from .PYB11class import * 11 | from .PYB11Publicist import * 12 | from .PYB11enum import * 13 | from .PYB11attr import * 14 | 15 | #------------------------------------------------------------------------------- 16 | # PYB11generateModule 17 | #------------------------------------------------------------------------------- 18 | def PYB11generateModule(modobj, modname=None, filename=None): 19 | if modname is None: 20 | modname = modobj.__name__ 21 | modobj.PYB11modulename = modname 22 | if filename is None: 23 | filename = modname + ".cc" 24 | with open(filename, "w") as f: 25 | ss = f.write 26 | PYB11generateModuleStart(modobj, ss, modname) 27 | 28 | # enums 29 | PYB11generateModuleEnums(modobj, ss) 30 | 31 | # STL types 32 | PYB11generateModuleSTL(modobj, ss) 33 | 34 | # classes 35 | PYB11generateModuleClasses(modobj, ss) 36 | 37 | # methods 38 | PYB11generateModuleFunctions(modobj, ss) 39 | 40 | # Attributes 41 | PYB11generateModuleAttrs(modobj, ss) 42 | 43 | # Closing 44 | ss("}\n") 45 | f.close() 46 | 47 | return 48 | 49 | #------------------------------------------------------------------------------- 50 | # PYB11generateModuleStart 51 | # 52 | # All the stuff up to the methods. 53 | #------------------------------------------------------------------------------- 54 | def PYB11generateModuleStart(modobj, ss, name): 55 | 56 | # Compiler guard. 57 | ss("""//------------------------------------------------------------------------------ 58 | // Module %(name)s 59 | //------------------------------------------------------------------------------ 60 | // Put Python includes first to avoid compile warnings about redefining _POSIX_C_SOURCE 61 | #include "pybind11/pybind11.h" 62 | #include "pybind11/stl_bind.h" 63 | #include "pybind11/stl.h" 64 | #include "pybind11/functional.h" 65 | #include "pybind11/operators.h" 66 | 67 | namespace py = pybind11; 68 | using namespace pybind11::literals; 69 | 70 | #define PYB11COMMA , 71 | 72 | """ % {"name" : name}) 73 | 74 | # Includes 75 | if hasattr(modobj, "PYB11includes"): 76 | for inc in modobj.PYB11includes: 77 | ss('#include %s\n' % inc) 78 | ss("\n") 79 | 80 | # Use namespaces 81 | if hasattr(modobj, "PYB11namespaces"): 82 | for ns in modobj.PYB11namespaces: 83 | ss("using namespace " + ns + ";\n") 84 | ss("\n") 85 | 86 | # Use objects from scopes 87 | if hasattr(modobj, "PYB11scopenames"): 88 | for scopename in modobj.PYB11scopenames: 89 | ss("using " + scopename + "\n") 90 | ss("\n") 91 | 92 | # Preamble 93 | if hasattr(modobj, "PYB11preamble"): 94 | ss(modobj.PYB11preamble + "\n") 95 | ss("\n") 96 | 97 | # Some pybind11 types need their own preamble. 98 | for objname, obj in PYB11STLobjs(modobj): 99 | obj.preamble(modobj, ss, objname) 100 | ss("\n") 101 | 102 | # Does anyone have any opaque types? 103 | if hasattr(modobj, "PYB11opaque"): 104 | for x in modobj.PYB11opaque: 105 | ss("PYBIND11_MAKE_OPAQUE(" + x.replace(",", " PYB11COMMA ") + ")\n") 106 | 107 | # Trampolines 108 | PYB11generateModuleTrampolines(modobj, ss) 109 | 110 | # Trampolines 111 | PYB11generateModulePublicists(modobj, ss) 112 | 113 | # Declare the module 114 | ss(""" 115 | //------------------------------------------------------------------------------ 116 | // Make the module 117 | //------------------------------------------------------------------------------ 118 | PYBIND11_MODULE(%(name)s, m) { 119 | 120 | """ % {"name" : name, 121 | }) 122 | 123 | doc = inspect.getdoc(modobj) 124 | if doc: 125 | ss(" m.doc() = ") 126 | PYB11docstring(doc, ss) 127 | ss(" ;\n") 128 | ss("\n") 129 | 130 | # Any module preamble? 131 | if hasattr(modobj, "PYB11modulepreamble"): 132 | ss(modobj.PYB11modulepreamble + "\n\n") 133 | 134 | # Are there any objects to import from other modules 135 | othermods = PYB11othermods(modobj) 136 | for (kname, klass) in PYB11classes(modobj): 137 | klassattrs = PYB11attrs(klass) 138 | mods = klassattrs["module"] 139 | for otherklass in mods: 140 | othermod = mods[otherklass] 141 | if othermod not in othermods: 142 | othermods.append(othermod) 143 | if othermods: 144 | ss(" // Import external modules\n") 145 | for othermod in othermods: 146 | if othermod != name: 147 | ss(' py::module::import("%s");\n' % othermod) 148 | ss("\n") 149 | 150 | return 151 | 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PYB11Generator 2 | ============== 3 | 4 | PYB11Generator is a python based code generator that creates [pybind11](https://github.com/pybind/pybind11) code for binding C++ libraries as extensions in Python. PYB11Generator parses input that is very close to writing the desired interface in native python, turning this into the corresponding pybind11 C++ code. 5 | 6 | Note, since PYB11Generator blindly generates C++ pybind11 code, it is essential to understand the pybind11 package itself as well! In other words, be sure to read and understand the [pybind11 documentation](https://pybind11.readthedocs.io/en/stable/) before trying to go too far with PYB11Generator. The purpose of PYB11Generator is to reduce the burden of writing and maintaining redundant code when working with pybind11 (such as the [trampoline classes](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python)), and provide a natural syntax for those already familiar with writing interfaces in Python. However, since the generated pybind11 code produced by PYB11Generator is what is actually compiled by a C++ compiler to create the corresponding python package, any errors reported by the compiler will refer to this generated code, and require understanding pybind11 itself to properly interpret. 7 | 8 | Documentation 9 | ------------- 10 | 11 | PYB11Generator is documented at [readthedocs](https://pyb11generator.readthedocs.io/en/latest/). 12 | 13 | Note the source for this documentation is embedded in the PYB11Generator repository under docs/. 14 | 15 | Contributions 16 | ------------- 17 | 18 | Although a great deal of the functionality of pybind11 is available via PYB11Generator, there are certainly missing pieces and improvements that can be made. Contributions are welcome, and should be provided as pull requests to the main repository. Note all contributions must be provided under the same license for distribution (in this case the BSD license). 19 | 20 | License 21 | ------- 22 | 23 | PYB11Generator is released under the [BSD license](https://github.com/jmikeowen/PYB11Generator/blob/master/LICENSE). 24 | 25 | LLNL-CODE-767799 26 | -------------------------------------------------------------------------------- /cmake/FindSphinx.cmake: -------------------------------------------------------------------------------- 1 | #if (NOT DEFINED SPHINX_EXECUTABLE) 2 | 3 | # find_program(SPHINX_EXECUTABLE 4 | # NAMES sphinx-build sphinx-build2 5 | # HINTS 6 | # $ENV{SPHINX_DIR} 7 | # PATH_SUFFIXES bin 8 | # DOC "Sphinx documentation generator" 9 | # ) 10 | 11 | 12 | # # Handle REQUIRED and QUIET arguments 13 | # # this will also set SPHINX_FOUND to true if SPHINX_EXECUTABLE exists 14 | # include(FindPackageHandleStandardArgs) 15 | # find_package_handle_standard_args(Sphinx 16 | # "Failed to locate sphinx-build executable" 17 | # SPHINX_EXECUTABLE) 18 | 19 | # endif() 20 | 21 | # # Provide options for controlling Sphinx 22 | # option(SPHINX_OUTPUT_HTML "Output standalone HTML files" ON) 23 | # option(SPHINX_OUTPUT_MAN "Output man pages" OFF) 24 | # option(SPHINX_WARNINGS_AS_ERRORS "When building documentation treat warnings as errors" OFF) 25 | 26 | # message("-- Sphinx executable: ${SPHINX_EXECUTABLE}") 27 | # mark_as_advanced(SPHINX_EXECUTABLE) 28 | ############################################################################## 29 | # @file FindSphinx.cmake 30 | # @brief Find Sphinx documentation build tools. 31 | # 32 | # @par Input variables: 33 | # 34 | # 35 | # @tp @b Sphinx_DIR @endtp 36 | # 37 | # 38 | # 39 | # @tp @b SPHINX_DIR @endtp 40 | # 41 | # 42 | # 43 | # @tp @b Sphinx_FIND_COMPONENTS @endtp 44 | # 45 | # 46 | #
Installation directory of Sphinx tools. Can also be set as environment variable.
Alternative environment variable for @c Sphinx_DIR.
Sphinx build tools to look for, i.e., 'apidoc' and/or 'build'.
47 | # 48 | # @par Output variables: 49 | # 50 | # 51 | # @tp @b Sphinx_FOUND @endtp 52 | # 53 | # 54 | # 55 | # @tp @b SPHINX_FOUND @endtp 56 | # 58 | # 59 | # @tp @b SPHINX_EXECUTABLE @endtp 60 | # 61 | # 62 | # 63 | # @tp @b Sphinx_PYTHON_EXECUTABLE @endtp 64 | # 67 | # 68 | # 69 | # @tp @b Sphinx_PYTHON_OPTIONS @endtp 70 | # 74 | # 75 | # 76 | # @tp @b Sphinx-build_EXECUTABLE @endtp 77 | # 78 | # 79 | # 80 | # @tp @b Sphinx-apidoc_EXECUTABLE @endtp 81 | # 82 | # 83 | # 84 | # @tp @b Sphinx_VERSION_STRING @endtp 85 | # 86 | # 87 | # 88 | # @tp @b Sphinx_VERSION_MAJOR @endtp 89 | # 90 | # 91 | # 92 | # @tp @b Sphinx_VERSION_MINOR @endtp 93 | # 94 | # 95 | # 96 | # @tp @b Sphinx_VERSION_PATCH @endtp 97 | # 98 | # 99 | #
Whether all or only the requested Sphinx build tools were found.
Alias for @c Sphinx_FOUND. 57 | #
Non-cached alias for @c Sphinx-build_EXECUTABLE.
Python executable used to run sphinx-build. This is either the 65 | # by default found Python interpreter or a specific version as 66 | # specified by the shebang (#!) of the sphinx-build script.
A list of Python options extracted from the shebang (#!) of the 71 | # sphinx-build script. The -E option is added by this module 72 | # if the Python executable is not the system default to avoid 73 | # problems with a differing setting of the @c PYTHONHOME.
Absolute path of the found sphinx-build tool.
Absolute path of the found sphinx-apidoc tool.
Sphinx version found e.g. 1.1.2.
Sphinx major version found e.g. 1.
Sphinx minor version found e.g. 1.
Sphinx patch version found e.g. 2.
100 | # 101 | # @ingroup CMakeFindModules 102 | ############################################################################## 103 | 104 | #============================================================================= 105 | # Copyright 2011-2012 University of Pennsylvania 106 | # Copyright 2013-2016 Andreas Schuh 107 | # 108 | # Distributed under the OSI-approved BSD License (the "License"); 109 | # see accompanying file Copyright.txt for details. 110 | # 111 | # This software is distributed WITHOUT ANY WARRANTY; without even the 112 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 113 | # See the License for more information. 114 | #============================================================================= 115 | # (To distribute this file outside of CMake, substitute the full 116 | # License text for the above reference.) 117 | 118 | set (_Sphinx_REQUIRED_VARS) 119 | 120 | # ---------------------------------------------------------------------------- 121 | # initialize search 122 | if (NOT Sphinx_DIR) 123 | if (NOT $ENV{Sphinx_DIR} STREQUAL "") 124 | set (Sphinx_DIR "$ENV{Sphinx_DIR}") 125 | else () 126 | set (Sphinx_DIR "$ENV{SPHINX_DIR}") 127 | endif () 128 | endif () 129 | 130 | # ---------------------------------------------------------------------------- 131 | # default components to look for 132 | if (NOT Sphinx_FIND_COMPONENTS) 133 | set (Sphinx_FIND_COMPONENTS "build" "apidoc") 134 | elseif (NOT Sphinx_FIND_COMPONENTS MATCHES "^(build|apidoc)$") 135 | message (FATAL_ERROR "Invalid Sphinx component in: ${Sphinx_FIND_COMPONENTS}") 136 | endif () 137 | 138 | # ---------------------------------------------------------------------------- 139 | # find components, i.e., build tools 140 | foreach (_Sphinx_TOOL IN LISTS Sphinx_FIND_COMPONENTS) 141 | if (Sphinx_DIR) 142 | find_program ( 143 | Sphinx-${_Sphinx_TOOL}_EXECUTABLE 144 | NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py 145 | HINTS "${Sphinx_DIR}" 146 | PATH_SUFFIXES bin 147 | DOC "The sphinx-${_Sphinx_TOOL} Python script." 148 | NO_DEFAULT_PATH 149 | ) 150 | else () 151 | find_program ( 152 | Sphinx-${_Sphinx_TOOL}_EXECUTABLE 153 | NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py 154 | DOC "The sphinx-${_Sphinx_TOOL} Python script." 155 | ) 156 | endif () 157 | mark_as_advanced (Sphinx-${_Sphinx_TOOL}_EXECUTABLE) 158 | list (APPEND _Sphinx_REQUIRED_VARS Sphinx-${_Sphinx_TOOL}_EXECUTABLE) 159 | endforeach () 160 | 161 | # set main Sphinx_EXECUTABLE so basis_find_package can derive DEPENDS_Sphinx_DIR 162 | if (Sphinx-build_EXECUTABLE) 163 | set (Sphinx_EXECUTABLE ${Sphinx-build_EXECUTABLE}) 164 | else () 165 | set (Sphinx_EXECUTABLE ${Sphinx-apidoc_EXECUTABLE}) 166 | endif () 167 | 168 | # ---------------------------------------------------------------------------- 169 | # determine Python executable used by Sphinx 170 | if (Sphinx-build_EXECUTABLE) 171 | # extract python executable from shebang of sphinx-build 172 | find_package (PythonInterp QUIET) 173 | set (Sphinx_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}") 174 | set (Sphinx_PYTHON_OPTIONS) 175 | file (STRINGS "${Sphinx-build_EXECUTABLE}" FIRST_LINE LIMIT_COUNT 1) 176 | if (FIRST_LINE MATCHES "^#!(.*/python.*)") # does not match "#!/usr/bin/env python" ! 177 | string (REGEX REPLACE "^ +| +$" "" Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}") 178 | if (Sphinx_PYTHON_EXECUTABLE MATCHES "([^ ]+) (.*)") 179 | set (Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}") 180 | string (REGEX REPLACE " +" ";" Sphinx_PYTHON_OPTIONS "${CMAKE_MATCH_2}") 181 | endif () 182 | endif () 183 | # this is done to avoid problems with multiple Python versions being installed 184 | # remember: CMake command if(STR EQUAL STR) is bad and may cause many troubles ! 185 | string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" _Sphinx_PYTHON_EXECUTABLE_RE "${PYTHON_EXECUTABLE}") 186 | list (FIND Sphinx_PYTHON_OPTIONS -E IDX) 187 | if (IDX EQUAL -1 AND NOT Sphinx_PYTHON_EXECUTABLE MATCHES "^${_Sphinx_PYTHON_EXECUTABLE_RE}$") 188 | list (INSERT Sphinx_PYTHON_OPTIONS 0 -E) 189 | endif () 190 | unset (_Sphinx_PYTHON_EXECUTABLE_RE) 191 | endif () 192 | 193 | # ---------------------------------------------------------------------------- 194 | # determine Sphinx version 195 | if (Sphinx-build_EXECUTABLE) 196 | # intentionally use invalid -h option here as the help that is shown then 197 | # will include the Sphinx version information 198 | if (Sphinx_PYTHON_EXECUTABLE) 199 | execute_process ( 200 | COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" -h 201 | OUTPUT_VARIABLE _Sphinx_VERSION 202 | ERROR_VARIABLE _Sphinx_VERSION 203 | ) 204 | elseif (UNIX) 205 | execute_process ( 206 | COMMAND "${Sphinx-build_EXECUTABLE}" -h 207 | OUTPUT_VARIABLE _Sphinx_VERSION 208 | ERROR_VARIABLE _Sphinx_VERSION 209 | ) 210 | endif () 211 | if (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+\\.[0-9]+)") 212 | set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") 213 | string (REPLACE "." ";" _Sphinx_VERSION "${Sphinx_VERSION_STRING}") 214 | list(GET _Sphinx_VERSION 0 Sphinx_VERSION_MAJOR) 215 | list(GET _Sphinx_VERSION 1 Sphinx_VERSION_MINOR) 216 | list(GET _Sphinx_VERSION 2 Sphinx_VERSION_PATCH) 217 | if (Sphinx_VERSION_PATCH EQUAL 0) 218 | string (REGEX REPLACE "\\.0$" "" Sphinx_VERSION_STRING "${Sphinx_VERSION_STRING}") 219 | endif () 220 | endif() 221 | endif () 222 | 223 | # ---------------------------------------------------------------------------- 224 | # compatibility with FindPythonInterp.cmake and FindPerl.cmake 225 | set (SPHINX_EXECUTABLE "${Sphinx-build_EXECUTABLE}") 226 | 227 | # ---------------------------------------------------------------------------- 228 | # handle the QUIETLY and REQUIRED arguments and set SPHINX_FOUND to TRUE if 229 | # all listed variables are TRUE 230 | include (FindPackageHandleStandardArgs) 231 | FIND_PACKAGE_HANDLE_STANDARD_ARGS ( 232 | Sphinx 233 | REQUIRED_VARS 234 | ${_Sphinx_REQUIRED_VARS} 235 | VERSION_VAR 236 | Sphinx_VERSION_STRING 237 | ) 238 | 239 | unset (_Sphinx_VERSION) 240 | unset (_Sphinx_REQUIRED_VARS) 241 | -------------------------------------------------------------------------------- /cmake/PYB11Generator.cmake: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------------- 2 | # PYB11Generator_add_module 3 | # 4 | # Front-end CMake function for use building PYB11Generator Python modules. Takes a 5 | # single argument (package_name), which is the name of the Python module to be 6 | # generated. The associated PYB11Generator code should be in the file 7 | # _PYB11.py 8 | # That file may in turn import as many other Python sources for PYB11Generator as 9 | # desired, so it is not necessary to cram all the PYB11Generator bindings into 10 | # _PYB11.py. 11 | # 12 | # Important CMake variables: 13 | # PYB11GENERATOR_ROOT_DIR : (required) 14 | # - Top-level director for PYB11Generator installation 15 | # PYBIND11_ROOT_DIR : (optional) 16 | # - Location of the pybind11 install 17 | # - defaults to ${PYB11GENERATOR_ROOT_DIR}/extern/pybind11 18 | # PYTHON_EXE : (optional) 19 | # - Python executable 20 | # - if not set, we use CMake's find_package to search for Python3 21 | # 22 | # Usage: 23 | # PYB11Generator_add_module( 24 | # MODULE ... 25 | # SOURCE ... 26 | # INSTALL ... 27 | # INCLUDES ... 28 | # LINKS ... 29 | # DEPENDS ... 30 | # PYBIND11_OPTIONS ... 31 | # COMPILE_OPTIONS ... 32 | # USE_BLT ON/OFF) 33 | # where arguments are: 34 | # (required) 35 | # The base name of the Python module being generated. Results in a module 36 | # which can be imported in Python as "import ". 37 | # MODULE ... (optional) 38 | # default: 39 | # Specify the name of the Python module to be imported and bound 40 | # Also used as the corresponding CMake target name 41 | # SOURCE ... (optional) 42 | # default: _PYB11.py 43 | # Specify the name of the Python file holding the PYB11Generator description 44 | # of the bindings. 45 | # EXTRA_SOURCE ... (optional) 46 | # Any additional source files we want to compile into the library 47 | # INSTALL ... (optional) 48 | # default: ${Python3_SITEARCH}/${package_name} 49 | # Path to install the final Python module to 50 | # INCLUDES ... (optional) 51 | # Any necessary C++ include paths for the compilation of the generated C++ code 52 | # LINKS ... (optional) 53 | # Any link flags or libraries necessary to link the compiled Python module 54 | # DEPENDS ... (optional) 55 | # Any CMake targets that need to be built/satisfied before this module is 56 | # built. 57 | # PYBIND11_OPTIONS ... (optional) 58 | # Any flags that should be bassed to the pybind11 CMake function 59 | # pybind11_add_module. See documentation at 60 | # https://pybind11.readthedocs.io/en/stable/compiling.html#pybind11-add-module 61 | # COMPILE_OPTIONS ... (optional) 62 | # Any additional flags that should be passed during the compile stage. See 63 | # CMake documentation for TARGET_COMPILE_OPTIONS. 64 | # USE_BLT ON/OFF (optional, default OFF) 65 | # For those using the BLT Cmake extension (https://llnl-blt.readthedocs.io/), 66 | # which does not play well with standard CMake add_library options. 67 | # Note, using this option skips using pybind11's own add_module CMake logic, 68 | # and therefore may make some pybind11 options no-ops. 69 | # VIRTUAL_ENV ... (optional) 70 | # The name of a python virtual environment target. The target must supply 71 | # target properties EXECUTABLE and ACTIVATE_VENV to define the python executable 72 | # and the command to activate the environment respectively. 73 | # 74 | # This is the function users should call directly. The macro PYB11_GENERATE_BINDINGS 75 | # defined next is primarily for internal use. 76 | # 77 | # Based on Mike Davis' original Cmake scripts for handling PYB11Generator extensions 78 | # in Spheral. Also uses the pybind11 add_pybind11_module function for most of the 79 | # compilation work. 80 | #----------------------------------------------------------------------------------- 81 | 82 | # Need Python components and pybind11 83 | if (NOT DEFINED PYTHON_EXE) 84 | find_package(Python3 COMPONENTS Interpreter Development) 85 | set(PYTHON_EXE ${Python3_EXECUTABLE}) 86 | endif() 87 | if (NOT TARGET pybind11_headers) 88 | if (DEFINED PYBIND11_ROOT_DIR) 89 | add_subdirectory(${PYBIND11_ROOT_DIR} ${CMAKE_CURRENT_BINARY_DIR}/external) 90 | else() 91 | add_subdirectory(${PYB11GENERATOR_ROOT_DIR}/extern/pybind11 ${CMAKE_CURRENT_BINARY_DIR}/external) 92 | endif() 93 | endif() 94 | 95 | function(PYB11Generator_add_module package_name) 96 | 97 | # Define our arguments 98 | set(options ) 99 | set(oneValueArgs MODULE SOURCE INSTALL USE_BLT VIRTUAL_ENV) 100 | set(multiValueArgs INCLUDES LINKS DEPENDS PYBIND11_OPTIONS COMPILE_OPTIONS EXTRA_SOURCE) 101 | cmake_parse_arguments(${package_name} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 102 | # message("-- MODULE: ${${package_name}_MODULE}") 103 | # message("-- SOURCE: ${${package_name}_SOURCE}") 104 | # message("-- INSTALL: ${${package_name}_INSTALL}") 105 | # message("-- INCLUDES: ${${package_name}_INCLUDES}") 106 | # message("-- LINKS: ${${package_name}_LINKS}") 107 | # message("-- DEPENDS: ${${package_name}_DEPENDS}") 108 | # message("-- PYBIND11_OPTIONS: ${${package_name}_PYBIND11_OPTIONS}") 109 | # message("-- COMPILE_OPTIONS: ${${package_name}_COMPILE_OPTIONS}") 110 | # message("-- USE_BLT: ${package_name}_USE_BLT") 111 | # message("-- EXTRA_SOURCE: ${package_name}_EXTRA_SOURCE") 112 | # message("-- VIRTUAL_ENV: ${${package_name}_VIRTUAL_ENV}") 113 | 114 | # Set our names and paths 115 | if (NOT DEFINED ${package_name}_MODULE) 116 | set(${package_name}_MODULE ${package_name}) 117 | endif() 118 | if (NOT DEFINED ${package_name}_SOURCE) 119 | set(${package_name}_SOURCE "${${package_name}_MODULE}_PYB11.py") 120 | endif() 121 | # message("-- ${package_name}_MODULE: ${${package_name}_MODULE}") 122 | # message("-- ${package_name}_SOURCE: ${${package_name}_SOURCE}") 123 | 124 | # Generate the pybind11 C++ source file 125 | PYB11_GENERATE_BINDINGS(${package_name} ${${package_name}_MODULE} ${${package_name}_SOURCE} 126 | DEPENDS ${${package_name}_DEPENDS} 127 | VIRTUAL_ENV ${${package_name}_VIRTUAL_ENV}) 128 | 129 | if (${${package_name}_USE_BLT}) 130 | # Build using BLT macros -- assumes you've already included BLT CMake rules 131 | blt_add_library(NAME ${${package_name}_MODULE} 132 | SOURCES ${${package_name}_MODULE}.cc ${${package_name}_SOURCE} ${${package_name}_EXTRA_SOURCE} 133 | DEPENDS_ON ${${package_name}_DEPENDS} 134 | INCLUDES ${${package_name}_INCLUDES} 135 | OUTPUT_NAME ${${package_name}_MODULE} 136 | CLEAR_PREFIX TRUE 137 | SHARED TRUE) 138 | else() 139 | # Build using the normal pybind11 rules 140 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${${package_name}_INCLUDES}) 141 | pybind11_add_module(${package_name} ${${package_name}_PYBIND11_OPTIONS} ${${package_name}_MODULE}.cc ${${package_name}_EXTRA_SOURCE}) 142 | set_target_properties(${${package_name}_MODULE} PROPERTIES SUFFIX ".so" LIBRARY_OUTPUT_NAME ${${package_name}_MODULE}) 143 | target_link_libraries(${${package_name}_MODULE} PRIVATE ${${package_name}_LINKS}) 144 | endif() 145 | 146 | add_dependencies(${${package_name}_MODULE} ${${package_name}_MODULE}_src) 147 | target_compile_options(${${package_name}_MODULE} PRIVATE ${${package_name}_COMPILE_OPTIONS}) 148 | 149 | # Installation 150 | if (NOT ${${package_name}_INSTALL} STREQUAL "OFF") 151 | if ("${${package_name}_INSTALL} " STREQUAL " ") 152 | set(${package_name}_INSTALL ${Python3_SITEARCH}/${package_name}) 153 | endif() 154 | install(TARGETS ${${package_name}_MODULE} DESTINATION ${${package_name}_INSTALL}) 155 | endif() 156 | 157 | endfunction() 158 | 159 | #----------------------------------------------------------------------------------- 160 | # PYB11_GENERATE_BINDINGS 161 | # - Generates the Python bindings for each module in the list 162 | # - Generates python stamp files for listing python dependency file to help 163 | # detecting changes in the pyb11 python files at build time 164 | # 165 | # Usage: 166 | # PYB11_GENERATE_BINDINGS( 167 | # DEPENDS ... 168 | # PYTHONPATH ...) 169 | # where the arguments are: 170 | # (required) 171 | # The CMake target name 172 | # (required) 173 | # The base name for the Python module 174 | # (required) 175 | # Source file containing the PYB11Generator bindings description 176 | # DEPENDS ... (optional) 177 | # Any CMake targets this package should depend on being built first 178 | # PYTHONPATH ... (optional) 179 | # Additions needed for the environment PYTHONPATH 180 | # 181 | # To get the names of the generated source 182 | # use: ${PYB11_GENERATED_SOURCE} 183 | #----------------------------------------------------------------------------------- 184 | 185 | macro(PYB11_GENERATE_BINDINGS package_name module_name PYB11_SOURCE) 186 | set(PYB11_GENERATED_SOURCE "${module_name}.cc") 187 | 188 | # Define our arguments 189 | set(options ) 190 | set(oneValueArgs VIRTUAL_ENV) 191 | set(multiValueArgs DEPENDS PYTHONPATH) 192 | cmake_parse_arguments(${package_name} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 193 | # message("** package_name: ${package_name}") 194 | # message("** module_name: ${module_name}") 195 | # message("** PYB11_SOURCE: ${PYB11_SOURCE}") 196 | # message("** DEPENDS: ${${package_name}_DEPENDS}") 197 | # message("** PYTHONPATH: ${${package_name}_PYTHONPATH}") 198 | 199 | # Places we need in the Python path 200 | set(PYTHON_ENV "${CMAKE_CURRENT_BINARY_DIR}:${CMAKE_CURRENT_SOURCE_DIR}:${PYB11GENERATOR_ROOT_DIR}:${${package_name}_PYTHONPATH}") 201 | if (DEFINED ENV{PYTHONPATH}) 202 | set(PYTHON_ENV "${PYTHON_ENV}:$ENV{PYTHONPATH}") 203 | endif() 204 | 205 | # Extract the name of PYB11 generating source code without the .py extension 206 | string(REGEX REPLACE "\\.[^.]*$" "" pyb11_module ${PYB11_SOURCE}) 207 | 208 | set(PYTHON_EXE_BAK ${PYTHON_EXE}) 209 | 210 | if (DEFINED ${package_name}_VIRTUAL_ENV) 211 | get_target_property(ACTIVATE_VENV_CMD ${${package_name}_VIRTUAL_ENV} ACTIVATE_VENV) 212 | list(APPEND ACTIVATE_VENV_CMD &&) 213 | get_target_property(PYTHON_EXE ${${package_name}_VIRTUAL_ENV} EXECUTABLE) 214 | endif() 215 | 216 | # Always generate cpp files at build time. Any change in the cpp file 217 | # will trigger a rebuild of the target pyb11 module 218 | add_custom_target( 219 | ${module_name}_src ALL 220 | COMMAND ${ACTIVATE_VENV_CMD} env PYTHONPATH="${PYTHON_ENV}" ${PYTHON_EXE} ${PYB11GENERATOR_ROOT_DIR}/cmake/generate_cpp.py ${pyb11_module} ${module_name} 221 | BYPRODUCTS ${PYB11_GENERATED_SOURCE} 222 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 223 | DEPENDS ${${package_name}_VIRTUAL_ENV} 224 | ) 225 | 226 | set(PYTHON_EXE ${PYTHON_EXE_BAK}) 227 | 228 | endmacro() 229 | -------------------------------------------------------------------------------- /cmake/add_sphinx_target.cmake: -------------------------------------------------------------------------------- 1 | ##------------------------------------------------------------------------------ 2 | ## add_sphinx_target(sphinx_target_name) 3 | ## 4 | ## Creates a build target for invoking sphinx to generate docs 5 | ## 6 | ## Lifted from the BLT implementation 7 | ##------------------------------------------------------------------------------ 8 | macro(add_sphinx_target sphinx_target_name ) 9 | 10 | # configured documentation tools and intermediate build results 11 | set(SPHINX_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build") 12 | 13 | # Sphinx cache with pickled ReST documents 14 | set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees") 15 | 16 | # HTML output directory 17 | set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html") 18 | 19 | # support both direct use of a conf.py file and a cmake-configured 20 | # sphinx input file (conf.py.in). The cmake-configured input file is 21 | # preferred when both exist. 22 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in") 23 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" 24 | "${SPHINX_BUILD_DIR}/conf.py" 25 | @ONLY) 26 | 27 | add_custom_target(${sphinx_target_name} 28 | ${SPHINX_EXECUTABLE} 29 | -q -b html 30 | #-W disable warn on error for now, while our sphinx env is still in flux 31 | -c "${SPHINX_BUILD_DIR}" 32 | -d "${SPHINX_CACHE_DIR}" 33 | "${CMAKE_CURRENT_SOURCE_DIR}" 34 | "${SPHINX_HTML_DIR}" 35 | COMMENT "Building HTML documentation with Sphinx for ${sphinx_target_name} target" 36 | DEPENDS ${deps}) 37 | elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/conf.py") 38 | add_custom_target(${sphinx_target_name} 39 | ${SPHINX_EXECUTABLE} 40 | -q -b html 41 | #-W disable warn on error for now, while our sphinx env is still in flux 42 | -d "${SPHINX_CACHE_DIR}" 43 | "${CMAKE_CURRENT_SOURCE_DIR}" 44 | "${SPHINX_HTML_DIR}" 45 | COMMENT "Building HTML documentation with Sphinx for ${sphinx_target_name} target" 46 | DEPENDS ${deps}) 47 | else() 48 | message(FATAL_ERROR "Failed to find sphinx 'conf.py' or 'conf.py.in' in ${CMAKE_CURRENT_SOURCE_DIR}") 49 | endif() 50 | 51 | # # hook our new target into the docs dependency chain 52 | # add_dependencies(sphinx_docs ${sphinx_target_name}) 53 | 54 | ###### 55 | # This snippet makes sure if we do a make install w/o the optional "docs" 56 | # target built, it will be built during the install process. 57 | ###### 58 | 59 | install(CODE "execute_process(COMMAND ${CMAKE_BUILD_TOOL} ${sphinx_target_name} WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") 60 | 61 | install(DIRECTORY "${SPHINX_HTML_DIR}" 62 | DESTINATION "docs/sphinx/${sphinx_target_name}" OPTIONAL) 63 | 64 | endmacro(add_sphinx_target) 65 | -------------------------------------------------------------------------------- /cmake/generate_cpp.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import filecmp 4 | 5 | pyb11_mod_name = sys.argv[1] 6 | mod_name = sys.argv[2] 7 | 8 | current_src = mod_name + ".cc" 9 | new_src = mod_name + "_tmp.cc" 10 | 11 | # Generate the source anew 12 | code = """ 13 | from PYB11Generator import * 14 | import {pyb11_module} 15 | PYB11generateModule({pyb11_module}, 16 | modname = \"{mod_name}\", 17 | filename = \"{new_src}\") 18 | """.format(pyb11_module = pyb11_mod_name, 19 | mod_name = mod_name, 20 | new_src = new_src) 21 | exec(code) 22 | assert os.path.isfile(new_src) 23 | 24 | # If the module source is changed, update it. Otherwise 25 | # get rid of the temporary file and we're done. 26 | if os.path.isfile(current_src): 27 | if filecmp.cmp(current_src, new_src): 28 | os.remove(new_src) 29 | else: 30 | os.remove(current_src) 31 | os.rename(new_src, current_src) 32 | else: 33 | os.rename(new_src, current_src) 34 | assert os.path.isfile(current_src) 35 | assert not os.path.isfile(new_src) 36 | -------------------------------------------------------------------------------- /cmake/moduleCheck.py: -------------------------------------------------------------------------------- 1 | # Find all the non-system python dependencies of a given python file 2 | # Based on the original version developed in Spheral. 3 | from modulefinder import ModuleFinder 4 | import sys, os 5 | import filecmp 6 | 7 | mod_name = sys.argv[1] 8 | mod_file = sys.argv[2] 9 | 10 | finder = ModuleFinder() 11 | finder.run_script(mod_file) 12 | #print("Warning failed imports: ", finder.badmodules.keys()) 13 | 14 | current_stamp_name = mod_name + "_stamp.cmake" 15 | tmp_stamp_name = current_stamp_name + ".tmp" 16 | 17 | with open(tmp_stamp_name, "w") as newF: 18 | newF.write("set("+mod_name+"_DEPENDS \n") 19 | 20 | for name, mod in finder.modules.items(): 21 | if (mod.__file__): 22 | if not ("lib/python3" in mod.__file__): 23 | newF.write(mod.__file__) 24 | newF.write('\n') 25 | 26 | newF.write(")\n") 27 | 28 | if (os.path.isfile(current_stamp_name)): 29 | if (not filecmp.cmp(current_stamp_name, tmp_stamp_name)): 30 | os.rename(tmp_stamp_name, current_stamp_name) 31 | else: 32 | os.remove(tmp_stamp_name) 33 | else: 34 | os.rename(tmp_stamp_name, current_stamp_name) 35 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | _templates 4 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_MODULE_PATH}/add_sphinx_target.cmake) 2 | 3 | if(NOT DEFINED SPHINX_THEME) 4 | set(SPHINX_THEME sphinx_rtd_theme) 5 | endif() 6 | 7 | if(NOT DEFINED SPHINX_THEME_DIR) 8 | set(SPHINX_THEME_DIR) 9 | endif() 10 | 11 | add_sphinx_target(docs) 12 | 13 | # # configured documentation tools and intermediate build results 14 | # set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build") 15 | 16 | # # Sphinx cache with pickled ReST documents 17 | # set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees") 18 | 19 | # # HTML output directory 20 | # set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html") 21 | 22 | # add_custom_target(docs ALL 23 | # ${PYTHON_EXE} ${SPHINX_EXECUTABLE} 24 | # -q -b html 25 | # -c "${BINARY_BUILD_DIR}" 26 | # -d "${SPHINX_CACHE_DIR}" 27 | # "${CMAKE_CURRENT_SOURCE_DIR}" 28 | # "${SPHINX_HTML_DIR}" 29 | # DEPENDS ${DOC_DEPENDS} 30 | # COMMENT "Building HTML documentation with Sphinx") 31 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/PYB11decorators.rst: -------------------------------------------------------------------------------- 1 | .. _decorators: 2 | 3 | ================ 4 | PYB11 decorators 5 | ================ 6 | 7 | .. ############################################################################# 8 | .. decorator:: PYB11ignore 9 | 10 | Specifies that the decorated object should be ignored by PYB11Generator, i.e., not processed to produce any pybind11 binding output. 11 | 12 | .. ############################################################################# 13 | .. decorator:: PYB11template("type1", "type2", ...) 14 | 15 | Indicates the object should be treated as a C++ template. Accepts any number of strings which represent the names of the template arguments. 16 | 17 | The succeeding python class or function can use the specified template argument strings as patterns for substitation with python dictionary string replacement. So if we are binding a C++ templated function:: 18 | 19 | template 20 | T manipulate(const T& val); 21 | 22 | The corresponding PYB11 template specfication would look like:: 23 | 24 | @PYB11template("T") 25 | def manipulate(val = "const %(T)s"): 26 | return "%(T)s" 27 | 28 | .. ############################################################################# 29 | .. decorator:: PYB11template_dict 30 | 31 | Explicitly specifies the dictionary of template args to values for use with ``@PYB11template`` types. 32 | 33 | *NOTE: this is a highly unusual pattern to need/use. It is preferable to use the ordinary PYB11 template instantion methods* ``PYB11TemplateClass``, ``PYB11TemplateMethod``, *or* ``PYB11TemplateFunction``. 34 | 35 | .. ############################################################################# 36 | .. decorator:: PYB11singleton 37 | 38 | Specifies that the decorated object should be treated as a C++ singleton. 39 | 40 | .. ############################################################################# 41 | .. decorator:: PYB11holder(holder_type) 42 | 43 | Specify a special C++ holder for the generated type in ``pybind``, rather than the usual default ``std::unique_ptr``. See pybind11 documentation on using `shared_ptr as a holder type `_. 44 | 45 | .. ############################################################################# 46 | .. decorator:: PYB11dynamic_attr 47 | 48 | Make the wrapped class modifiable, i.e., allow attributes to be added dynamically to an instance of the class in python. See pybind11 documentation about `dynamic attributes `_. 49 | 50 | .. ############################################################################# 51 | .. decorator:: PYB11namespace("val") 52 | 53 | Set the namespace the C++ type should be found in. 54 | 55 | .. ############################################################################# 56 | .. decorator:: PYB11cppname("val") 57 | 58 | Give a value for the C++ name of the decorated function, class, or method. Overrides the default assumption that the C++ name is the same as that given for the object in the PYB11 python binding file. 59 | 60 | .. ############################################################################# 61 | .. decorator PYB11pyname("val") 62 | 63 | Give a value for the generated Python name of the decorated function, class, or method. Overrides the default assumption that the Python name is the same as that given for the object in the PYB11 python binding file. 64 | 65 | .. ############################################################################# 66 | .. decorator:: PYB11pycppname("val") 67 | 68 | Simultaneously set the Python and C++ name of the decorated function, class, or method. Shorthand for specifying both ``@PYB11pyname`` and ``@PYB11cppname`` to the given ``"val"``. 69 | 70 | .. ############################################################################# 71 | .. decorator:: PYB11virtual 72 | 73 | Mark a class method as virtual. 74 | 75 | .. ############################################################################# 76 | .. decorator:: PYB11pure_virtual 77 | 78 | Mark a class method as pure virtual, making the class abstract. 79 | 80 | .. ############################################################################# 81 | .. decorator:: PYB11protected 82 | 83 | Mark a class method as protected. 84 | 85 | .. ############################################################################# 86 | .. decorator:: PYB11const 87 | 88 | Mark a class method as const. 89 | 90 | .. ############################################################################# 91 | .. decorator:: PYB11static 92 | 93 | Mark a class method as static. 94 | 95 | .. ############################################################################# 96 | .. decorator:: PYB11operator 97 | 98 | Mark a class method as an operator. See `pybind11 discussion of py::is_operator in the discussion of operator overloading`_. 99 | 100 | .. ############################################################################# 101 | .. decorator:: PYB11noconvert 102 | 103 | Applies ``py::noconvert`` to all the arguments of a method to prevent automatic conversion. See `pybind11 discussion of py:: `_. 104 | 105 | .. ############################################################################# 106 | .. decorator:: PYB11implementation("val") 107 | 108 | Give an implementation for the bound function or method. This is typically used to specify lambda function implementations, or explicitly call a helper method. 109 | 110 | .. ############################################################################# 111 | .. _returnpolicy: 112 | .. decorator:: PYB11returnpolicy("val") 113 | 114 | Specify a pybind11 return policy for the return value of a function or method. This is a tricky topic that if misused can create memory errors, but is at times absolutely necessary to get the expected behavior from the underlying C++ code and types. Before using this method carefully read the pybind11 discussion about :ref:`pybind11:return_value_policies`. 115 | 116 | .. ############################################################################# 117 | .. decorator:: PYB11keepalive(a, b) 118 | 119 | Tie the lifetime of objects in the return value/argument spec together, where the arguments (``a``, ``b``) are integers indicating the order of the arguments to tie together (0 refers to the return value). This is another way of specifying memory policies, similar to returnpolicy_. Carefully read the pybind11 discussion of the ``keep_alive`` directive in :ref:`pybind11:call_policies`. 120 | 121 | .. ############################################################################# 122 | .. _call_guard: 123 | .. decorator:: PYB11call_guard("val") 124 | 125 | Specify a pybind11 call_guard for a function or method. See the discussion of `pybind11:call_policies` for examples of call_guards. 126 | 127 | .. ############################################################################# 128 | .. decorator:: PYB11module("val") 129 | 130 | Indicate the object should be imported from the specified python module. This is useful for classes wrapped in one module which are needed in another, such as for inheritance. 131 | -------------------------------------------------------------------------------- /docs/PYB11functions.rst: -------------------------------------------------------------------------------- 1 | .. _PYB11-functions: 2 | 3 | PYB11 special functions and classes 4 | =================================== 5 | 6 | This section describes the special functions and classes defined in PYB11Generator for use in createing python bindings. Note we use the convention that PYB11 internals always start with the ``PYB11`` prefix. 7 | 8 | .. ############################################################################# 9 | .. py:function:: PYB11generateModule(pymodule[, modname=None, filename=None]) 10 | 11 | Inspect the function and class definitions in ``pymodule``, and write a C++ file containing pybind11 statements to bind those interfaces. 12 | 13 | * ``pymodule``: the module to be introspected for the interface 14 | 15 | * ``modname``: optionally specify a different name for the generated Python module to be imported under. Defaults to ``pymodule``. 16 | 17 | * ``filename``: a file name for the generated C++ file. If specified, the output is written to the given name, otherwise output will be written to ``pymodule.cc`` 18 | 19 | .. ############################################################################# 20 | .. py:function:: PYB11TemplateFunction(func_template, template_parameters[, cppname = None, pyname = None, docext = ""]) 21 | 22 | Instantiate a function template (``func_template``) that was decorated by ``@PYB11template``. 23 | 24 | * ``func_template``: The template function definition 25 | 26 | * ``template_parameters``: A single string (for a single template parameter function) or tuple of strings (for multiple template parameters), one for each template parameter defined by ``@PYB11template`` on ``func_template``. 27 | 28 | * ``cppname``: The name of the C++ function template, if different from that used for ``func_template``. 29 | 30 | * ``pyname``: The name of the resulting Python function; defaults to the name of the instance created for this invocation of ``PYB11TemplateFunction``. 31 | 32 | * ``docext``: An optional string extension to be applied to the docstring associated with ``func_template``. 33 | 34 | .. ############################################################################# 35 | .. py:function:: PYB11attr([value=None, pyname=None]) 36 | 37 | Create an attribute in a module; corresponds to the pybind11 command ``attr``. 38 | 39 | * ``value``: define the C++ name this variable corresponds to. If ``None``, defaults to the name of the local python variable. 40 | 41 | * ``pyname``: define the generated python attribte name. If ``None``, defaults to the name of the local python variable. 42 | 43 | .. ############################################################################# 44 | .. py:function:: PYB11readwrite([static=False, pyname=None, cppname=None, returnpolicy=None, doc=None]) 45 | 46 | Define a readwrite class attribute; corresponds to pybind11 ``def_readwrite``. 47 | 48 | * ``static``: If ``True``, specifies the bound attribute is static. 49 | 50 | * ``pyname``: Optionally specify the Python name of the attribute. If ``None``, assumes the Python name is the name of Python variable instance. 51 | 52 | * ``cppname``: Optionally specify the C++ name of the attribute. If ``None``, assumes the C++ name is the name of Python variable instance. 53 | 54 | * ``returnpolicy``: Specify a special return policy for how to handle the memory of the return value. Read pybind11 documentation at :ref:`pybind11:return_value_policies`. 55 | 56 | * ``doc``: Optionally give a docstring. 57 | 58 | .. ############################################################################# 59 | .. py:function:: PYB11readonly([static=False, pyname=None, cppname=None, returnpolicy=None, doc=None]) 60 | 61 | Define a readonly class attribute; corresponds to pybind11 ``def_readonly``. 62 | 63 | * ``static``: If ``True``, specifies the bound attribute is static. 64 | 65 | * ``pyname``: Optionally specify the Python name of the attribute. If ``None``, assumes the Python name is the name of Python variable instance. 66 | 67 | * ``cppname``: Optionally specify the C++ name of the attribute. If ``None``, assumes the C++ name is the name of Python variable instance. 68 | 69 | * ``returnpolicy``: Specify a special return policy for how to handle the memory of the return value. Read pybind11 documentation at :ref:`pybind11:return_value_policies`. 70 | 71 | * ``doc``: Optionally give a docstring. 72 | 73 | .. ############################################################################# 74 | .. py:function:: PYB11property([returnType = None, getter = None, setter = None, doc = None, getterraw = None, setterraw = None, getterconst = True, setterconst = False, static = None, constexpr = False, returnpolicy = None]) 75 | 76 | Helper to setup a class property. 77 | 78 | * ``returnType``: Specify the C++ type of the property 79 | 80 | * ``getter``: A string with the name of the getter method. If ``None``, assumes the getter C++ specification looks like ``returnType (klass::*)() const``. 81 | 82 | * ``setter``: A string with the name of the setter method. If ``None``, assumes the setter C++ specification looks like ``void (klass::*)(returnType& val)``. 83 | 84 | * ``doc``: Specify a document string for the property. 85 | 86 | * ``getterraw``: Optionally specify raw coding for the getter method. Generally this is used to insert a C++ lambda function. Only one of ``getter`` or ``getterraw`` may be specified. 87 | 88 | * ``setterraw``: Optionally specify raw coding for the setter method. Generally this is used to insert a C++ lambda function. Only one of ``setter`` or ``setterraw`` may be specified. 89 | 90 | * ``getterconst``: Specify if ``getter`` is a const method. 91 | 92 | * ``setterconst``: Specify if ``setter`` is a const method. 93 | 94 | * ``static``: If ``True``, make this a static property. 95 | 96 | * ``constexpr``: Set to ``True`` if the property is a C++ constexpr expression. 97 | 98 | * ``returnpolicy``: Specify a special return policy for how to handle the memory of the return value. Read pybind11 documentation at :ref:`pybind11:return_value_policies`. 99 | 100 | .. ############################################################################# 101 | .. py:function:: PYB11TemplateMethod(func_template, template_parameters[, cppname = None, pyname = None, docext = ""]) 102 | 103 | Instantiate a class method (``func_template``) that was decorated by ``@PYB11template``. 104 | 105 | * ``func_template``: The template method definition 106 | 107 | * ``template_parameters``: A single string (for a single template parameter method) or tuple of strings (for multiple template parameters), one for each template parameter defined by ``@PYB11template`` on ``func_template``. 108 | 109 | * ``cppname``: The name of the C++ method template, if different from that used for ``func_template``. 110 | 111 | * ``pyname``: The name of the resulting Python method; defaults to the name of the instance created for this invocation of ``PYB11TemplateMethod``. 112 | 113 | * ``docext``: An optional string extension to be applied to the docstring associated with ``func_template``. 114 | 115 | .. ############################################################################# 116 | .. py:function:: PYB11TemplateClass(klass_template, template_parameters[, cppname = None, pyname = None, docext = ""]) 117 | 118 | Instantiate a class template (``klass_template``) that was decorated by ``@PYB11template``. 119 | 120 | * ``klass_template``: The template class definition 121 | 122 | * ``template_parameters``: A single string (for a single template parameter class) or tuple of strings (for multiple template parameters), one for each template parameter defined by ``@PYB11template`` on ``klass_template``. 123 | 124 | * ``cppname``: The name of the C++ class template, if different from that used for ``klass_template``. 125 | 126 | * ``pyname``: The name of the resulting Python class; defaults to the name of the instance created for this invocation of ``PYB11TemplateClass``. 127 | 128 | * ``docext``: An optional string extension to be applied to the docstring associated with ``klass_template``. 129 | 130 | .. ############################################################################# 131 | .. py:function:: PYB11enum(values[, name=None, namespace="", cppname=None, export_values=False, doc=None]) 132 | 133 | Declare a C++ enum for wrapping in pybind11 -- see `pybind11 docs `_. 134 | 135 | * ``values``: a tuple of strings listing the possible values for the enum 136 | 137 | * name: set the name of enum type in Python. ``None`` defaults to the name of the instance given this enum declaration instance. 138 | 139 | * namespace: an optional C++ namespace the enum lives in. 140 | 141 | * cppname: the C++ name of the enum. ``None`` defaults to the same as ``name``. 142 | 143 | * export_values: if ``True``, causes the enum values to be exported into the enclosing scope (like an old-style C enum). 144 | 145 | * doc: an optional document string. 146 | 147 | .. ############################################################################# 148 | .. py:function:: PYB11_bind_vector(element[, opaque=False, local=None]) 149 | 150 | Bind an STL::vector explicitly. This is essentially a thin wrapper around the pybind11 ``py::bind_vector`` function (see :ref:`pybind11:stl_bind`). 151 | 152 | * ``element``: the C++ element type of the ``std::vector`` 153 | 154 | * ``opaque``: if ``True``, causes the bound STL vector to be "opaque", so elements can be changed in place rather than accessed as copies. See :ref:`pybind11:stl_bind`. 155 | 156 | * ``local``: determines whether the binding of the STL vector should be module local or not; once again, see :ref:`pybind11:stl_bind`. 157 | 158 | .. ############################################################################# 159 | .. py:function:: PYB11_bind_map(key, value[, opaque=False, local=None]) 160 | 161 | Bind an STL::map explicitly. This is a thin wrapper around the pybind11 ``py::bind_map`` function (see :ref:`pybind11:stl_bind`). 162 | 163 | * ``key``: the C++ key type 164 | 165 | * ``value``: the C++ value type 166 | 167 | * ``opaque``: if ``True``, causes the bound STL map to be "opaque", so elements can be changed in place rather than accessed as copies. See :ref:`pybind11:stl_bind`. 168 | 169 | * ``local``: determines whether the binding of the STL map should be module local or not; once again, see :ref:`pybind11:stl_bind`. 170 | 171 | .. ############################################################################# 172 | .. py:function:: PYB11_inject(fromcls, tocls[, virtual=None, pure_virtual=None]) 173 | 174 | Convenience method to inject methods from class ``fromcls`` into ``tocls``. This is intended as a utility to help avoiding writing redundant methods common to many classes over and over again. Instead a convenience class can be defined containing the shared methods (typically screened from generation by ``@PYB11ignore``), and then ``PYB11_inject`` is used to copy those methods into the target classes. 175 | 176 | * ``fromcls``: Python class with methods we want to copy from. 177 | 178 | * ``tocls``: Python class we're copying methods to. 179 | 180 | * ``virtual``: if ``True``, force all methods we're copying to be treated as virtual. 181 | 182 | * ``pure_virtual``: if ``True``, force all methods we're copying to be treated as pure virtual. 183 | -------------------------------------------------------------------------------- /docs/PYB11variables.rst: -------------------------------------------------------------------------------- 1 | .. _variables: 2 | 3 | ======================== 4 | PYB11 reserved variables 5 | ======================== 6 | 7 | For the most part Python variables declared in module bindings are ignored by PYB11Generator. There are a few exceptions to this rule though -- some variables are used to communicate information to PYB11Generator as described below. 8 | 9 | PYB11includes = [...] 10 | A list of strings, each of which represents a file that should be ``#include``-ed in the final C++ generated file. For instance, if we needed the C++ to have the following include statements: 11 | 12 | .. code-block:: cpp 13 | 14 | #include "A.hh" 15 | #include 16 | 17 | We would put this in our file for PYB11Generator:: 18 | 19 | PYB11includes = ['"A.hh"', ''] 20 | 21 | PYB11namespaces = [...] 22 | A list of strings for C++ namespaces we wish to use in the generated C++ -- as an example, the following statement:: 23 | 24 | PYB11namespaces = ["extreme", "measures"] 25 | 26 | results in the following in the generated pybind11 C++ source: 27 | 28 | .. code-block:: cpp 29 | 30 | using namespace extreme; 31 | using namespace measures; 32 | 33 | PYB11scopenames = [...] 34 | A list of C++ types we want to directly declare with ``use`` (more focused than importing an entire namespace). In order to decare we want to use ``std::vector`` and ``MyNameSpace::A``, we could use:: 35 | 36 | PYB11scopenames = ["std::vector", "MyNameSpace::A"] 37 | 38 | which simply inserts the following in the generated C++: 39 | 40 | .. code-block:: cpp 41 | 42 | using std::vector 43 | using MyNameSpace::A 44 | 45 | PYB11preamble = "..." 46 | PYB11preamble is used to specify a string of arbitrary C++ code that will be inserted near the top of the generated pybind11 source file. ``PYB11preamble`` is a bit of catch-all, we could for instance directly perform the tasks of ``PYB11includes`` and ``PYB11namespace`` using ``PYB11preamble`` by simply typing the final C++ code in here. One typical usage of this preamble variable is to insert small inline utility methods directly in the final C++ code. For instance, if we had need of a simple function we want to use in the subsequent bindings, we could do something like:: 47 | 48 | PYB11preamble = """ 49 | namespace JustForBindings { 50 | inline int square(int x) { return x*x; } 51 | }""" 52 | 53 | and now our generated code will include this function. 54 | 55 | PYB11modulepreamble = "..." 56 | PYB11modulepreamble is used to specify a string of arbitrary C++ code that will be inserted following the ``PYBIND11_MODULE`` statement, so inside module scope. A typical use of this variable is to insert macros such as ``PYBIND11_NUMPY_DTYPE(...)```, for native support of user defined types with Numpy. 57 | 58 | PYB11opaque = [...] 59 | A list of C++ types we want to be treated as opaque: typically STL types declared as opaque and global in another module. See :ref:`stl` for further information. As an example, to declare that ``std::vector`` and ``std::vector`` are declared as opaque in another module:: 60 | 61 | PYB11opaque = ["std::vector", "std::vector"] 62 | 63 | which results in the following inserted into the generated C++: 64 | 65 | .. code-block:: cpp 66 | 67 | PYBIND11_MAKE_OPAQUE(std::vector) 68 | PYBIND11_MAKE_OPAQUE(std::vector) 69 | 70 | Note all of these reserved variables affect the start of the generated pybind11 C++ code, coming before any of the function, class, module, or other pybind11 declarations that are subsequently generated. The order that these methods are executed in is the same as they are listed above: first any ``PYB11includes``, then ``PYB11namespaces``, ``PYB11scopenames``, ``PYB11preamble``, and finally ``PYB11opaque``. If we were to include all of the above examples (in any order) in a single source code for instance like so:: 71 | 72 | PYB11includes = ['"A.hh"', ''] 73 | PYB11namespaces = ["extreme", "measures"] 74 | PYB11scopenames = ["std::vector", "MyNameSpace::A"] 75 | PYB11preamble = """ 76 | namespace JustForBindings { 77 | inline int square(int x) { return x*x; } 78 | } 79 | """ 80 | PYB11opaque = ["std::vector", "std::vector"] 81 | 82 | the generated pybind11 code would look like: 83 | 84 | .. code-block:: cpp 85 | 86 | ... 87 | #include "A.hh" 88 | #include 89 | 90 | using namespace extreme; 91 | using namespace measures; 92 | 93 | using std::vector 94 | using MyNameSpace::A 95 | 96 | 97 | namespace JustForBindings { 98 | inline int square(int x) { return x*x; } 99 | } 100 | 101 | 102 | PYBIND11_MAKE_OPAQUE(std::vector) 103 | PYBIND11_MAKE_OPAQUE(std::vector) 104 | 105 | //------------------------------------------------------------------------------ 106 | // Make the module 107 | //------------------------------------------------------------------------------ 108 | ... 109 | 110 | .. _class-variables: 111 | 112 | --------------------------- 113 | PYB11 variables for classes 114 | --------------------------- 115 | 116 | There is also one reserved variable for class scope in PYB11: 117 | 118 | PYB11typedefs = "...." 119 | A string of C++ to be inserted at the beginning of a class declaration. This is a bit of a misnomer; the string can be any valid C++ for use in the class declaration scope. Suppose for instance we are defining a class ``A``, and we want to declare some typedefs only for use in the scope of ``A``:: 120 | 121 | class A: 122 | 123 | PYB11typedefs = """ 124 | typedef int IntType; 125 | typedef double ScalarType; 126 | """ 127 | 128 | which results in the following C++ code: 129 | 130 | .. code-block:: cpp 131 | 132 | //............................................................................ 133 | // Class A 134 | { 135 | 136 | typedef int IntType; 137 | typedef double ScalarType; 138 | 139 | class_ obj(m, "A"); 140 | ... 141 | } 142 | 143 | so now ``IntType`` and ``ScalarType`` are avaiable as types in the scope where we are defining A. 144 | -------------------------------------------------------------------------------- /docs/cmake.rst: -------------------------------------------------------------------------------- 1 | .. _cmake 2 | 3 | =========================================== 4 | Using CMake to build PYB11Generator modules 5 | =========================================== 6 | 7 | pybind11 provides excellent support for building python modules using `CMake `_, as is documented `on pybind11 readthedocs page `_. PYB11Generator provides a thin wrapper around this capability to simplify generating PYB11Generator Python extensions as well, such that PYB11Generator modules can be compiled with just a few lines of CMake code. Suppose we have a simple function in a file ``example_functions.hh``:: 8 | 9 | int add(int i, int j) { 10 | return i + j; 11 | } 12 | 13 | int subtract(int i, int j) { 14 | return i - j; 15 | } 16 | 17 | and we write PYB11Generator bindings for these functions in the file ``example_PYB11.py``:: 18 | 19 | """ 20 | Simple example function performing sums in C++ 21 | """ 22 | 23 | PYB11includes = ['"example_functions.hh"'] 24 | 25 | def add(i = "int", j = "int"): 26 | "Add two integers" 27 | return "int" 28 | 29 | def subtract(i = "int", j = "int"): 30 | "Subtract two integers" 31 | return "int" 32 | 33 | If we have added PYB11Generator as a submodule of our project (say in the directory ``extern/PYB11Generator``), we can write a CMake ``CMakeLists.txt`` file to compile this example in just a few lines of code:: 34 | 35 | project(example LANGUAGES CXX) 36 | 37 | set(PYB11GENERATOR_ROOT_DIR ${CMAKE_SOURCE_DIR}/extern/PYB11Generator) # <-- Required to set PYB11GENERATOR_ROOT_DIR 38 | include(${PYB11GENERATOR_ROOT_DIR}/cmake/PYB11Generator.cmake) # <-- include the PYB11Generator CMake functions 39 | PYB11Generator_add_module(example) 40 | 41 | This will generate the build necessary to compile a Python extension ``example`` from the PYB11Generator source ``example_PYB11.py``. The CMake function ``PYB11Generator_add_module`` is the new CMake function which automatically calls PYB11Generator on the Python code stored in ``example_PYB11.py`` to generate the C++ pybind11 code, and then compiles and links that code to a functioning Python module. 42 | 43 | Some CMake variables that influence ``PYB11Generator_add_module`` are: 44 | 45 | PYB11GENERATOR_ROOT_DIR (required) : 46 | Top-level directory for PYB11Generator installation 47 | 48 | PYBDIND11_ROOT_DIR (optional) : 49 | Location of the pybind11 install. Defaults to ``${PYB11GENERATOR_ROOT_DIR}/extern/pybind11``. 50 | 51 | PYTHON_EXE (optional) : 52 | Python executable 53 | if not set, we use CMake's find_package to search for Python3 54 | 55 | The full function specification for ``PYB11Generator_add_module`` is:: 56 | 57 | PYB11Generator_add_module( 58 | MODULE ... 59 | SOURCE ... 60 | INSTALL ... 61 | INCLUDES ... 62 | LINKS ... 63 | DEPENDS ... 64 | PYBIND11_OPTIONS ...) 65 | 66 | where the arguments are: 67 | 68 | (required) : 69 | Name of the package to be created. 70 | 71 | MODULE (optional) : 72 | Name of the Python module and CMake target to be generated. Defaults to ````. 73 | 74 | SOURCE (optional) : 75 | Optionally specify the name of the Python source file containing the PYB11Generator bindings. If not specified, defaults to ``_PYB11.py``. 76 | 77 | INSTALL (optional) : 78 | Path to install extension module -- defaults to ``${Python3_SITEARCH}/${package_name}``. 79 | 80 | INCLUDES ... (optional) : 81 | List of addition includes needed to compile the extension module. Note all standard Python include paths are included by default. 82 | 83 | LINKS ... (optional) : 84 | List of addition linking libraries, targets, or flags necessary to link the extension module. 85 | 86 | DEPENDS ... (optional) : 87 | List of targets the extension module depends on, i.e., targets that should be satisfied first. 88 | 89 | PYBIND11_OPTIONS ... (optional) : 90 | Any valid flags that can be passed to the built-in pybind11 ``pybind11_add_module`` CMake function. See pybind11 CMake `documentation `_. 91 | 92 | .. Note:: 93 | 94 | ``PYB11Generator_add_module`` only looks at the ``SOURCE`` Python file (default ``_PYB11.py``. However, that file may in turn import as many other Python files as desired to expose more interface as part of the module, so the user should feel free to organize their PYB11Generator bindings as desired for clarity. A typical pattern would be to have the top-level module ``_PYB11.py`` import individual class bindings from separate Python files for each bound class, for instance. 95 | -------------------------------------------------------------------------------- /docs/complications.rst: -------------------------------------------------------------------------------- 1 | .. _complications: 2 | 3 | ============================== 4 | Complications and corner cases 5 | ============================== 6 | 7 | .. _cross-module-inheritance: 8 | 9 | ------------------------ 10 | Cross-module inheritance 11 | ------------------------ 12 | 13 | For the most part having C++ types exposed in different modules is transparent: so long as you import all the necessary modules once they are all compiled and bound, everything just works. However, the exception to this rule is if you want to bind a class in one module that inherits from a class bound in another. Suppose for instance we have two C++ classes (``A`` and ``B``), defined in two different headers ``A.hh`` and ``B.hh``, as follows. 14 | 15 | A.hh: 16 | 17 | .. code-block:: cpp 18 | 19 | struct A { 20 | A() { printf("A::A()\n"); } 21 | virtual ~A() { printf("A::~A()\n"); } 22 | virtual int func(const int x) { printf("A::func(%d)\n", x); return x; } 23 | }; 24 | 25 | B.hh: 26 | 27 | .. code-block:: cpp 28 | 29 | #include "A.hh" 30 | 31 | struct B: public A { 32 | B(): A() { printf("B::B()\n"); } 33 | virtual ~B() { printf("B::~B()\n"); } 34 | virtual int func(const int x) override { printf("B::func(%d)\n", x); return x*x; } 35 | }; 36 | 37 | 38 | We want to expose these two class in two different modules, ``Amodule`` and ``Bmodule``. We will now need to annotate the bindings for the ``A`` class with one piece of new information -- the module it will be bound in. This is accomplished with a new decorator: ``PYB11module``, and the bindings for ``Amodule`` might look like:: 39 | 40 | from PYB11Generator import * 41 | 42 | PYB11includes = ['"A.hh"'] 43 | 44 | @PYB11module("Amodule") # <--- This is our new annotation 45 | class A: 46 | 47 | def pyinit(self): 48 | "Default constructor" 49 | 50 | @PYB11virtual 51 | def func(self, x="int"): 52 | "A::func" 53 | return "int" 54 | 55 | Let's suppose the above binding source is stored in file ``Amodule_bindings.py``. We can now write our binding source for ``Bmodule`` as normal, but we need to import ``Amodule_bindings`` so we can express the inheritance relation between ``B`` and ``A``:: 56 | 57 | from PYB11Generator import * 58 | 59 | import Amodule_bindings 60 | 61 | PYB11includes = ['"B.hh"'] 62 | 63 | class B(Amodule_bindings.A): 64 | 65 | def pyinit(self): 66 | "Default constructor" 67 | 68 | @PYB11virtual 69 | def func(self, x="int"): 70 | "B::func" 71 | return "int" 72 | 73 | The ``@PYB11module`` decoration on ``A`` tells PYB11Generator how to generate the pybind11 code to correctly import ``A`` rather than generate ``A`` locally, as described in the `pybind11 documentation `_. 74 | 75 | .. Note:: 76 | 77 | It is critical here in the bindings for ``Bmodule`` that we use ``import Amodule_bindings``, and do *not* import ``A`` into the local scope using ``from Amodule_bindings import A``! If we put ``A`` in the top-level scope of our bindings for ``B``, the binding code for ``A`` will be generated redundantly in the new bindings, and cause a conflict when we try to import the two modules together. 78 | 79 | .. _non-template-to-template-inheritance: 80 | 81 | ----------------------------------------------------- 82 | Non-templated class inheriting from a templated class 83 | ----------------------------------------------------- 84 | 85 | PYB11Generator needs to know template parameters for templated classes in order to create concrete instantiations, but since Python does not have the concept of templates we have adopted a two-stage process for creating template class instantiations in PYB11 as described in :ref:`class-templates`. However, if we have a non-templated class which inherits from a templated base, there is no longer the second-stage of this procedure using :func:`PYB11TemplateClass` to instantiate the base with the proper template parameters. 86 | 87 | It is possible to handle this situation, but it requires two decorations be applied to the non-templated descendant: 88 | 89 | #. Because the descendant will inherit the template decoration of the base class, we must explicitly state that the descendant has no template parameters with ``@PYB11template()``. 90 | 91 | #. We still need to specify what template parameters should be used for the base class. Template parameters in PYB11Generator are specified using python dictionary matching, so we can directly insert the proper template parameter choices in the appropriate dictionary for our non-templated descendant using ``@PYB11template_dict``. 92 | 93 | These two steps are best demonstrated by an example -- consider the following C++ class hierarchy: 94 | 95 | .. code-block:: cpp 96 | 97 | template 98 | class A { 99 | public: 100 | A(); 101 | virtual ~A(); 102 | virtual std::string func(const Value1& x, const Value2& y) const; 103 | }; 104 | 105 | class B: public A { 106 | public: 107 | B(); 108 | virtual ~B(); 109 | virtual std::string func(const double& x, const int& y) const; 110 | }; 111 | 112 | PYB11Generator can represent this hierarchy with:: 113 | 114 | @PYB11template("Value1", "Value2") 115 | class A: 116 | 117 | def pyinit(self): 118 | "Default A()" 119 | 120 | @PYB11virtual 121 | @PYB11const 122 | def func(self, x="const %(Value1)s&", y="const %(Value2)s&"): 123 | "Default A::func" 124 | return "std::string" 125 | 126 | @PYB11template() # <--- force not to inherit template parameters from A 127 | @PYB11template_dict({"Value1" : "double", "Value2" : "int"}) # <--- specify the template parameter substitutions 128 | class B(A): 129 | 130 | def pyinit(self): 131 | "Default B()" 132 | 133 | @PYB11virtual 134 | @PYB11const 135 | def func(self, x="const double&", y="const int&"): 136 | "B::func override" 137 | return "std::string" 138 | 139 | # We still need to instantiate any versions of A that we need/use. 140 | A_double_int = PYB11TemplateClass(A, template_parameters=("double", "int")) 141 | 142 | .. _template_class_inheritance_changes: 143 | 144 | ----------------------------------------------------------- 145 | Templated class inheritance with template parameter changes 146 | ----------------------------------------------------------- 147 | 148 | Another variation on the above is the templated class inheritance where the template parameters are changed between the base and descendant types. For example, consider the following class hierarchy: 149 | 150 | .. code-block:: cpp 151 | 152 | template 153 | class A { 154 | ... 155 | }; 156 | 157 | template 158 | class B: public A { 159 | ... 160 | }; 161 | 162 | In this case the descendant ``B`` class inherits from ``A``, but specializes one of the template arguments to ``unsigned``. Binding instantiations of ``A`` is straightforward using the methods described in :ref:`class-templates`, but how should we create instantiations of ``B``? The solution here is to use ``PYB11template_dict`` as above to specify the ``Value1`` template parameter for ``B``:: 163 | 164 | @PYB11template("Value2", "Value3") 165 | @PYB11template_dict({"Value1" : "unsigned"}) 166 | class B(A): 167 | ... 168 | 169 | B_double_int = PYB11TemplateClass(B, template_parameters=("double", "int") 170 | 171 | -------------------------------- 172 | Binding ``constexpr`` statements 173 | -------------------------------- 174 | 175 | In C++ ``constexpr`` denotes quantities that are known at compile time and therefore can be entirely optimized away during the compilation phase. Python does not really have this concept, but nonetheless we can expose ``constexpr`` definitions in our Python modules. There are two cases we might want to consider. 176 | 177 | * If we have a constexpr defined outside the scope of class or struct, we can simply use the :func:`PYB11attr` command to bind it. So for example the following C++ declaration: 178 | 179 | .. code-block:: cpp 180 | 181 | static constexpr unsigned square_of_two = 2u*2u; 182 | 183 | can be bound using the PYB11Generator statement:: 184 | 185 | square_of_two = PYB11attr() 186 | 187 | * If we have a constexpr statment defined inside a class scope we can use the :func:`PYB11property` method with the special ``constexpr`` flag instead. So a class such as: 188 | 189 | .. code-block:: cpp 190 | 191 | class A { 192 | public: 193 | A() { printf("A::A()\\n"); } 194 | ~A() { printf("A::~A()\\n"); } 195 | static constexpr double square_of_pi = M_PI*M_PI; 196 | static constexpr size_t size_of_something = 24u; 197 | }; 198 | 199 | can have its nested ``constexpr`` variables exposed with the bindings:: 200 | 201 | class A: 202 | def pyinit(self): 203 | "Default A()" 204 | square_of_pi = PYB11property(constexpr=True) 205 | size_of_something = PYB11property(constexpr=True) 206 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | import sphinx_rtd_theme 20 | 21 | 22 | # -- Project information ----------------------------------------------------- 23 | 24 | project = u'PYB11Generator' 25 | copyright = u'2018, LLNS' 26 | author = u'J. Michael Owen' 27 | 28 | # The short X.Y version 29 | version = u'2.1' 30 | # The full version, including alpha/beta/rc tags 31 | release = u'2.1.0' 32 | 33 | 34 | # -- General configuration --------------------------------------------------- 35 | 36 | # If your documentation needs a minimal Sphinx version, state it here. 37 | # 38 | # needs_sphinx = '1.0' 39 | 40 | # Add any Sphinx extension module names here, as strings. They can be 41 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 42 | # ones. 43 | extensions = [ 44 | 'sphinx.ext.intersphinx', 45 | 'sphinx.ext.todo', 46 | 'sphinx.ext.githubpages', 47 | ] 48 | 49 | # Add any paths that contain templates here, relative to this directory. 50 | templates_path = ['_templates'] 51 | 52 | # The suffix(es) of source filenames. 53 | # You can specify multiple suffix as a list of string: 54 | # 55 | # source_suffix = ['.rst', '.md'] 56 | source_suffix = '.rst' 57 | 58 | # The master toctree document. 59 | master_doc = 'index' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = None 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | # This pattern also affects html_static_path and html_extra_path. 71 | exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] 72 | 73 | # The name of the Pygments (syntax highlighting) style to use. 74 | pygments_style = None 75 | 76 | 77 | # -- Options for HTML output ------------------------------------------------- 78 | 79 | # The theme to use for HTML and HTML Help pages. See the documentation for 80 | # a list of builtin themes. 81 | # 82 | html_theme = 'sphinx_rtd_theme' 83 | 84 | # Theme options are theme-specific and customize the look and feel of a theme 85 | # further. For a list of options available for each theme, see the 86 | # documentation. 87 | # 88 | # html_theme_options = {} 89 | 90 | # Add any paths that contain custom static files (such as style sheets) here, 91 | # relative to this directory. They are copied after the builtin static files, 92 | # so a file named "default.css" will overwrite the builtin "default.css". 93 | html_static_path = [''] # ['_static'] 94 | 95 | # Custom sidebar templates, must be a dictionary that maps document names 96 | # to template names. 97 | # 98 | # The default sidebars (for documents that don't match any pattern) are 99 | # defined by theme itself. Builtin themes are using these templates by 100 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 101 | # 'searchbox.html']``. 102 | # 103 | # html_sidebars = {} 104 | 105 | 106 | # -- Options for HTMLHelp output --------------------------------------------- 107 | 108 | # Output file base name for HTML help builder. 109 | htmlhelp_basename = 'PYB11Generatordoc' 110 | 111 | 112 | # -- Options for LaTeX output ------------------------------------------------ 113 | 114 | latex_elements = { 115 | # The paper size ('letterpaper' or 'a4paper'). 116 | # 117 | # 'papersize': 'letterpaper', 118 | 119 | # The font size ('10pt', '11pt' or '12pt'). 120 | # 121 | # 'pointsize': '10pt', 122 | 123 | # Additional stuff for the LaTeX preamble. 124 | # 125 | # 'preamble': '', 126 | 127 | # Latex figure (float) alignment 128 | # 129 | # 'figure_align': 'htbp', 130 | } 131 | 132 | # Grouping the document tree into LaTeX files. List of tuples 133 | # (source start file, target name, title, 134 | # author, documentclass [howto, manual, or own class]). 135 | latex_documents = [ 136 | (master_doc, 'PYB11Generator.tex', u'PYB11Generator Documentation', 137 | u'J. Michael Owen', 'manual'), 138 | ] 139 | 140 | 141 | # -- Options for manual page output ------------------------------------------ 142 | 143 | # One entry per manual page. List of tuples 144 | # (source start file, name, description, authors, manual section). 145 | man_pages = [ 146 | (master_doc, 'pyb11generator', u'PYB11Generator Documentation', 147 | [author], 1) 148 | ] 149 | 150 | 151 | # -- Options for Texinfo output ---------------------------------------------- 152 | 153 | # Grouping the document tree into Texinfo files. List of tuples 154 | # (source start file, target name, title, author, 155 | # dir menu entry, description, category) 156 | texinfo_documents = [ 157 | (master_doc, 'PYB11Generator', u'PYB11Generator Documentation', 158 | author, 'J. Michael Owen', 'One line description of project.', 159 | 'Miscellaneous'), 160 | ] 161 | 162 | 163 | # -- Options for Epub output ------------------------------------------------- 164 | 165 | # Bibliographic Dublin Core info. 166 | epub_title = project 167 | 168 | # The unique identifier of the text. This can be a ISBN number 169 | # or the project homepage. 170 | # 171 | # epub_identifier = '' 172 | 173 | # A unique identification for the text. 174 | # 175 | # epub_uid = '' 176 | 177 | # A list of files that should not be packed into the epub file. 178 | epub_exclude_files = ['search.html'] 179 | 180 | 181 | # -- Extension configuration ------------------------------------------------- 182 | 183 | # -- Options for intersphinx extension --------------------------------------- 184 | 185 | # Example configuration for intersphinx: refer to the Python standard library. 186 | intersphinx_mapping = { 187 | #'python' : ('https://docs.python.org/', None), 188 | 'pybind11' : ('https://pybind11.readthedocs.io/en/stable', 'pybind11_objects.inv') 189 | } 190 | 191 | # -- Options for todo extension ---------------------------------------------- 192 | 193 | # If true, `todo` and `todoList` produce output, else they produce nothing. 194 | todo_include_todos = True 195 | -------------------------------------------------------------------------------- /docs/enums.rst: -------------------------------------------------------------------------------- 1 | .. _enums: 2 | 3 | ===== 4 | Enums 5 | ===== 6 | 7 | C++ enums are handled in a fairly straightforward manner as discussed in the `pybind11 docs `_. Suppose we have the following enum in C++: 8 | 9 | .. code-block:: cpp 10 | 11 | // A collection of adorable rodents 12 | enum Rodents { 13 | mouse = 0, 14 | rat = 1, 15 | squirrel = 2, 16 | hamster = 3, 17 | gerbil = 4, 18 | capybara = 5 19 | }; 20 | 21 | PYB11 uses the special method ``PYB11enum`` to declare enums, directly corresponding to the pybind11 construct ``py::enum_``. We can bind our enumeration of Rodents using:: 22 | 23 | Rodents = PYB11enum(("mouse", "rat", "squirrel", "hamster", "gerbil", "capybara"), 24 | doc="A collection of adorable rodents") 25 | 26 | See :func:`PYB11enum` for the full set of options to ``PYB11enum``. 27 | 28 | It is also straightforward to declare an enum type that is inside a class scope; if we have a C++ class with an enum like the following: 29 | 30 | .. code-block:: cpp 31 | 32 | class Homestararmy { 33 | public: 34 | 35 | enum members { 36 | HomestarRunner = 0, 37 | StrongSad = 1, 38 | Homsar = 2, 39 | PaintingOfGuyWithBigKnife = 3, 40 | FrankBennedetto = 4, 41 | }; 42 | }; 43 | 44 | We can bind this enum using PYB11Generator with:: 45 | 46 | class Homestararmy: 47 | 48 | members = PYB11enum(("HomestarRunner", "StrongSad", "Homsar", "PaintingOfGuyWithBigKnife", "FrankBennedetto")) 49 | 50 | -------------------------------------------------------------------------------- /docs/functions.rst: -------------------------------------------------------------------------------- 1 | .. _functions: 2 | 3 | ========= 4 | Functions 5 | ========= 6 | 7 | We have already introduced a quick example of binding a function in :ref:`first-example`; this section will go into more detail on how to generate pybind11 bindings for functions, including complications such as overloaded methods and C++ templates. 8 | 9 | .. _function-overloads: 10 | 11 | --------------------------------- 12 | Ordinary and overloaded functions 13 | --------------------------------- 14 | 15 | Suppose we have a header defining the following functions that we wish to bind for Python: 16 | 17 | .. code-block:: cpp 18 | 19 | double unique_function(int a, double b); 20 | double overloaded_function(int a, int b, int c); 21 | double overloaded_function(double a, double b, double c); 22 | 23 | We can use PYB11Generator to bind these functions with a file containing the following code:: 24 | 25 | from PYB11Generator import * # Necessary to get decorators 26 | 27 | def unique_function(): 28 | "This is a unique function prescription, and so requires no details about arguments or return types" 29 | return 30 | 31 | def overloaded_function(a = "int", 32 | b = "int", 33 | c = "int"): 34 | "This is the version of overloaded_function that takes ints" 35 | return "double" 36 | 37 | @PYB11pycppname("overloaded_function") 38 | def overloaded_function1(a = "double", 39 | b = "double", 40 | c = "double"): 41 | "This is the version of overloaded_function that takes doubles" 42 | return "double" 43 | 44 | The first function ``unique_function`` is trivial, since it is unambiguous and can be wrapped with an unadorned C++ function pointer as shown in :ref:`first-example`. In this case PYB11Generator assumes the C++ function name is the same as the Python function name, and all is simple. 45 | 46 | The overloaded functions take a bit more work. The first challenge is that Python does not support the concept of function overloading: two Python functions cannot have the same name. Therefore we need to use unique Python names for the C++ ``overloaded_function`` Python descriptions, which is why we define ``overloaded_function`` and ``overloaded_function1`` in the source for PYB11Generator. In order to tell PYB11Generator that we really want to call ``overloaded_function1`` ``overloaded_function`` in both the C++ and Python bindings, we use our first PYB11 decorator: ``PYB11pycppname``. This decorator tells PYB11Generator that that function in question is really called ``overloaded_function`` in C++, and we wish the Python name in the resulting binding code to call this function ``overloaded_function`` in Python as well. This is actually two statements, and there are two PYB11 decorators that can do these individual tasks independently if needed (``PYB11cppname`` and ``PYB11pyname``): ``PYB11pycppname`` is simply a convenient shorthand combination to cover the common case of wanting to simultaneously rename the bound method for C++ and Python. For a full listing of the PYB11 decorators see :ref:`decorators`. 47 | 48 | Note we have also now specified the arguments and return types for both bindings of ``overloaded_function``. This is required since the C++ functions are overloaded, and in order for the C++ compiler to distinguish which one we want it is necessary to fully specify the function signatures for the function pointers in the pybind11 binding code. PYB11Generator always checks the return value for a wrapped function: if a return value is present, it should be a string describing the C++ return type (as shown here, with both ``overloaded_function`` and ``overloaded_function1`` returning the string value ``"double"``). If such a return value is specified, PYB11Generator assumes a fully qualified C++ function pointer signature is required, and will also look for and generate the argument types as well. The function arguments should be named what the argument name will be in the resulting Python code, and set equal to a string with the C++ type of the argument as shown above for the ``overloaded_function`` descriptions. Note, a C++ ``void`` return value or argument should be set to the string ``"void"`` for PYB11Generator for such explicit specifications. 49 | 50 | .. _functions-default-args: 51 | 52 | ----------------------- 53 | Default argument values 54 | ----------------------- 55 | 56 | Another useful feature of pybind11 is the ability to specify default values for arguments to functions/methods in Python, and naturally PYB11Generator supports this feature as well. In order to specify a default value for an argument, we set the value of the argument in the Python binding code as a tuple, where the first element is a string describing the C++ type, and the second a string with the C++ default value. As an example suppose we wish to bind the following function that has two arguments (an ``int`` and a ``std::string``): 57 | 58 | .. code-block:: cpp 59 | 60 | void howToDrawADragon(int numberOfBeefyArms, std::string label name); 61 | 62 | and we want to use the default values ``1`` and ``"Trogdor"`` for these arguments. The PYB11Generator code would then look like:: 63 | 64 | def howToDrawADragon(numberOfBeefyArms = ("int", "1"), 65 | name = ("std::string", "Trogdor")): 66 | return "void" 67 | 68 | .. _function-templates: 69 | 70 | ---------------------- 71 | C++ template functions 72 | ---------------------- 73 | 74 | C++ templates present another challenge, as this another concept not found in Python. Suppose we wish to expose several instantiations of the following method: 75 | 76 | .. code-block:: cpp 77 | 78 | template 79 | ValueC 80 | transmogrify(const ValueA& x, const ValueB& y); 81 | 82 | It is always possible to explicitly (and repetitively) define the function over and over again for each template instantiation combination of (``ValueA``, ``ValueB``, ``ValueC``), but we would rather write the prescription once and have the computer generate the necessary redundant code. PYB11Generator has such a facility: a template method can be defined with the ``@PYB11template`` decorator, which takes the template arguments as a set of string arguments. The function can then be instantiated as many times as needed using the function ``PYB11TemplateFunction``. The complete PYB11Generator binding code then might look like:: 83 | 84 | from PYB11Generator import * # Necessary to get decorators and PYB11TemplateFunction 85 | 86 | @PYB11template("ValueA", "ValueB", "ValueC") 87 | def transmogrify(x = "const %(ValueA)s&", 88 | y = "const %(ValueB)s&"): 89 | "I'm sure this does something useful..." 90 | return "%(ValueC)s" 91 | 92 | transmogrifyIntIntDouble = PYB11TemplateFunction(transmogrify, ("int", "int", "double"), pyname="transmogrify") 93 | transmogrifyI32I32I64 = PYB11TemplateFunction(transmogrify, ("uint32_t", "uint32_t", "uint64_t"), pyname="transmogrify") 94 | 95 | The first thing to note when defining a template function is that the template arguments can be used as Python string dictionary substitution variables, as shown above in the definition of ``transmogrify``. Since we have defined the template parameters using the decorator ``@PYB11template("ValueA", "ValueB", "ValueC")`` we can use ``%(ValueA)s, %(ValueB)s``, or ``%(ValueC)s`` in the body of the function, as we do in this case defining the arguments and return type. 96 | 97 | Because we have decorated the ``transmogrify`` function with ``@PYB11template``, PYB11 will not generate any pybind11 code directly from this function. Instead we must define instantiations of such template functions using the PYB11 function ``PYB11TemplateFunction``. In this example we have created two such instantiations, and could continue making as many as we wish for different types. Note in this example we have made these different instantiations overloaded in Python by forcing them all to have the name ``transmogrify`` via the ``pyname="transmogrify"`` argument. This is not necessarily required: we must give each instantiation of the template a unique name in Python (``transmogrifyIntIntDouble`` and ``transmogrifyI32I32I64`` in this case), and if we are happy with those being the Python names of the wrapped results we need not specify ``pyname``. Such unique names in Python are safest, in that which instantiation the user wants to call down the line in the wrapped library call is unambiguous, but often it is nicer to force the Python names to match the C++ as we do in this case. 98 | 99 | For a full description of ``PYB11TemplateFunction`` see :func:`PYB11TemplateFunction`. 100 | 101 | .. Note:: 102 | 103 | In this example we have used the common case of C++ templates declared with ``typename`` (as in ``template``). However, for C++ templates can also use specialized parameters, such as 104 | 105 | .. code-block:: cpp 106 | 107 | template func(const double x); 108 | 109 | In such cases we need need to specify these template parameters appropriately to PYB11Generator. This is done by explictly declaring the types of the template parameters in ``PYB11template``:: 110 | 111 | @PYB11template("int T1", "double T2") 112 | def func(x = "const double"): 113 | "What does this function do?" 114 | return 115 | 116 | .. _functions-implementation: 117 | 118 | ------------------------------------------------------------- 119 | Explicitly defining the binding implementation for a function 120 | ------------------------------------------------------------- 121 | 122 | In some instances it is useful to take direct control of or modify how a given function is exposed to Python. PYB11Generator allows the user to directly specify what is passed in-place of the function pointer in such cases via the ``@PYB11implementation`` decorator. There are far too many possible use cases for this direct control to possibly discuss, but as an example suppose we have a function like the following that uses an exotic container type as an argument: 123 | 124 | .. code-block:: cpp 125 | 126 | void ExoticContainer permutate(const ExoticContainer& c); 127 | 128 | If pybind11 knows nothing about the ``ExoticContainer`` class, and we would rather expose this to Python using ordinary Python lists, we could use the following pattern to wrap a list based interface around ``permutate``:: 129 | 130 | @PYB11implementation("""[](py::list c) -> py::list { 131 | ExoticContainer ccopy; 132 | for (const auto& x: c) ccopy.push_back(x); 133 | permutate(ccopy); 134 | py::list result; 135 | for (const auto& x: ccopy) result.append(x); 136 | return result; 137 | }""") 138 | def permutate(c = "py::list"): 139 | return "py::list" 140 | 141 | The resulting pybind11 code is: 142 | 143 | .. code-block:: cpp 144 | 145 | m.def("permutate", [](py::list c) -> py::list { 146 | ExoticContainer ccopy; 147 | for (const auto& x: c) ccopy.push_back(x); 148 | permutate(ccopy); 149 | py::list result; 150 | for (const auto& x: ccopy) result.append(x); 151 | return result;1 152 | }, "c"_a); 153 | 154 | so as you can see ``@PYB11implementation`` allows the author to directly control the code inserted in the usual spot for a function pointer. Note that the argument spec is still generated (``"c"_a`` in this example), including any default arguments defined as described above in :ref:`functions-default-args`. 155 | 156 | .. _functions-noconvert: 157 | 158 | ----------------------------------------- 159 | Preventing automatic casting of arguments 160 | ----------------------------------------- 161 | 162 | In C++ automatic casting of arguments which are implicitly convertible to a different type (such as calling a function that accepts a ``double`` argument with an ``int``) is usually allowed. In pybind11 it is possible to prevent this behavior using the ``.noconvert()`` option to a Python argument, such as ``py::arg().noconvert()``. PYB11Generator supports the idea of ``noconvert`` as well, though in a less granular fashion currently as it is used to decorate an entire function signature rather than individual arguments. For instance, if we wanted to bind the following method and ensure automatic conversions of the argument are prevented: 163 | 164 | .. code-block:: cpp 165 | 166 | double munge_my_double(double x); 167 | 168 | we can accomplish this using the ``@PYB11noconvert`` decorator:: 169 | 170 | @PYB11noconvert 171 | def munge_my_double(x = "double"): 172 | return "double" 173 | 174 | See the `pybind11 discussion for more information `_. 175 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. PYB11Generator documentation master file, created by 2 | sphinx-quickstart on Sun Nov 25 11:21:07 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | PYB11Generator's documentation 7 | ============================== 8 | 9 | PYB11Generator is a python based code generator that creates `pybind11 `_ code for binding C++ libraries as extensions in Python. PYB11Generator parses input that is very close to writing the desired interface in native python, turning this into the corresponding pybind11 C++ code. 10 | 11 | .. Note:: 12 | 13 | Since PYB11Generator blindly generates C++ pybind11 code, it is essential to understand the pybind11 package itself as well! In other words, be sure to read and understand the `pybind11 documentation `_ before trying to go too far with PYB11Generator. The purpose of PYB11Generator is to reduce the burden of writing and maintaining redundant code when working with pybind11 (such as the trampoline classes required by :ref:`pybind11:overriding_virtuals`), and provide a natural syntax for those already familiar with writing interfaces in Python. However, since the generated pybind11 code produced by PYB11Generator is what is actually compiled by a C++ compiler to create the corresponding python package, any errors reported by the compiler will refer to this generated code, and require understanding pybind11 itself to properly interpret. 14 | 15 | .. Note:: 16 | PYB11Generator is now Python 3 only -- for users of Python 2 the last version of PYB11Generator supporting Python 2 is 1.0.12 (available from PyPI and Github). 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | :caption: Contents: 21 | 22 | intro 23 | functions 24 | classes 25 | enums 26 | memory 27 | stl 28 | complications 29 | cmake 30 | PYB11variables 31 | PYB11decorators 32 | PYB11functions 33 | 34 | Indices and tables 35 | ================== 36 | 37 | * :ref:`genindex` 38 | * :ref:`modindex` 39 | * :ref:`search` 40 | -------------------------------------------------------------------------------- /docs/intro.rst: -------------------------------------------------------------------------------- 1 | .. _intro: 2 | 3 | ============================== 4 | Introduction to PYB11Generator 5 | ============================== 6 | 7 | Using PYB11Generator to write the interface for a C++ binding is intended to emulate writing that same interface in Python, so if you're familiar with Python it should be easy to get started with PYB11Generator. As an example, if we have a header for a C++ function that looks like: 8 | 9 | .. code-block:: cpp 10 | 11 | // A really important function! 12 | int func(); 13 | 14 | We can define the PYB11Generator prescription for binding this method by writing a Python method as:: 15 | 16 | def func(): 17 | "A really important function!" 18 | return "int" 19 | 20 | Wherever possible we try to use ordinary Python syntax to correspond to pybind11/C++ constructs: Python functions correspond to and generate binding code for C++ functions as above; a Python class generates binding code for pybind11 to bind a C++ class; arguments for functions and methods in Python generate corresponding argument specifications in C++ function pointer syntax. Because Python is not a strongly typed language, we specify C++ types using strings (if needed) as above, where we specify the return ``int`` type by returning the string ``"int"`` from ``func``. We also use Python decorators to annotate Python methods with uniquely C++ concepts such as ``const``, ``virtual``, etc., as will be discussed in succeeding sections. 21 | 22 | .. _installation: 23 | 24 | Installation 25 | ------------ 26 | 27 | PYB11Generator uses the Python Package Index `PyPI `_ to simplify installation of PYB11Generator, so installing PYB11Generator is simply a matter of requesting it using `pip `_:: 28 | 29 | $ pip install PYB11Generator 30 | 31 | This command installs PYB11Generator (and pybind11 since it is a dependency of PYB11Generator) as packages in the Python you are using (i.e., the Python associated with the ``pip`` command). 32 | 33 | For those curious, the source for PYB11Generator is hosted on `github `_. 34 | 35 | Another approach (if you are using git for source code control) is to include PYB11Generator as a submodule of your project, in which case PYB11Generator and pybind11 (which is a submodule of PYB11Generator) will be available for use in your project directly. See the `Git documentation on submodules `_ for more information. This can also simplify building if you are using `CMake `_ as your build system, since this makes the PYB11Generator CMake extensions available as well. Use of CMake to build PYB11Generator generated modules is discussed :ref:`cmake`. 36 | 37 | .. _the-basics: 38 | 39 | The basics: how to generate pybind11 code using PYB11Generator 40 | -------------------------------------------------------------- 41 | 42 | PYB11Generator works by starting up a Python process, importing a module containing Python definitions for functions and classes corresponding to the C++ interface to be bound, and invoking the function ``PYB11generateModule`` on the imported Python module, which writes out a C++ file of pybind11 statements binding that interface. This generated pybind11 C++ file is what is compiled by a C++ compiler to create the final Python shared module allowing the C++ methods and classes to be exercised from Python. As an example, if we have created a Python file ``mymodule.py`` containing the Python description of the C++ methods we wish to expose in a module to be called ``mymodule``, we can invoke ``PYB11generateModule`` to create the intermediate C++ file as:: 43 | 44 | python -c 'from PYB11Generator import *; import mymodule; PYB11generateModule(mymodule)' 45 | 46 | resulting in the creation of a C++ source file ``mymodule.cc``. A full description of the ``PYB11generateModule`` interface is given in :ref:`PYB11-functions`. 47 | 48 | .. _first-example: 49 | 50 | A first example start to finish 51 | ------------------------------- 52 | 53 | To explicitly demonstrate the stages of creating bindings using PYB11Generator, here we recreate an early example in the pybind11 documentation: :ref:`pybind11:simple_example`. Say we want to wrap the following C++ method: 54 | 55 | .. code-block:: cpp 56 | 57 | int add(int i, int j) { 58 | return i + j; 59 | } 60 | 61 | We can use PYB11Generator to create the same pybind11 code used to wrap this method in the pybind11 tutorial by writing a file ``simple_example.py`` containing:: 62 | 63 | "pybind11 example plugin" 64 | 65 | PYB11preamble = """ 66 | int add(int i, int j) { 67 | return i + j; 68 | } 69 | """ 70 | 71 | def add(): 72 | "A function which adds two numbers" 73 | return 74 | 75 | Now executing the command:: 76 | 77 | $ python -c 'from PYB11Generator import *; import simple_example; PYB11generateModule(simple_example, "example")' 78 | 79 | creates a file ``example.cc``, which looks like (omitting the boilerplate preamble code with ``#include``'s): 80 | 81 | .. code-block:: cpp 82 | 83 | int add(int i, int j) { 84 | return i + j; 85 | } 86 | 87 | //------------------------------------------------------------------------------ 88 | // Make the module 89 | //------------------------------------------------------------------------------ 90 | PYBIND11_MODULE(example, m) { 91 | 92 | m.doc() = "pybind11 example plugin" ; 93 | 94 | //........................................................................... 95 | // Methods 96 | m.def("add", &add, "A function which adds two numbers"); 97 | } 98 | 99 | This is identical to the native pybind11 binding code from the pybind11 tutorial :ref:`pybind11:simple_example`, modulo some comments. This code can now be compiled to the final Python shared module as described this same pybind11 tutorial:: 100 | 101 | $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python -m pybind11 --includes` example.cc -o example.so 102 | 103 | A few things worth noting: 104 | 105 | * This example uses the fact that if the function being wrapped is unambiguous, allowing us to use a bare C++ function pointer (without the full explicit function prescription). This is reflected in the PYB11Generator syntax when we write the ``def add()`` function in python without arguments or a return type. 106 | * In order to directly insert the C++ function definition into the resulting C++ file, we have used the special variable ``PYB11preamble`` variable. A more typical use case will require ``#include``-ing the necessary C++ header files in the generated code, which is accomplished through another special variable, ``PYB11includes``, described in :ref:`variables`. 107 | * In general special variables and commands to PYB11Generator use the prefix ``PYB11`` such as ``PYB11preamble`` in this example. 108 | * Note also that ordinary Python doc strings (both for the module and function) are picked up from ``simple_example.py`` and propagated to the pybind11 bindings. 109 | 110 | This example demonstrates the steps necessary to create a usable Python module using PYB11Generator: 111 | 112 | #. Create a Python file describing the desired interface using ordinary Python syntax, based on the C++ methods and classes to be bound. 113 | #. Run a Python line like above to generate the pybind11 C++ code from this Python input. 114 | #. Compile the resulting pybind11 C++ code to create the Python shared module. 115 | 116 | In the following sections we describe the nuances of creating the PYB11 Python input files in much more detail; we will not show the compilation examples beyond this point since it is no different than using pybind11 directly, and the above example pretty much covers it. 117 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/memory.rst: -------------------------------------------------------------------------------- 1 | .. _memory-policies: 2 | 3 | ================= 4 | Memory management 5 | ================= 6 | 7 | Generally memory management "just works" when binding C++ and Python with pybind11 without worrying about how the memory/lifetime of the objects is handled. However, since C++ allows memory and objects to be allocated/deallocated in a variety of ways, at times it is necessary to pay attention to this issue. In this section we discuss the pybind11 methods of handling memory and object lifetimes PYB11Generator provides wrappers for. In order to understand this section it is advisable to read the `pybind11 documentation on the use of smart pointers `_, pybind11 :ref:`pybind11:return_value_policies`, and :ref:`pybind11:call_policies`. 8 | 9 | .. _class-holder: 10 | 11 | Class holder types 12 | ================== 13 | 14 | When pybind11 creates a new instance of a bound C++ class, it uses a smart pointer type to hold and manage that instance. The default type used by pybind11 for this purpose is ``std::unique_ptr``, which means new objects created in this manner by Python will be deallocated when their reference count goes to zero. In most circumstances this is fine, but some C++ applications may have a smart pointer type they already are working with. In such cases it might be preferable to make pybind11 manage these object using the same sort of smart pointer. In PYB11 we specify this by decorating class declarations with ``@PYB11holder``. For instance, to make pybind11 use ``std::shared_ptr`` to hold a class type ``A``:: 15 | 16 | @PYB11holder("std::shared_ptr") 17 | class A: 18 | 19 | def pyinit(self): 20 | "Default constructor" 21 | 22 | This tells pybind11 any new instance of ``A`` created from python should be managed by ``std::shared_ptr``. pybind11 supports ``std::unique_ptr`` and ``std::shared_ptr`` without further work. It is possible to use any reference counting smart pointer for this purpose, but types other than ``std::unique_ptr`` and ``std::shared_ptr`` require more information be specified to pybind11. PYB11 does not provide any convenience methods for adding that additional information, but it can be done directly with pybind11 as described in the `pybind11 documentation `_. 23 | 24 | .. Note:: 25 | 26 | Overriding the holder smart pointer type can result in subtleties that lead to hard to understand memory errors. If using this capability, read the `pybind11 description `_ carefully! 27 | 28 | .. _return-policies: 29 | 30 | Return value policies 31 | ===================== 32 | 33 | Return value policies are important but at times tricky for C++ types returned by reference or pointer. By default pybind11 assumes Python will take ownership of returned values, implying Python can delete those objects when the Python reference count falls to zero. If a C++ library returns a pointer to memory it expects to manage, however, the result of this conflict over who can manage (delete) that memory is an error. For this reason pybind11 provides :ref:`pybind11:return_value_policies`, allowing the developer to explicitly state how memory returned from C++ should be handled. Before using these policies it is *critical* to read and understand these policies from pybind11. These return value policies are applied (for functions or methods) using the ``@PYB11returnpolicy`` decorator, with allowed values ``take_ownership``, ``copy``, ``move``, ``reference``, ``reference_internal``, ``automatic``, and ``automatic_reference``. The default policy is ``automatic``. 34 | 35 | Consider the example C++ function from the pybind11 documentation: 36 | 37 | .. code-block:: cpp 38 | 39 | /* Function declaration */ 40 | Data *get_data() { return _data; /* (pointer to a static data structure) */ } 41 | 42 | If we want to tell pybind11 the C++ side will manage the memory for the returned ``Data*`` from this method, the ``reference`` return policy is appropriate. We can express this by decorating the function binding as:: 43 | 44 | @PYB11returnpolicy("reference") 45 | def get_data(): 46 | return "Data*" 47 | 48 | Decorating return values from class methods is identical to functions: simply use ``@PYB11returnpolicy`` to decorate the method declaration. 49 | 50 | .. _call-policies: 51 | 52 | Call policies 53 | ============= 54 | 55 | While :ref:`return-policies` are specific to return types from functions or methods, call policies allow the user to tie together the lifetimes of return values and/or arguments. This is discussed in depth in the pybind11 documentation :ref:`pybind11:call_policies`. PYB11 provides the decorator ``@PYB11keepalive(a, b)`` for direct access to the pybind11 command ``py::keep_alive``. The arguments to the decorator ``a`` and ``b`` are integers indicating arguments in the call signature by position index, with the convention: 56 | 57 | * 0 denotes the return value of the function/method. 58 | 59 | * If decorating a class method, index 1 is the ``this`` (or ``self``) pointer. 60 | 61 | To recreate the example from the pybind11 documentation, if we have a custom ``List`` class which we are binding in Python, we might want to decorate the ``append`` method like:: 62 | 63 | class List: 64 | 65 | @PYB11keepalive(1, 2) 66 | def append(self, x): 67 | return "void" 68 | 69 | This tells pybind11 to keep the the second argument (``x``, the element being appended) alive so long as the first argument (``self``, our container class) is alive. 70 | 71 | .. _call_guards: 72 | 73 | Call guards 74 | =========== 75 | 76 | Another variation on wrapping functions/methods is to provide ``call_guard`` as discussed in the `pybind11 call_guard documentation `_. Call guards must be default constructable C++ types, and will be built in scope just before calling the wrapped method. One typical usage would be to release the Python Global Interpreter Lock (GIL) before calling a wrapped method, so something like:: 77 | 78 | @PYB11call_guard("py::gil_scoped_release") 79 | def my_wrapped_method(): 80 | "Some C++ method that needs to have the GIL released." 81 | return 82 | -------------------------------------------------------------------------------- /docs/pybind11_objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLNL/PYB11Generator/d28bb7e985052cb2e7928e7b63b850ac871f5542/docs/pybind11_objects.inv -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme 2 | -------------------------------------------------------------------------------- /docs/stl.rst: -------------------------------------------------------------------------------- 1 | .. _stl: 2 | 3 | ============== 4 | STL containers 5 | ============== 6 | 7 | In many cases STL containers (such as ``vector``, ``deque``, ``map``, etc.) can be used transparently with pybind11: python lists will automatically convert to ``std::vector`` and vice versa for example with no extra work or notation needed. The main caveat to this convenience, however, is that this is accomplished by copying the data in the container, which has two potential drawbacks: for large containers this may not be practical; second, any attempt to change data on either the C++ or Python side will be lost due to the fact you would be operating on a copy. Even if your function/method specification is passing STL containers by reference, this silent copying will make modifying them across the Python/C++ interface impossible without further work. 8 | 9 | If you need to pass an STL container without all this magic copying, it becomes necessary to directly bind such containers and define the behavior you want. PYB11Generator provides some interfaces to pybind11's machinery for such bindings, but it is essential to first read and understand what `pybind11 is doing for STL types `_. 10 | 11 | PYB11Generator currently only provides Python convenience methods for handling two STL containers: ``std::vector`` via :func:`PYB11_bind_vector`, and ``std::map`` with :func:`PYB11_bind_map`. It is possible to use the pybind11 semantics directly in C++ in combination with PYB11Generator to handle cases beyond ``std::vector`` and ``std::map`` of course, it simply involves using the `pybind11 C++ interface `_ directly. 12 | 13 | std::vector 14 | =========== 15 | 16 | Suppose we want to bind ``std::vector`` and ``std::vector`` in our module such that they will be modifiable through the interface (no copying). We can accomplish this by adding two lines to our Python module definition:: 17 | 18 | vector_of_int = PYB11_bind_vector("int", opaque=True) 19 | vector_of_Aclass = PYB11_bind_vector("Aclass", opaque=True) 20 | 21 | When we import the resulting compiled module it will now include the types ``vector_of_int`` and ``vector_of_Aclass`` explicitly, and we need to deal in those types rather than the more convenient Python lists for arguments of those types. The ``opaque`` argument here is what makes pybind11 treat these vector's as mutable references through the Python/C++ interface. We also have the option of making these bindings local to the module or global: see :func:`PYB11_bind_vector` for the full description. 22 | 23 | std::map 24 | ======== 25 | 26 | Making ``std::map`` instances mutable through the Python/C++ interface (opaque as described in pybind11 terms) is similar to our treatment of ``std::vector``. If in our module we need to use ``std::map`` as an opaque argument we simply add a line:: 27 | 28 | map_of_int_to_Aclass = PYB11_bind_map("int", "Aclass", opaque=True) 29 | 30 | Just as in our prior ``std::vector`` examples, our module will now include a type ``map_of_int_to_Aclass`` which we can use explicitly to pass this container between Python and C++ mutably. 31 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "setuptools-scm"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "PYB11Generator" 7 | authors = [{name = "J. Michael Owen", email="mikeowen@llnl.gov"}] 8 | description = "PYB11Generator is a code generation tool which generates pybind11 code for binding C++ to Python. Documentation is available at https://pyb11generator.readthedocs.io/" 9 | readme = "README.md" 10 | requires-python = ">=3.7" 11 | classifiers=[ 12 | "Programming Language :: Python :: 3", 13 | "License :: OSI Approved :: BSD License", 14 | "Operating System :: OS Independent", 15 | ] 16 | dependencies = ["pybind11", "decorator"] 17 | #dynamic = ["version"] 18 | version = "2.1.1" 19 | 20 | [project.urls] 21 | "Homepage" = "https://github.com/LLNL/PYB11Generator" 22 | "Bug Tracker" = "https://github.com/LLNL/PYB11Generator/issues" 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Distribution script for PYB11Generator pybind11 code generation package 2 | 3 | import setuptools 4 | import os, glob 5 | 6 | with open("README.md", "r") as fh: 7 | long_description = fh.read() 8 | 9 | cmake_files = glob.glob("cmake/*.cmake") + glob.glob("cmake/*.py") 10 | 11 | # This base logic is cribbed from the pybind11 example. 12 | # This will _not_ affect installing from wheels, 13 | # only building wheels or installing from SDist. 14 | # Primarily intended on Windows, where this is sometimes 15 | # customized (for example, conda-forge uses Library/) 16 | base = os.environ.get("PYBIND11_GLOBAL_PREFIX", "") 17 | 18 | # Must have a separator 19 | if base and not base.endswith("/"): 20 | base += "/" 21 | 22 | setuptools.setup( 23 | name = "PYB11Generator", 24 | version = "2.1.1", 25 | author = "J. Michael Owen", 26 | author_email = "mikeowen@llnl.gov", 27 | description = "A code generator for the pybind11 C++ <-> Python language binding tool", 28 | long_description = long_description, 29 | long_description_content_type = "text/markdown", 30 | url = "https://github.com/LLNL/PYB11Generator", 31 | include_package_data = True, 32 | packages = setuptools.find_packages(), 33 | classifiers = [ 34 | "Programming Language :: Python :: 3", 35 | "License :: OSI Approved :: BSD License", 36 | "Operating System :: OS Independent", 37 | ], 38 | install_requires = [ 39 | "decorator", 40 | "pybind11" 41 | ], 42 | data_files = [ 43 | (base + "cmake", cmake_files), 44 | ], 45 | ) 46 | -------------------------------------------------------------------------------- /tests/arrays/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(arrays INSTALL ${CMAKE_INSTALL_PREFIX}/tests/arrays) 2 | -------------------------------------------------------------------------------- /tests/arrays/MyArray.hh: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class MyArray { 6 | 7 | public: 8 | using ContainerType = std::vector; 9 | using iterator = typename ContainerType::iterator; 10 | using const_iterator = typename ContainerType::const_iterator; 11 | 12 | MyArray(): mContainer() { std::cerr << "MyArray()\n"; } 13 | MyArray(const size_t size): mContainer(size) { std::cerr << "MyArray(" << size << ")\n"; } 14 | MyArray(const size_t size, 15 | const Value& x): mContainer(size, x) { std::cerr << "MyArray(" << size << ", x)\n"; } 16 | ~MyArray() { std::cerr << "~MyArray()\n"; } 17 | size_t size() const { std::cerr << "MyArray::size\n"; return mContainer.size(); } 18 | Value& operator[](const size_t index) { std::cerr << "MyArray[" << index << "]\n"; return mContainer[index]; } 19 | iterator begin() { std::cerr << "MyArray::begin()\n"; return mContainer.begin(); } 20 | iterator end() { std::cerr << "MyArray::end()\n"; return mContainer.end(); } 21 | const_iterator begin() const { std::cerr << "MyArray::begin() (const)\n"; return mContainer.begin(); } 22 | const_iterator end() const { std::cerr << "MyArray::end() (const)\n"; return mContainer.end(); } 23 | 24 | private: 25 | ContainerType mContainer; 26 | }; 27 | -------------------------------------------------------------------------------- /tests/arrays/arrays_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11includes = ['"MyArray.hh"'] 4 | 5 | @PYB11template("Value") 6 | class MyArray: 7 | "Thin wrapper around a native C++ array type" 8 | 9 | PYB11typedefs = """ 10 | using MyArrayType = MyArray<%(Value)s>; 11 | """ 12 | 13 | def pyinit(self): 14 | "Default MyArray constructor" 15 | return 16 | 17 | def pyinit1(self, 18 | size = "const size_t"): 19 | "Construct with given size" 20 | return 21 | 22 | def pyinit2(self, 23 | size = "const size_t", 24 | x = "const %(Value)s&"): 25 | "Construct with a give size and initial value for all elements" 26 | return 27 | 28 | @PYB11const 29 | def size(self): 30 | "Return size of array" 31 | return "size_t" 32 | 33 | @PYB11cppname("size") 34 | @PYB11const 35 | def __len__(self): 36 | return "size_t" 37 | 38 | @PYB11cppname("operator[]") 39 | @PYB11returnpolicy("reference_internal") 40 | def __getitem__(self, 41 | index = "const size_t"): 42 | return "%(Value)s&" 43 | 44 | @PYB11implementation("[](MyArrayType& self, size_t i, const %(Value)s x) { const auto n = self.size(); if (i >= n) throw py::index_error(); self[(i %% n + n) %% n] = x; }") 45 | def __setitem__(self, 46 | index = "const size_t", 47 | x = "const %(Value)s&"): 48 | "Set a value" 49 | return 50 | 51 | @PYB11implementation("[](const MyArrayType& self) { return py::make_iterator(self.begin(), self.end()); }, py::keep_alive<0,1>()") 52 | def __iter__(self): 53 | "Python iteration through MyArray." 54 | return 55 | 56 | MyArray_double = PYB11TemplateClass(MyArray, template_parameters="double") 57 | MyArray_of_MyArray_double = PYB11TemplateClass(MyArray, template_parameters="MyArray") 58 | -------------------------------------------------------------------------------- /tests/constexpr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(constexpr INSTALL ${CMAKE_INSTALL_PREFIX}/tests/constexpr) 2 | -------------------------------------------------------------------------------- /tests/constexpr/constexpr_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11preamble = """ 4 | 5 | constexpr unsigned square_of_two = 2u*2u; 6 | static constexpr unsigned cube_of_three = 3u*3u*3u; 7 | 8 | class A { 9 | public: 10 | A() { printf("A::A()\\n"); } 11 | ~A() { printf("A::~A()\\n"); } 12 | static constexpr double square_of_pi = M_PI*M_PI; 13 | static constexpr size_t size_of_something = 24u; 14 | }; 15 | """ 16 | 17 | #............................................................................... 18 | square_of_two = PYB11attr() 19 | cube_of_three = PYB11attr() 20 | 21 | #............................................................................... 22 | class A: 23 | 24 | def pyinit(self): 25 | "Default A()" 26 | 27 | square_of_pi = PYB11property("double", constexpr=True, static=True) 28 | size_of_something = PYB11property("unsigned", constexpr=True, static=True) 29 | -------------------------------------------------------------------------------- /tests/cross_module/A.hh: -------------------------------------------------------------------------------- 1 | struct A { 2 | A() { printf("A::A()\n"); } 3 | ~A() { printf("A::~A()\n"); } 4 | virtual int func(const int x) { printf("A::func(%d)\n", x); return x; } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/cross_module/A_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11includes = ['"A.hh"'] 4 | 5 | @PYB11module("A") 6 | class A: 7 | 8 | def pyinit(self): 9 | "Default constructor" 10 | 11 | @PYB11virtual 12 | def func(self, x="int"): 13 | "A::func" 14 | return "int" 15 | -------------------------------------------------------------------------------- /tests/cross_module/B.hh: -------------------------------------------------------------------------------- 1 | #include "A.hh" 2 | 3 | struct B: public A { 4 | B(): A() { printf("B::B()\n"); } 5 | ~B() { printf("B::~B()\n"); } 6 | virtual int func(const int x) override { printf("B::func(%d)\n", x); return x*x; } 7 | }; 8 | -------------------------------------------------------------------------------- /tests/cross_module/B_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | import A_PYB11 4 | 5 | PYB11includes = ['"B.hh"'] 6 | 7 | class B(A_PYB11.A): 8 | 9 | def pyinit(self): 10 | "Default constructor" 11 | 12 | @PYB11virtual 13 | def func(self, x="int"): 14 | "B::func" 15 | return "int" 16 | -------------------------------------------------------------------------------- /tests/cross_module/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(A INSTALL ${CMAKE_INSTALL_PREFIX}/tests/cross_module) 2 | PYB11Generator_add_module(B INSTALL ${CMAKE_INSTALL_PREFIX}/tests/cross_module) 3 | -------------------------------------------------------------------------------- /tests/inheritance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(change_templates INSTALL ${CMAKE_INSTALL_PREFIX}/tests/inheritance) 2 | PYB11Generator_add_module(inherit_base_virtual_methods INSTALL ${CMAKE_INSTALL_PREFIX}/tests/inheritance) 3 | PYB11Generator_add_module(nontemplate_inherit_from_template INSTALL ${CMAKE_INSTALL_PREFIX}/tests/inheritance) 4 | 5 | -------------------------------------------------------------------------------- /tests/inheritance/change_templates_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11preamble = """ 4 | #include 5 | 6 | template 7 | class A { 8 | public: 9 | A() { printf("A::A()\\n"); } 10 | virtual ~A() { printf("A::~A()\\n"); } 11 | virtual void func(const Value1& x, const Value2& y) const { printf("A::func(...)\\n"); } 12 | }; 13 | 14 | template 15 | class B: public A { 16 | public: 17 | B(): A() { printf("B::B()\\n"); } 18 | virtual ~B() { printf("B::~B()\\n"); } 19 | virtual void yetAnotherFunc(const Value3& x, const Value4& y) const { printf("B::yetAnotherFunc(...)\\n"); } 20 | }; 21 | """ 22 | 23 | @PYB11template("Value1", "Value2") 24 | class A: 25 | 26 | def pyinit(self): 27 | "Default A()" 28 | 29 | @PYB11virtual 30 | @PYB11const 31 | def func(self, x="const %(Value1)s&", y="const %(Value2)s&"): 32 | "Default A::func" 33 | return "void" 34 | 35 | @PYB11template("Value3", "Value4") 36 | @PYB11template_dict({"Value1" : "double", "Value2" : "int"}) 37 | class B(A): 38 | 39 | def pyinit(self): 40 | "Default B()" 41 | 42 | @PYB11virtual 43 | @PYB11const 44 | def yetAnotherFunc(self, x="const %(Value3)s&", y="const %(Value4)s&"): 45 | "Default B::yetAnotherFunc" 46 | return "void" 47 | 48 | # We still need to instantiate any versions of A that we need/use. 49 | A_double_int = PYB11TemplateClass(A, template_parameters=("double", "int")) 50 | B_ADI_uint = PYB11TemplateClass(B, template_parameters= {"Value3" : "A", 51 | "Value4" : "unsigned"}) 52 | -------------------------------------------------------------------------------- /tests/inheritance/inherit_base_virtual_methods_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11preamble = """ 4 | #include 5 | 6 | class A { 7 | public: 8 | A() { printf("A::A()\\n"); } 9 | virtual ~A() { printf("A::~A()\\n"); } 10 | virtual void Afunc() { printf("A::Afunc()\\n"); } 11 | }; 12 | 13 | class B: public A { 14 | public: 15 | B() { printf("B::B()\\n"); } 16 | virtual ~B() { printf("B::~B()\\n"); } 17 | virtual void Bfunc() { printf("B::Bfunc()\\n"); } 18 | }; 19 | """ 20 | 21 | #............................................................................... 22 | class A: 23 | 24 | def pyinit(self): 25 | "Default A()" 26 | 27 | @PYB11virtual 28 | def Afunc(self): 29 | "Default A::Afunc" 30 | return "void" 31 | 32 | #............................................................................... 33 | class B(A): 34 | 35 | def pyinit(self): 36 | "Default B()" 37 | 38 | @PYB11virtual 39 | def Bfunc(self): 40 | "Default B::Bfunc" 41 | return "void" 42 | -------------------------------------------------------------------------------- /tests/inheritance/nontemplate_inherit_from_template_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11preamble = """ 4 | #include 5 | 6 | template 7 | class A { 8 | public: 9 | A() { printf("A::A()\\n"); } 10 | virtual ~A() { printf("A::~A()\\n"); } 11 | virtual void func(const Value1& x, const Value2& y) const { printf("A::func(...)\\n"); } 12 | }; 13 | 14 | class B: public A { 15 | public: 16 | B(): A() { printf("B::B()\\n"); } 17 | virtual ~B() { printf("B::~B()\\n"); } 18 | virtual void func(const double& x, const int& y) const override { printf("B::func(...)\\n"); } 19 | }; 20 | """ 21 | 22 | @PYB11template("Value1", "Value2") 23 | class A: 24 | 25 | def pyinit(self): 26 | "Default A()" 27 | 28 | @PYB11virtual 29 | @PYB11const 30 | def func(self, x="const %(Value1)s&", y="const %(Value2)s&"): 31 | "Default A::func" 32 | return "void" 33 | 34 | @PYB11template() # <--- force not to inherit template parameters from A 35 | @PYB11template_dict({"Value1" : "double", "Value2" : "int"}) # <--- specify the template parameter substitutions 36 | class B(A): 37 | 38 | def pyinit(self): 39 | "Default B()" 40 | 41 | @PYB11virtual 42 | @PYB11const 43 | def func(self, x="const double&", y="const int&"): 44 | "B::func override" 45 | return "void" 46 | 47 | # We still need to instantiate any versions of A that we need/use. 48 | A_double_int = PYB11TemplateClass(A, template_parameters=("double", "int")) 49 | -------------------------------------------------------------------------------- /tests/inject/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(inject_test INSTALL ${CMAKE_INSTALL_PREFIX}/tests/inject) 2 | -------------------------------------------------------------------------------- /tests/inject/inject_test_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11preamble = """ 4 | #include 5 | 6 | class A { 7 | public: 8 | A() { printf("A::A()\\n"); } 9 | virtual ~A() { printf("A::~A()\\n"); } 10 | virtual void Apure() = 0; 11 | void blago() const { printf("A::blago()\\n"); } 12 | }; 13 | 14 | """ 15 | 16 | class A: 17 | def pyinit(self): 18 | "A()" 19 | 20 | @PYB11const 21 | def blago(self): 22 | "blago" 23 | return 24 | 25 | @PYB11ignore 26 | class bogus_for_injection: 27 | 28 | def Apure(self): 29 | "injected A virtual method" 30 | return "void" 31 | 32 | PYB11inject(bogus_for_injection, A, pure_virtual=True) 33 | -------------------------------------------------------------------------------- /tests/numpy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(test_np_array) 2 | -------------------------------------------------------------------------------- /tests/numpy/my_array.hh: -------------------------------------------------------------------------------- 1 | class Vector { 2 | public: 3 | double x, y, z; 4 | Vector(double x = 0.0, 5 | double y = 0.0, 6 | double z = 0.0): 7 | x(x), 8 | y(y), 9 | z(z) {} 10 | }; 11 | 12 | class my_array { 13 | private: 14 | std::vector mdata; 15 | public: 16 | std::vector& data() { return mdata; } 17 | }; 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/numpy/test_np_array_PYB11.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | PYB11includes = ['"my_array.hh"', 4 | '"pybind11/numpy.h"'] 5 | 6 | PYB11modulepreamble = """ 7 | PYBIND11_NUMPY_DTYPE(Vector, x, y, z); 8 | """ 9 | 10 | class Vector: 11 | def pyinit(self, 12 | x = ("double", "0.0"), 13 | y = ("double", "0.0"), 14 | z = ("double", "0.0")): 15 | return 16 | 17 | x = PYB11readwrite() 18 | y = PYB11readwrite() 19 | z = PYB11readwrite() 20 | 21 | class my_array: 22 | def pyinit(self): 23 | return 24 | 25 | def data(self): 26 | return "std::vector&" 27 | 28 | @PYB11implementation("[](py::object& obj) -> py::array_t { auto& self = obj.cast(); return py::array_t({self.data().size()}, &(*self.data().begin()), obj); }") 29 | def array(self): 30 | return "py::array_t" 31 | -------------------------------------------------------------------------------- /tests/protected/A.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | 3 | @PYB11template("T1", "T2") 4 | class A: 5 | 6 | def pyinit(self): 7 | "A default constructor" 8 | return 9 | 10 | @PYB11pure_virtual 11 | @PYB11protected 12 | def func(self, 13 | val1 = "const %(T1)s", 14 | val2 = "const %(T2)s"): 15 | return "void" 16 | 17 | x = PYB11readwrite() 18 | y = PYB11readwrite() 19 | 20 | A_int_double = PYB11TemplateClass(A, template_parameters=("int", "double")) 21 | -------------------------------------------------------------------------------- /tests/protected/B.py: -------------------------------------------------------------------------------- 1 | from PYB11Generator import * 2 | from A import * 3 | 4 | @PYB11template("T1", "T2") 5 | class B(A): 6 | 7 | def pyinit(self): 8 | "B default constructor" 9 | return 10 | 11 | @PYB11virtual 12 | @PYB11protected 13 | def func(self, 14 | val1 = "const %(T1)s", 15 | val2 = "const %(T2)s"): 16 | return "void" 17 | 18 | x = PYB11readwrite() 19 | y = PYB11readwrite() 20 | 21 | B_int_double = PYB11TemplateClass(B, template_parameters=("int", "double")) 22 | -------------------------------------------------------------------------------- /tests/protected/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PYB11Generator_add_module(protected INSTALL ${CMAKE_INSTALL_PREFIX}/tests/cross_module) 2 | -------------------------------------------------------------------------------- /tests/protected/protected.hh: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class A { 5 | public: 6 | T1 x; 7 | T2 y; 8 | A() { printf("A()\n"); } 9 | virtual ~A() { printf("~A()\n"); } 10 | protected: 11 | virtual void func(const T1 val1, const T2 val2) = 0; 12 | }; 13 | 14 | template 15 | class B: public A { 16 | public: 17 | B() { printf("B()\n"); } 18 | virtual ~B() {printf("~B()\n"); } 19 | protected: 20 | virtual void func(const T1 val1, const T2 val2) override { 21 | std::cout << "B::val(" << val1 << " , " << val2 << ")\n"; 22 | A::x = val1; 23 | A::y = val2; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /tests/protected/protected_PYB11.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test of overriding protected abstract methods 3 | """ 4 | 5 | from PYB11Generator import * 6 | 7 | PYB11includes = ['"protected.hh"'] 8 | 9 | from A import * 10 | from B import * 11 | --------------------------------------------------------------------------------