From d8401de4669598d9e9dfbab86ae6a3a4584b778a Mon Sep 17 00:00:00 2001 From: uoip Date: Sat, 6 Jan 2018 07:32:36 +0800 Subject: [PATCH] add files --- CMakeLists.txt | 104 + CMakeModules/AndroidUtils.cmake | 245 ++ CMakeModules/CreateMethodCallFile.cmake | 11 + CMakeModules/EmbedBinaryFiles.cmake | 32 + CMakeModules/FindDC1394.cmake | 32 + CMakeModules/FindDepthSense.cmake | 43 + CMakeModules/FindEigen.cmake | 83 + CMakeModules/FindFFMPEG.cmake | 88 + CMakeModules/FindFREEGLUT.cmake | 44 + CMakeModules/FindGLEW.cmake | 53 + CMakeModules/FindGLUES.cmake | 38 + CMakeModules/FindLibRealSense.cmake | 34 + CMakeModules/FindMediaFoundation.cmake | 20 + CMakeModules/FindOculus.cmake | 63 + CMakeModules/FindOpenEXR.cmake | 33 + CMakeModules/FindOpenNI.cmake | 49 + CMakeModules/FindOpenNI2.cmake | 59 + CMakeModules/FindPleora.cmake | 143 + CMakeModules/FindROBOTVISION.cmake | 31 + CMakeModules/FindTeliCam.cmake | 58 + CMakeModules/FindTooN.cmake | 28 + CMakeModules/FindXrandr.cmake | 32 + CMakeModules/Findlibusb1.cmake | 42 + CMakeModules/Findpthread.cmake | 42 + CMakeModules/Finduvc.cmake | 39 + CMakeModules/Findzstd.cmake | 35 + CMakeModules/SetPlatformVars.cmake | 56 + LICENCE | 22 + cmake_uninstall.cmake.in | 25 + examples/CMakeLists.txt | 31 + examples/HelloPangolin/CMakeLists.txt | 6 + examples/HelloPangolin/main.cpp | 34 + examples/SharedMemoryCamera/CMakeLists.txt | 7 + examples/SharedMemoryCamera/main.cpp | 53 + examples/SimpleDisplay/CMakeLists.txt | 7 + examples/SimpleDisplay/app.cfg | 26 + examples/SimpleDisplay/main.cpp | 129 + examples/SimpleDisplayImage/CMakeLists.txt | 6 + examples/SimpleDisplayImage/main.cpp | 72 + examples/SimpleMultiDisplay/CMakeLists.txt | 6 + examples/SimpleMultiDisplay/app.cfg | 28 + examples/SimpleMultiDisplay/main.cpp | 106 + examples/SimplePlot/CMakeLists.txt | 6 + examples/SimplePlot/main.cpp | 49 + examples/SimpleRecord/CMakeLists.txt | 6 + examples/SimpleRecord/main.cpp | 94 + examples/SimpleScene/CMakeLists.txt | 6 + examples/SimpleScene/main.cpp | 40 + examples/SimpleVideo/CMakeLists.txt | 6 + examples/SimpleVideo/main.cpp | 111 + examples/VBODisplay/CMakeLists.txt | 19 + examples/VBODisplay/kernal.cu | 30 + examples/VBODisplay/main.cpp | 84 + external/CMakeLists.txt | 157 + external/pybind11/.appveyor.yml | 66 + external/pybind11/.gitignore | 37 + external/pybind11/.gitmodules | 3 + external/pybind11/.readthedocs.yml | 3 + external/pybind11/.travis.yml | 212 ++ external/pybind11/CMakeLists.txt | 155 + external/pybind11/CONTRIBUTING.md | 37 + external/pybind11/ISSUE_TEMPLATE.md | 17 + external/pybind11/LICENSE | 36 + external/pybind11/MANIFEST.in | 2 + external/pybind11/README.md | 129 + external/pybind11/docs/Doxyfile | 20 + .../pybind11/docs/_static/theme_overrides.css | 11 + .../pybind11/docs/advanced/cast/chrono.rst | 81 + .../pybind11/docs/advanced/cast/custom.rst | 91 + .../pybind11/docs/advanced/cast/eigen.rst | 310 ++ .../docs/advanced/cast/functional.rst | 109 + .../pybind11/docs/advanced/cast/index.rst | 42 + .../pybind11/docs/advanced/cast/overview.rst | 163 + external/pybind11/docs/advanced/cast/stl.rst | 243 ++ .../pybind11/docs/advanced/cast/strings.rst | 303 ++ external/pybind11/docs/advanced/classes.rst | 1001 +++++ external/pybind11/docs/advanced/embedding.rst | 261 ++ .../pybind11/docs/advanced/exceptions.rst | 142 + external/pybind11/docs/advanced/functions.rst | 498 +++ external/pybind11/docs/advanced/misc.rst | 272 ++ .../pybind11/docs/advanced/pycpp/index.rst | 13 + .../pybind11/docs/advanced/pycpp/numpy.rst | 366 ++ .../pybind11/docs/advanced/pycpp/object.rst | 170 + .../docs/advanced/pycpp/utilities.rst | 144 + .../pybind11/docs/advanced/smart_ptrs.rst | 173 + external/pybind11/docs/basics.rst | 293 ++ external/pybind11/docs/benchmark.py | 88 + external/pybind11/docs/benchmark.rst | 97 + external/pybind11/docs/changelog.rst | 927 +++++ external/pybind11/docs/classes.rst | 500 +++ external/pybind11/docs/compiling.rst | 273 ++ external/pybind11/docs/conf.py | 332 ++ external/pybind11/docs/faq.rst | 273 ++ external/pybind11/docs/index.rst | 47 + external/pybind11/docs/intro.rst | 95 + external/pybind11/docs/limitations.rst | 20 + external/pybind11/docs/pybind11-logo.png | Bin 0 -> 58510 bytes .../docs/pybind11_vs_boost_python1.png | Bin 0 -> 44653 bytes .../docs/pybind11_vs_boost_python1.svg | 427 +++ .../docs/pybind11_vs_boost_python2.png | Bin 0 -> 41121 bytes .../docs/pybind11_vs_boost_python2.svg | 427 +++ external/pybind11/docs/reference.rst | 102 + external/pybind11/docs/release.rst | 25 + external/pybind11/docs/requirements.txt | 1 + external/pybind11/docs/upgrade.rst | 404 +++ external/pybind11/include/pybind11/attr.h | 489 +++ .../pybind11/include/pybind11/buffer_info.h | 108 + external/pybind11/include/pybind11/cast.h | 2053 +++++++++++ external/pybind11/include/pybind11/chrono.h | 162 + external/pybind11/include/pybind11/common.h | 2 + external/pybind11/include/pybind11/complex.h | 61 + .../pybind11/include/pybind11/detail/class.h | 606 ++++ .../pybind11/include/pybind11/detail/common.h | 800 ++++ .../pybind11/include/pybind11/detail/descr.h | 185 + .../pybind11/include/pybind11/detail/init.h | 325 ++ .../include/pybind11/detail/internals.h | 247 ++ .../pybind11/include/pybind11/detail/typeid.h | 53 + external/pybind11/include/pybind11/eigen.h | 610 ++++ external/pybind11/include/pybind11/embed.h | 194 + external/pybind11/include/pybind11/eval.h | 117 + .../pybind11/include/pybind11/functional.h | 85 + external/pybind11/include/pybind11/iostream.h | 200 + external/pybind11/include/pybind11/numpy.h | 1600 ++++++++ .../pybind11/include/pybind11/operators.h | 168 + external/pybind11/include/pybind11/options.h | 65 + external/pybind11/include/pybind11/pybind11.h | 1965 ++++++++++ external/pybind11/include/pybind11/pytypes.h | 1332 +++++++ external/pybind11/include/pybind11/stl.h | 369 ++ external/pybind11/include/pybind11/stl_bind.h | 599 +++ external/pybind11/pybind11/__init__.py | 11 + external/pybind11/pybind11/__main__.py | 37 + external/pybind11/pybind11/_version.py | 2 + external/pybind11/setup.cfg | 10 + external/pybind11/setup.py | 108 + external/pybind11/tests/CMakeLists.txt | 236 ++ external/pybind11/tests/conftest.py | 241 ++ external/pybind11/tests/constructor_stats.h | 276 ++ external/pybind11/tests/local_bindings.h | 64 + external/pybind11/tests/object.h | 175 + .../tests/pybind11_cross_module_tests.cpp | 123 + external/pybind11/tests/pybind11_tests.cpp | 93 + external/pybind11/tests/pybind11_tests.h | 65 + external/pybind11/tests/pytest.ini | 15 + external/pybind11/tests/test_buffers.cpp | 169 + external/pybind11/tests/test_buffers.py | 83 + .../pybind11/tests/test_builtin_casters.cpp | 156 + .../pybind11/tests/test_builtin_casters.py | 322 ++ .../pybind11/tests/test_call_policies.cpp | 98 + external/pybind11/tests/test_call_policies.py | 187 + external/pybind11/tests/test_callbacks.cpp | 149 + external/pybind11/tests/test_callbacks.py | 107 + external/pybind11/tests/test_chrono.cpp | 47 + external/pybind11/tests/test_chrono.py | 101 + external/pybind11/tests/test_class.cpp | 357 ++ external/pybind11/tests/test_class.py | 235 ++ .../tests/test_cmake_build/CMakeLists.txt | 58 + .../pybind11/tests/test_cmake_build/embed.cpp | 21 + .../installed_embed/CMakeLists.txt | 15 + .../installed_function/CMakeLists.txt | 12 + .../installed_target/CMakeLists.txt | 22 + .../pybind11/tests/test_cmake_build/main.cpp | 6 + .../subdirectory_embed/CMakeLists.txt | 25 + .../subdirectory_function/CMakeLists.txt | 8 + .../subdirectory_target/CMakeLists.txt | 15 + .../pybind11/tests/test_cmake_build/test.py | 5 + .../tests/test_constants_and_functions.cpp | 113 + .../tests/test_constants_and_functions.py | 39 + external/pybind11/tests/test_copy_move.cpp | 213 ++ external/pybind11/tests/test_copy_move.py | 112 + .../pybind11/tests/test_docstring_options.cpp | 61 + .../pybind11/tests/test_docstring_options.py | 38 + external/pybind11/tests/test_eigen.cpp | 317 ++ external/pybind11/tests/test_eigen.py | 681 ++++ .../pybind11/tests/test_embed/CMakeLists.txt | 34 + external/pybind11/tests/test_embed/catch.cpp | 16 + .../tests/test_embed/test_interpreter.cpp | 269 ++ .../tests/test_embed/test_interpreter.py | 9 + external/pybind11/tests/test_enum.cpp | 71 + external/pybind11/tests/test_enum.py | 121 + external/pybind11/tests/test_eval.cpp | 91 + external/pybind11/tests/test_eval.py | 17 + external/pybind11/tests/test_eval_call.py | 4 + external/pybind11/tests/test_exceptions.cpp | 168 + external/pybind11/tests/test_exceptions.py | 144 + .../tests/test_factory_constructors.cpp | 337 ++ .../tests/test_factory_constructors.py | 459 +++ external/pybind11/tests/test_iostream.cpp | 73 + external/pybind11/tests/test_iostream.py | 203 ++ .../tests/test_kwargs_and_defaults.cpp | 71 + .../tests/test_kwargs_and_defaults.py | 107 + .../pybind11/tests/test_local_bindings.cpp | 101 + .../pybind11/tests/test_local_bindings.py | 226 ++ .../tests/test_methods_and_attributes.cpp | 446 +++ .../tests/test_methods_and_attributes.py | 476 +++ external/pybind11/tests/test_modules.cpp | 98 + external/pybind11/tests/test_modules.py | 72 + .../tests/test_multiple_inheritance.cpp | 220 ++ .../tests/test_multiple_inheritance.py | 349 ++ external/pybind11/tests/test_numpy_array.cpp | 295 ++ external/pybind11/tests/test_numpy_array.py | 402 ++ external/pybind11/tests/test_numpy_dtypes.cpp | 451 +++ external/pybind11/tests/test_numpy_dtypes.py | 298 ++ .../pybind11/tests/test_numpy_vectorize.cpp | 89 + .../pybind11/tests/test_numpy_vectorize.py | 196 + external/pybind11/tests/test_opaque_types.cpp | 63 + external/pybind11/tests/test_opaque_types.py | 46 + .../tests/test_operator_overloading.cpp | 146 + .../tests/test_operator_overloading.py | 106 + external/pybind11/tests/test_pickling.cpp | 130 + external/pybind11/tests/test_pickling.py | 36 + external/pybind11/tests/test_pytypes.cpp | 272 ++ external/pybind11/tests/test_pytypes.py | 240 ++ .../tests/test_sequences_and_iterators.cpp | 334 ++ .../tests/test_sequences_and_iterators.py | 158 + external/pybind11/tests/test_smart_ptr.cpp | 270 ++ external/pybind11/tests/test_smart_ptr.py | 220 ++ external/pybind11/tests/test_stl.cpp | 238 ++ external/pybind11/tests/test_stl.py | 200 + external/pybind11/tests/test_stl_binders.cpp | 107 + external/pybind11/tests/test_stl_binders.py | 183 + .../pybind11/tests/test_virtual_functions.cpp | 450 +++ .../pybind11/tests/test_virtual_functions.py | 371 ++ external/pybind11/tools/FindCatch.cmake | 57 + external/pybind11/tools/FindEigen3.cmake | 81 + .../pybind11/tools/FindPythonLibsNew.cmake | 195 + external/pybind11/tools/check-style.sh | 70 + external/pybind11/tools/libsize.py | 38 + external/pybind11/tools/mkdoc.py | 304 ++ .../pybind11/tools/pybind11Config.cmake.in | 100 + external/pybind11/tools/pybind11Tools.cmake | 202 ++ include/pangolin/compat/glconsole.h | 76 + include/pangolin/compat/glutbitmap.h | 96 + include/pangolin/compat/ovr.h | 37 + include/pangolin/compat/type_traits.h | 49 + include/pangolin/config.h | 74 + include/pangolin/console/ConsoleInterpreter.h | 80 + include/pangolin/console/ConsoleView.h | 109 + include/pangolin/display/attach.h | 87 + include/pangolin/display/device/OsxWindow.h | 66 + .../display/device/PangolinNSApplication.h | 59 + .../display/device/PangolinNSGLView.h | 45 + include/pangolin/display/device/WinWindow.h | 87 + .../pangolin/display/device/X11GlContext.h | 0 include/pangolin/display/device/X11Window.h | 107 + .../pangolin/display/device/display_android.h | 333 ++ .../pangolin/display/device/display_glut.h | 75 + include/pangolin/display/display.h | 219 ++ include/pangolin/display/display_internal.h | 134 + include/pangolin/display/image_view.h | 68 + .../pangolin/display/opengl_render_state.h | 425 +++ include/pangolin/display/user_app.h | 43 + include/pangolin/display/view.h | 233 ++ include/pangolin/display/viewport.h | 62 + include/pangolin/display/widgets/widgets.h | 140 + include/pangolin/display/window.h | 52 + include/pangolin/factory/factory_registry.h | 113 + include/pangolin/gl/cg.h | 283 ++ include/pangolin/gl/colour.h | 173 + include/pangolin/gl/compat/gl2engine.h | 320 ++ include/pangolin/gl/compat/gl_es_compat.h | 60 + include/pangolin/gl/gl.h | 246 ++ include/pangolin/gl/gl.hpp | 761 ++++ include/pangolin/gl/glchar.h | 78 + include/pangolin/gl/glcuda.h | 258 ++ include/pangolin/gl/gldraw.h | 518 +++ include/pangolin/gl/glfont.h | 78 + include/pangolin/gl/glformattraits.h | 214 ++ include/pangolin/gl/glglut.h | 48 + include/pangolin/gl/glinclude.h | 46 + include/pangolin/gl/glpangoglu.h | 80 + include/pangolin/gl/glpixformat.h | 91 + include/pangolin/gl/glplatform.h | 85 + include/pangolin/gl/glsl.h | 563 +++ include/pangolin/gl/glstate.h | 220 ++ include/pangolin/gl/gltext.h | 98 + include/pangolin/gl/gltexturecache.h | 116 + include/pangolin/gl/glvbo.h | 225 ++ include/pangolin/handler/handler.h | 111 + include/pangolin/handler/handler_enums.h | 94 + include/pangolin/handler/handler_glbuffer.h | 48 + include/pangolin/handler/handler_image.h | 159 + include/pangolin/hud/oculus_hud.h | 89 + include/pangolin/image/copy.h | 45 + include/pangolin/image/image.h | 414 +++ include/pangolin/image/image_convert.h | 31 + include/pangolin/image/image_io.h | 65 + include/pangolin/image/image_utils.h | 153 + include/pangolin/image/managed_image.h | 175 + include/pangolin/image/memcpy.h | 110 + include/pangolin/image/pixel_format.h | 65 + include/pangolin/image/typed_image.h | 91 + include/pangolin/ios/PangolinAppDelegate.h | 36 + include/pangolin/ios/PangolinUIView.h | 22 + include/pangolin/log/packet.h | 70 + include/pangolin/log/packetstream.h | 111 + include/pangolin/log/packetstream_reader.h | 120 + include/pangolin/log/packetstream_source.h | 63 + include/pangolin/log/packetstream_tags.h | 46 + include/pangolin/log/packetstream_writer.h | 173 + include/pangolin/log/playback_session.h | 48 + include/pangolin/log/sync_time.h | 230 ++ include/pangolin/pangolin.h | 67 + include/pangolin/platform.h | 81 + include/pangolin/plot/datalog.h | 243 ++ include/pangolin/plot/plotter.h | 279 ++ include/pangolin/plot/range.h | 372 ++ include/pangolin/python/PyInterpreter.h | 70 + include/pangolin/python/PyModulePangolin.h | 40 + include/pangolin/python/PyPangoIO.h | 172 + include/pangolin/python/PyUniqueObj.h | 111 + include/pangolin/python/PyVar.h | 267 ++ include/pangolin/scene/axis.h | 113 + include/pangolin/scene/interactive.h | 67 + include/pangolin/scene/interactive_index.h | 115 + include/pangolin/scene/renderable.h | 118 + include/pangolin/scene/scenehandler.h | 182 + include/pangolin/tools/video_viewer.h | 104 + include/pangolin/utils/argagg.hpp | 1548 ++++++++ include/pangolin/utils/assert.h | 60 + include/pangolin/utils/compontent_cast.h | 42 + include/pangolin/utils/file_extension.h | 70 + include/pangolin/utils/file_utils.h | 145 + .../pangolin/utils/fix_size_buffer_queue.h | 153 + include/pangolin/utils/format_string.h | 87 + include/pangolin/utils/log.h | 44 + include/pangolin/utils/memstreambuf.h | 43 + include/pangolin/utils/params.h | 80 + include/pangolin/utils/picojson.h | 1408 +++++++ .../pangolin/utils/posix/condition_variable.h | 27 + include/pangolin/utils/posix/semaphore.h | 26 + .../utils/posix/shared_memory_buffer.h | 25 + include/pangolin/utils/registration.h | 64 + include/pangolin/utils/signal_slot.h | 56 + include/pangolin/utils/sigstate.h | 75 + include/pangolin/utils/simple_math.h | 437 +++ include/pangolin/utils/threadedfilebuf.h | 87 + include/pangolin/utils/timer.h | 115 + include/pangolin/utils/transform.h | 102 + include/pangolin/utils/type_convert.h | 210 ++ include/pangolin/utils/uri.h | 49 + include/pangolin/utils/xml/license.txt | 52 + include/pangolin/utils/xml/rapidxml.hpp | 2635 ++++++++++++++ .../pangolin/utils/xml/rapidxml_iterators.hpp | 174 + include/pangolin/utils/xml/rapidxml_print.hpp | 421 +++ include/pangolin/utils/xml/rapidxml_utils.hpp | 122 + include/pangolin/var/input_record_repeat.h | 86 + include/pangolin/var/var.h | 328 ++ include/pangolin/var/varextra.h | 92 + include/pangolin/var/varstate.h | 133 + include/pangolin/var/varvalue.h | 114 + include/pangolin/var/varvaluegeneric.h | 84 + include/pangolin/var/varvaluet.h | 46 + include/pangolin/var/varwrapper.h | 91 + include/pangolin/video/drivers/debayer.h | 116 + include/pangolin/video/drivers/deinterlace.h | 62 + include/pangolin/video/drivers/depthsense.h | 170 + include/pangolin/video/drivers/ffmpeg.h | 206 ++ include/pangolin/video/drivers/firewire.h | 254 ++ include/pangolin/video/drivers/images.h | 111 + include/pangolin/video/drivers/images_out.h | 59 + include/pangolin/video/drivers/join.h | 78 + include/pangolin/video/drivers/merge.h | 70 + include/pangolin/video/drivers/mirror.h | 93 + include/pangolin/video/drivers/openni.h | 72 + include/pangolin/video/drivers/openni2.h | 147 + .../pangolin/video/drivers/openni_common.h | 153 + include/pangolin/video/drivers/pango.h | 101 + .../video/drivers/pango_video_output.h | 70 + include/pangolin/video/drivers/pleora.h | 196 + include/pangolin/video/drivers/pvn.h | 77 + include/pangolin/video/drivers/realsense.h | 87 + .../pangolin/video/drivers/shared_memory.h | 37 + include/pangolin/video/drivers/shift.h | 73 + include/pangolin/video/drivers/split.h | 65 + include/pangolin/video/drivers/teli.h | 116 + include/pangolin/video/drivers/test.h | 66 + include/pangolin/video/drivers/thread.h | 112 + include/pangolin/video/drivers/unpack.h | 84 + include/pangolin/video/drivers/uvc.h | 103 + .../video/drivers/uvc_mediafoundation.h | 70 + include/pangolin/video/drivers/v4l.h | 124 + include/pangolin/video/iostream_operators.h | 132 + .../pangolin/video/stream_encoder_factory.h | 22 + include/pangolin/video/stream_info.h | 100 + include/pangolin/video/video.h | 257 ++ include/pangolin/video/video_exception.h | 29 + include/pangolin/video/video_input.h | 139 + include/pangolin/video/video_interface.h | 172 + include/pangolin/video/video_output.h | 78 + .../pangolin/video/video_output_interface.h | 52 + include/pangolin/video/video_record_repeat.h | 31 + python/CMakeLists.txt | 14 + python/blank.hpp | 21 + python/contrib.hpp | 258 ++ python/display/attach.hpp | 42 + python/display/display.hpp | 118 + python/display/image_view.hpp | 45 + python/display/opengl_render_state.hpp | 198 + python/display/user_app.hpp | 42 + python/display/view.hpp | 216 ++ python/display/viewport.hpp | 46 + python/display/widgets/widgets.hpp | 105 + python/display/window.hpp | 20 + python/examples/HelloPangolin.py | 48 + python/examples/SimpleDisplayImage.py | 67 + python/examples/SimpleDisplayMenu.py | 92 + python/examples/SimpleMultiDisplay.py | 113 + python/examples/SimplePlot.py | 51 + python/examples/SimplePlotDisplay.py | 76 + python/examples/SimpleScene.py | 44 + python/examples/app.cfg | 0 python/examples/imgs/HelloPangolin.png | Bin 0 -> 32968 bytes .../examples/imgs/HelloPangolinColorful.png | Bin 0 -> 79275 bytes python/examples/imgs/SPTAM_KITTI00.png | Bin 0 -> 202383 bytes python/examples/imgs/SPTAM_PointCloud.png | Bin 0 -> 194169 bytes python/examples/imgs/SimpleDisplayImage.png | Bin 0 -> 3480 bytes python/examples/imgs/SimpleDisplayMenu.png | Bin 0 -> 18892 bytes python/examples/imgs/SimpleMultiDisplay.png | Bin 0 -> 4242 bytes python/examples/imgs/SimplePlot.png | Bin 0 -> 52692 bytes python/examples/imgs/SimplePlotDisplay.png | Bin 0 -> 27974 bytes python/examples/imgs/SimpleScene.png | Bin 0 -> 2982 bytes python/examples/imgs/simple_draw.png | Bin 0 -> 46182 bytes python/examples/simple_draw.py | 87 + python/gl/colour.hpp | 38 + python/gl/gl.hpp | 92 + python/gl/gldraw.hpp | 23 + python/handler/blank.hpp | 18 + python/handler/handler.hpp | 46 + python/handler/handler_enums.hpp | 139 + python/handler/handler_image.hpp | 21 + python/pangolin.cpp | 104 + python/plot/datalog.hpp | 91 + python/plot/plotter.hpp | 135 + python/plot/range.hpp | 111 + python/scene/scene.hpp | 155 + python/utils/params.hpp | 25 + python/var/var.hpp | 95 + python/var/varextra.hpp | 57 + python/var/varvalue.hpp | 50 + python/var/varvaluegeneric.hpp | 51 + python/var/varvaluet.hpp | 38 + setup.py | 49 + src/CMakeLists.txt | 682 ++++ src/Doxyfile.in | 2281 ++++++++++++ src/PangolinConfig.cmake.in | 14 + src/PangolinConfigVersion.cmake.in | 17 + src/_embed_/fonts/AnonymousPro.ttf | Bin 0 -> 158080 bytes src/_embed_/fonts/AnonymousPro.txt | 94 + src/config.h.in | 74 + src/console/ConsoleView.cpp | 289 ++ src/display/device/PangolinNSApplication.mm | 91 + src/display/device/PangolinNSGLView.mm | 316 ++ src/display/device/display_android.cpp | 1008 ++++++ src/display/device/display_glut.cpp | 177 + src/display/device/display_osx.mm | 217 ++ src/display/device/display_win.cpp | 474 +++ src/display/device/display_x11.cpp | 516 +++ src/display/display.cpp | 637 ++++ src/display/image_view.cpp | 222 ++ src/display/opengl_render_state.cpp | 619 ++++ src/display/view.cpp | 584 +++ src/display/viewport.cpp | 101 + src/display/widgets/widgets.cpp | 678 ++++ src/gl/compat/gl2engine.cpp | 39 + src/gl/glchar.cpp | 70 + src/gl/gldraw.cpp | 51 + src/gl/glfont.cpp | 205 ++ src/gl/glpangoglu.cpp | 264 ++ src/gl/gltext.cpp | 202 ++ src/gl/gltexturecache.cpp | 38 + src/gl/stb_truetype.h | 3222 +++++++++++++++++ src/handler/handler.cpp | 380 ++ src/handler/handler_glbuffer.cpp | 45 + src/handler/handler_image.cpp | 452 +++ src/hud/oculus_hud.cpp | 395 ++ src/image/image_io.cpp | 157 + src/image/image_io_exr.cpp | 184 + src/image/image_io_jpg.cpp | 256 ++ src/image/image_io_pango.cpp | 62 + src/image/image_io_png.cpp | 233 ++ src/image/image_io_ppm.cpp | 104 + src/image/image_io_raw.cpp | 25 + src/image/image_io_tga.cpp | 53 + src/image/image_io_zstd.cpp | 125 + src/image/pixel_format.cpp | 67 + src/ios/PangolinAppDelegate.mm | 49 + src/ios/PangolinUIView.mm | 187 + src/log/packet.cpp | 79 + src/log/packetstream.cpp | 142 + src/log/packetstream_reader.cpp | 425 +++ src/log/packetstream_writer.cpp | 156 + src/log/playback_session.cpp | 26 + src/plot/datalog.cpp | 255 ++ src/plot/plotter.cpp | 1168 ++++++ src/python/PyInterpreter.cpp | 214 ++ src/python/PyModulePangolin.cpp | 157 + src/tools/video_viewer.cpp | 424 +++ src/utils/file_extension.cpp | 194 + src/utils/file_utils.cpp | 488 +++ src/utils/posix/condition_variable.cpp | 89 + src/utils/posix/semaphore.cpp | 83 + src/utils/posix/shared_memory_buffer.cpp | 134 + src/utils/sigstate.cpp | 60 + src/utils/threadedfilebuf.cpp | 255 ++ src/utils/timer.cpp | 0 src/utils/uri.cpp | 104 + src/var/input_record_repeat.cpp | 183 + src/var/vars.cpp | 260 ++ src/video/drivers/debayer.cpp | 360 ++ src/video/drivers/deinterlace.cpp | 107 + src/video/drivers/depthsense.cpp | 656 ++++ src/video/drivers/ffmpeg.cpp | 882 +++++ src/video/drivers/firewire.cpp | 970 +++++ src/video/drivers/images.cpp | 231 ++ src/video/drivers/images_out.cpp | 126 + src/video/drivers/join.cpp | 426 +++ src/video/drivers/json.cpp | 86 + src/video/drivers/merge.cpp | 165 + src/video/drivers/mirror.cpp | 272 ++ src/video/drivers/openni.cpp | 300 ++ src/video/drivers/openni2.cpp | 686 ++++ src/video/drivers/pango.cpp | 232 ++ src/video/drivers/pango_video_output.cpp | 269 ++ src/video/drivers/pleora.cpp | 744 ++++ src/video/drivers/pvn.cpp | 136 + src/video/drivers/realsense.cpp | 100 + src/video/drivers/shared_memory.cpp | 99 + src/video/drivers/shift.cpp | 159 + src/video/drivers/split.cpp | 158 + src/video/drivers/teli.cpp | 558 +++ src/video/drivers/test.cpp | 111 + src/video/drivers/thread.cpp | 248 ++ src/video/drivers/unpack.cpp | 268 ++ src/video/drivers/uvc.cpp | 316 ++ src/video/drivers/uvc_mediafoundation.cpp | 635 ++++ src/video/drivers/v4l.cpp | 741 ++++ src/video/stream_encoder_factory.cpp | 61 + src/video/video.cpp | 80 + src/video/video_input.cpp | 220 ++ src/video/video_interface_factory.cpp | 42 + src/video/video_output.cpp | 92 + src/video/video_output_interface_factory.cpp | 42 + test/CMakeLists.txt | 1 + test/log/CMakeLists.txt | 6 + test/log/testlog.cpp | 282 ++ tools/CMakeLists.txt | 8 + tools/Plotter/CMakeLists.txt | 53 + tools/Plotter/application-x-pangoplot.xml | 9 + tools/Plotter/csv_data_loader.h | 87 + tools/Plotter/main.cpp | 126 + tools/VideoConvert/CMakeLists.txt | 15 + tools/VideoConvert/main.cpp | 75 + tools/VideoJson/CMakeLists.txt | 18 + tools/VideoJson/main-print.cpp | 36 + tools/VideoJson/main-transform.cpp | 63 + tools/VideoViewer/CMakeLists.txt | 53 + tools/VideoViewer/application-x-pango.svg | 424 +++ tools/VideoViewer/application-x-pango.xml | 10 + tools/VideoViewer/main.cpp | 51 + 559 files changed, 101800 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 CMakeModules/AndroidUtils.cmake create mode 100644 CMakeModules/CreateMethodCallFile.cmake create mode 100644 CMakeModules/EmbedBinaryFiles.cmake create mode 100644 CMakeModules/FindDC1394.cmake create mode 100644 CMakeModules/FindDepthSense.cmake create mode 100644 CMakeModules/FindEigen.cmake create mode 100644 CMakeModules/FindFFMPEG.cmake create mode 100644 CMakeModules/FindFREEGLUT.cmake create mode 100644 CMakeModules/FindGLEW.cmake create mode 100644 CMakeModules/FindGLUES.cmake create mode 100644 CMakeModules/FindLibRealSense.cmake create mode 100644 CMakeModules/FindMediaFoundation.cmake create mode 100644 CMakeModules/FindOculus.cmake create mode 100644 CMakeModules/FindOpenEXR.cmake create mode 100644 CMakeModules/FindOpenNI.cmake create mode 100644 CMakeModules/FindOpenNI2.cmake create mode 100644 CMakeModules/FindPleora.cmake create mode 100644 CMakeModules/FindROBOTVISION.cmake create mode 100644 CMakeModules/FindTeliCam.cmake create mode 100644 CMakeModules/FindTooN.cmake create mode 100644 CMakeModules/FindXrandr.cmake create mode 100644 CMakeModules/Findlibusb1.cmake create mode 100644 CMakeModules/Findpthread.cmake create mode 100644 CMakeModules/Finduvc.cmake create mode 100644 CMakeModules/Findzstd.cmake create mode 100644 CMakeModules/SetPlatformVars.cmake create mode 100644 LICENCE create mode 100644 cmake_uninstall.cmake.in create mode 100644 examples/CMakeLists.txt create mode 100644 examples/HelloPangolin/CMakeLists.txt create mode 100644 examples/HelloPangolin/main.cpp create mode 100644 examples/SharedMemoryCamera/CMakeLists.txt create mode 100644 examples/SharedMemoryCamera/main.cpp create mode 100644 examples/SimpleDisplay/CMakeLists.txt create mode 100644 examples/SimpleDisplay/app.cfg create mode 100644 examples/SimpleDisplay/main.cpp create mode 100644 examples/SimpleDisplayImage/CMakeLists.txt create mode 100644 examples/SimpleDisplayImage/main.cpp create mode 100644 examples/SimpleMultiDisplay/CMakeLists.txt create mode 100644 examples/SimpleMultiDisplay/app.cfg create mode 100644 examples/SimpleMultiDisplay/main.cpp create mode 100644 examples/SimplePlot/CMakeLists.txt create mode 100644 examples/SimplePlot/main.cpp create mode 100644 examples/SimpleRecord/CMakeLists.txt create mode 100644 examples/SimpleRecord/main.cpp create mode 100644 examples/SimpleScene/CMakeLists.txt create mode 100644 examples/SimpleScene/main.cpp create mode 100644 examples/SimpleVideo/CMakeLists.txt create mode 100644 examples/SimpleVideo/main.cpp create mode 100644 examples/VBODisplay/CMakeLists.txt create mode 100644 examples/VBODisplay/kernal.cu create mode 100644 examples/VBODisplay/main.cpp create mode 100644 external/CMakeLists.txt create mode 100644 external/pybind11/.appveyor.yml create mode 100644 external/pybind11/.gitignore create mode 100644 external/pybind11/.gitmodules create mode 100644 external/pybind11/.readthedocs.yml create mode 100644 external/pybind11/.travis.yml create mode 100644 external/pybind11/CMakeLists.txt create mode 100644 external/pybind11/CONTRIBUTING.md create mode 100644 external/pybind11/ISSUE_TEMPLATE.md create mode 100644 external/pybind11/LICENSE create mode 100644 external/pybind11/MANIFEST.in create mode 100644 external/pybind11/README.md create mode 100644 external/pybind11/docs/Doxyfile create mode 100644 external/pybind11/docs/_static/theme_overrides.css create mode 100644 external/pybind11/docs/advanced/cast/chrono.rst create mode 100644 external/pybind11/docs/advanced/cast/custom.rst create mode 100644 external/pybind11/docs/advanced/cast/eigen.rst create mode 100644 external/pybind11/docs/advanced/cast/functional.rst create mode 100644 external/pybind11/docs/advanced/cast/index.rst create mode 100644 external/pybind11/docs/advanced/cast/overview.rst create mode 100644 external/pybind11/docs/advanced/cast/stl.rst create mode 100644 external/pybind11/docs/advanced/cast/strings.rst create mode 100644 external/pybind11/docs/advanced/classes.rst create mode 100644 external/pybind11/docs/advanced/embedding.rst create mode 100644 external/pybind11/docs/advanced/exceptions.rst create mode 100644 external/pybind11/docs/advanced/functions.rst create mode 100644 external/pybind11/docs/advanced/misc.rst create mode 100644 external/pybind11/docs/advanced/pycpp/index.rst create mode 100644 external/pybind11/docs/advanced/pycpp/numpy.rst create mode 100644 external/pybind11/docs/advanced/pycpp/object.rst create mode 100644 external/pybind11/docs/advanced/pycpp/utilities.rst create mode 100644 external/pybind11/docs/advanced/smart_ptrs.rst create mode 100644 external/pybind11/docs/basics.rst create mode 100644 external/pybind11/docs/benchmark.py create mode 100644 external/pybind11/docs/benchmark.rst create mode 100644 external/pybind11/docs/changelog.rst create mode 100644 external/pybind11/docs/classes.rst create mode 100644 external/pybind11/docs/compiling.rst create mode 100644 external/pybind11/docs/conf.py create mode 100644 external/pybind11/docs/faq.rst create mode 100644 external/pybind11/docs/index.rst create mode 100644 external/pybind11/docs/intro.rst create mode 100644 external/pybind11/docs/limitations.rst create mode 100644 external/pybind11/docs/pybind11-logo.png create mode 100644 external/pybind11/docs/pybind11_vs_boost_python1.png create mode 100644 external/pybind11/docs/pybind11_vs_boost_python1.svg create mode 100644 external/pybind11/docs/pybind11_vs_boost_python2.png create mode 100644 external/pybind11/docs/pybind11_vs_boost_python2.svg create mode 100644 external/pybind11/docs/reference.rst create mode 100644 external/pybind11/docs/release.rst create mode 100644 external/pybind11/docs/requirements.txt create mode 100644 external/pybind11/docs/upgrade.rst create mode 100644 external/pybind11/include/pybind11/attr.h create mode 100644 external/pybind11/include/pybind11/buffer_info.h create mode 100644 external/pybind11/include/pybind11/cast.h create mode 100644 external/pybind11/include/pybind11/chrono.h create mode 100644 external/pybind11/include/pybind11/common.h create mode 100644 external/pybind11/include/pybind11/complex.h create mode 100644 external/pybind11/include/pybind11/detail/class.h create mode 100644 external/pybind11/include/pybind11/detail/common.h create mode 100644 external/pybind11/include/pybind11/detail/descr.h create mode 100644 external/pybind11/include/pybind11/detail/init.h create mode 100644 external/pybind11/include/pybind11/detail/internals.h create mode 100644 external/pybind11/include/pybind11/detail/typeid.h create mode 100644 external/pybind11/include/pybind11/eigen.h create mode 100644 external/pybind11/include/pybind11/embed.h create mode 100644 external/pybind11/include/pybind11/eval.h create mode 100644 external/pybind11/include/pybind11/functional.h create mode 100644 external/pybind11/include/pybind11/iostream.h create mode 100644 external/pybind11/include/pybind11/numpy.h create mode 100644 external/pybind11/include/pybind11/operators.h create mode 100644 external/pybind11/include/pybind11/options.h create mode 100644 external/pybind11/include/pybind11/pybind11.h create mode 100644 external/pybind11/include/pybind11/pytypes.h create mode 100644 external/pybind11/include/pybind11/stl.h create mode 100644 external/pybind11/include/pybind11/stl_bind.h create mode 100644 external/pybind11/pybind11/__init__.py create mode 100644 external/pybind11/pybind11/__main__.py create mode 100644 external/pybind11/pybind11/_version.py create mode 100644 external/pybind11/setup.cfg create mode 100644 external/pybind11/setup.py create mode 100644 external/pybind11/tests/CMakeLists.txt create mode 100644 external/pybind11/tests/conftest.py create mode 100644 external/pybind11/tests/constructor_stats.h create mode 100644 external/pybind11/tests/local_bindings.h create mode 100644 external/pybind11/tests/object.h create mode 100644 external/pybind11/tests/pybind11_cross_module_tests.cpp create mode 100644 external/pybind11/tests/pybind11_tests.cpp create mode 100644 external/pybind11/tests/pybind11_tests.h create mode 100644 external/pybind11/tests/pytest.ini create mode 100644 external/pybind11/tests/test_buffers.cpp create mode 100644 external/pybind11/tests/test_buffers.py create mode 100644 external/pybind11/tests/test_builtin_casters.cpp create mode 100644 external/pybind11/tests/test_builtin_casters.py create mode 100644 external/pybind11/tests/test_call_policies.cpp create mode 100644 external/pybind11/tests/test_call_policies.py create mode 100644 external/pybind11/tests/test_callbacks.cpp create mode 100644 external/pybind11/tests/test_callbacks.py create mode 100644 external/pybind11/tests/test_chrono.cpp create mode 100644 external/pybind11/tests/test_chrono.py create mode 100644 external/pybind11/tests/test_class.cpp create mode 100644 external/pybind11/tests/test_class.py create mode 100644 external/pybind11/tests/test_cmake_build/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/embed.cpp create mode 100644 external/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/main.cpp create mode 100644 external/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt create mode 100644 external/pybind11/tests/test_cmake_build/test.py create mode 100644 external/pybind11/tests/test_constants_and_functions.cpp create mode 100644 external/pybind11/tests/test_constants_and_functions.py create mode 100644 external/pybind11/tests/test_copy_move.cpp create mode 100644 external/pybind11/tests/test_copy_move.py create mode 100644 external/pybind11/tests/test_docstring_options.cpp create mode 100644 external/pybind11/tests/test_docstring_options.py create mode 100644 external/pybind11/tests/test_eigen.cpp create mode 100644 external/pybind11/tests/test_eigen.py create mode 100644 external/pybind11/tests/test_embed/CMakeLists.txt create mode 100644 external/pybind11/tests/test_embed/catch.cpp create mode 100644 external/pybind11/tests/test_embed/test_interpreter.cpp create mode 100644 external/pybind11/tests/test_embed/test_interpreter.py create mode 100644 external/pybind11/tests/test_enum.cpp create mode 100644 external/pybind11/tests/test_enum.py create mode 100644 external/pybind11/tests/test_eval.cpp create mode 100644 external/pybind11/tests/test_eval.py create mode 100644 external/pybind11/tests/test_eval_call.py create mode 100644 external/pybind11/tests/test_exceptions.cpp create mode 100644 external/pybind11/tests/test_exceptions.py create mode 100644 external/pybind11/tests/test_factory_constructors.cpp create mode 100644 external/pybind11/tests/test_factory_constructors.py create mode 100644 external/pybind11/tests/test_iostream.cpp create mode 100644 external/pybind11/tests/test_iostream.py create mode 100644 external/pybind11/tests/test_kwargs_and_defaults.cpp create mode 100644 external/pybind11/tests/test_kwargs_and_defaults.py create mode 100644 external/pybind11/tests/test_local_bindings.cpp create mode 100644 external/pybind11/tests/test_local_bindings.py create mode 100644 external/pybind11/tests/test_methods_and_attributes.cpp create mode 100644 external/pybind11/tests/test_methods_and_attributes.py create mode 100644 external/pybind11/tests/test_modules.cpp create mode 100644 external/pybind11/tests/test_modules.py create mode 100644 external/pybind11/tests/test_multiple_inheritance.cpp create mode 100644 external/pybind11/tests/test_multiple_inheritance.py create mode 100644 external/pybind11/tests/test_numpy_array.cpp create mode 100644 external/pybind11/tests/test_numpy_array.py create mode 100644 external/pybind11/tests/test_numpy_dtypes.cpp create mode 100644 external/pybind11/tests/test_numpy_dtypes.py create mode 100644 external/pybind11/tests/test_numpy_vectorize.cpp create mode 100644 external/pybind11/tests/test_numpy_vectorize.py create mode 100644 external/pybind11/tests/test_opaque_types.cpp create mode 100644 external/pybind11/tests/test_opaque_types.py create mode 100644 external/pybind11/tests/test_operator_overloading.cpp create mode 100644 external/pybind11/tests/test_operator_overloading.py create mode 100644 external/pybind11/tests/test_pickling.cpp create mode 100644 external/pybind11/tests/test_pickling.py create mode 100644 external/pybind11/tests/test_pytypes.cpp create mode 100644 external/pybind11/tests/test_pytypes.py create mode 100644 external/pybind11/tests/test_sequences_and_iterators.cpp create mode 100644 external/pybind11/tests/test_sequences_and_iterators.py create mode 100644 external/pybind11/tests/test_smart_ptr.cpp create mode 100644 external/pybind11/tests/test_smart_ptr.py create mode 100644 external/pybind11/tests/test_stl.cpp create mode 100644 external/pybind11/tests/test_stl.py create mode 100644 external/pybind11/tests/test_stl_binders.cpp create mode 100644 external/pybind11/tests/test_stl_binders.py create mode 100644 external/pybind11/tests/test_virtual_functions.cpp create mode 100644 external/pybind11/tests/test_virtual_functions.py create mode 100644 external/pybind11/tools/FindCatch.cmake create mode 100644 external/pybind11/tools/FindEigen3.cmake create mode 100644 external/pybind11/tools/FindPythonLibsNew.cmake create mode 100755 external/pybind11/tools/check-style.sh create mode 100644 external/pybind11/tools/libsize.py create mode 100644 external/pybind11/tools/mkdoc.py create mode 100644 external/pybind11/tools/pybind11Config.cmake.in create mode 100644 external/pybind11/tools/pybind11Tools.cmake create mode 100644 include/pangolin/compat/glconsole.h create mode 100644 include/pangolin/compat/glutbitmap.h create mode 100644 include/pangolin/compat/ovr.h create mode 100644 include/pangolin/compat/type_traits.h create mode 100644 include/pangolin/config.h create mode 100644 include/pangolin/console/ConsoleInterpreter.h create mode 100644 include/pangolin/console/ConsoleView.h create mode 100644 include/pangolin/display/attach.h create mode 100644 include/pangolin/display/device/OsxWindow.h create mode 100644 include/pangolin/display/device/PangolinNSApplication.h create mode 100644 include/pangolin/display/device/PangolinNSGLView.h create mode 100644 include/pangolin/display/device/WinWindow.h create mode 100644 include/pangolin/display/device/X11GlContext.h create mode 100644 include/pangolin/display/device/X11Window.h create mode 100644 include/pangolin/display/device/display_android.h create mode 100644 include/pangolin/display/device/display_glut.h create mode 100644 include/pangolin/display/display.h create mode 100644 include/pangolin/display/display_internal.h create mode 100644 include/pangolin/display/image_view.h create mode 100644 include/pangolin/display/opengl_render_state.h create mode 100644 include/pangolin/display/user_app.h create mode 100644 include/pangolin/display/view.h create mode 100644 include/pangolin/display/viewport.h create mode 100644 include/pangolin/display/widgets/widgets.h create mode 100644 include/pangolin/display/window.h create mode 100644 include/pangolin/factory/factory_registry.h create mode 100644 include/pangolin/gl/cg.h create mode 100644 include/pangolin/gl/colour.h create mode 100644 include/pangolin/gl/compat/gl2engine.h create mode 100644 include/pangolin/gl/compat/gl_es_compat.h create mode 100644 include/pangolin/gl/gl.h create mode 100644 include/pangolin/gl/gl.hpp create mode 100644 include/pangolin/gl/glchar.h create mode 100644 include/pangolin/gl/glcuda.h create mode 100644 include/pangolin/gl/gldraw.h create mode 100644 include/pangolin/gl/glfont.h create mode 100644 include/pangolin/gl/glformattraits.h create mode 100644 include/pangolin/gl/glglut.h create mode 100644 include/pangolin/gl/glinclude.h create mode 100644 include/pangolin/gl/glpangoglu.h create mode 100644 include/pangolin/gl/glpixformat.h create mode 100644 include/pangolin/gl/glplatform.h create mode 100644 include/pangolin/gl/glsl.h create mode 100644 include/pangolin/gl/glstate.h create mode 100644 include/pangolin/gl/gltext.h create mode 100644 include/pangolin/gl/gltexturecache.h create mode 100644 include/pangolin/gl/glvbo.h create mode 100644 include/pangolin/handler/handler.h create mode 100644 include/pangolin/handler/handler_enums.h create mode 100644 include/pangolin/handler/handler_glbuffer.h create mode 100644 include/pangolin/handler/handler_image.h create mode 100644 include/pangolin/hud/oculus_hud.h create mode 100644 include/pangolin/image/copy.h create mode 100644 include/pangolin/image/image.h create mode 100644 include/pangolin/image/image_convert.h create mode 100644 include/pangolin/image/image_io.h create mode 100644 include/pangolin/image/image_utils.h create mode 100644 include/pangolin/image/managed_image.h create mode 100644 include/pangolin/image/memcpy.h create mode 100644 include/pangolin/image/pixel_format.h create mode 100644 include/pangolin/image/typed_image.h create mode 100644 include/pangolin/ios/PangolinAppDelegate.h create mode 100644 include/pangolin/ios/PangolinUIView.h create mode 100644 include/pangolin/log/packet.h create mode 100644 include/pangolin/log/packetstream.h create mode 100644 include/pangolin/log/packetstream_reader.h create mode 100644 include/pangolin/log/packetstream_source.h create mode 100644 include/pangolin/log/packetstream_tags.h create mode 100644 include/pangolin/log/packetstream_writer.h create mode 100644 include/pangolin/log/playback_session.h create mode 100644 include/pangolin/log/sync_time.h create mode 100644 include/pangolin/pangolin.h create mode 100644 include/pangolin/platform.h create mode 100644 include/pangolin/plot/datalog.h create mode 100644 include/pangolin/plot/plotter.h create mode 100644 include/pangolin/plot/range.h create mode 100644 include/pangolin/python/PyInterpreter.h create mode 100644 include/pangolin/python/PyModulePangolin.h create mode 100644 include/pangolin/python/PyPangoIO.h create mode 100644 include/pangolin/python/PyUniqueObj.h create mode 100644 include/pangolin/python/PyVar.h create mode 100644 include/pangolin/scene/axis.h create mode 100644 include/pangolin/scene/interactive.h create mode 100644 include/pangolin/scene/interactive_index.h create mode 100644 include/pangolin/scene/renderable.h create mode 100644 include/pangolin/scene/scenehandler.h create mode 100644 include/pangolin/tools/video_viewer.h create mode 100644 include/pangolin/utils/argagg.hpp create mode 100644 include/pangolin/utils/assert.h create mode 100644 include/pangolin/utils/compontent_cast.h create mode 100644 include/pangolin/utils/file_extension.h create mode 100644 include/pangolin/utils/file_utils.h create mode 100644 include/pangolin/utils/fix_size_buffer_queue.h create mode 100644 include/pangolin/utils/format_string.h create mode 100644 include/pangolin/utils/log.h create mode 100644 include/pangolin/utils/memstreambuf.h create mode 100644 include/pangolin/utils/params.h create mode 100644 include/pangolin/utils/picojson.h create mode 100644 include/pangolin/utils/posix/condition_variable.h create mode 100644 include/pangolin/utils/posix/semaphore.h create mode 100644 include/pangolin/utils/posix/shared_memory_buffer.h create mode 100644 include/pangolin/utils/registration.h create mode 100644 include/pangolin/utils/signal_slot.h create mode 100644 include/pangolin/utils/sigstate.h create mode 100644 include/pangolin/utils/simple_math.h create mode 100644 include/pangolin/utils/threadedfilebuf.h create mode 100644 include/pangolin/utils/timer.h create mode 100644 include/pangolin/utils/transform.h create mode 100644 include/pangolin/utils/type_convert.h create mode 100644 include/pangolin/utils/uri.h create mode 100755 include/pangolin/utils/xml/license.txt create mode 100755 include/pangolin/utils/xml/rapidxml.hpp create mode 100755 include/pangolin/utils/xml/rapidxml_iterators.hpp create mode 100755 include/pangolin/utils/xml/rapidxml_print.hpp create mode 100755 include/pangolin/utils/xml/rapidxml_utils.hpp create mode 100644 include/pangolin/var/input_record_repeat.h create mode 100644 include/pangolin/var/var.h create mode 100644 include/pangolin/var/varextra.h create mode 100644 include/pangolin/var/varstate.h create mode 100644 include/pangolin/var/varvalue.h create mode 100644 include/pangolin/var/varvaluegeneric.h create mode 100644 include/pangolin/var/varvaluet.h create mode 100644 include/pangolin/var/varwrapper.h create mode 100644 include/pangolin/video/drivers/debayer.h create mode 100644 include/pangolin/video/drivers/deinterlace.h create mode 100644 include/pangolin/video/drivers/depthsense.h create mode 100644 include/pangolin/video/drivers/ffmpeg.h create mode 100644 include/pangolin/video/drivers/firewire.h create mode 100644 include/pangolin/video/drivers/images.h create mode 100644 include/pangolin/video/drivers/images_out.h create mode 100644 include/pangolin/video/drivers/join.h create mode 100644 include/pangolin/video/drivers/merge.h create mode 100644 include/pangolin/video/drivers/mirror.h create mode 100644 include/pangolin/video/drivers/openni.h create mode 100644 include/pangolin/video/drivers/openni2.h create mode 100644 include/pangolin/video/drivers/openni_common.h create mode 100644 include/pangolin/video/drivers/pango.h create mode 100644 include/pangolin/video/drivers/pango_video_output.h create mode 100644 include/pangolin/video/drivers/pleora.h create mode 100644 include/pangolin/video/drivers/pvn.h create mode 100644 include/pangolin/video/drivers/realsense.h create mode 100644 include/pangolin/video/drivers/shared_memory.h create mode 100644 include/pangolin/video/drivers/shift.h create mode 100644 include/pangolin/video/drivers/split.h create mode 100644 include/pangolin/video/drivers/teli.h create mode 100644 include/pangolin/video/drivers/test.h create mode 100644 include/pangolin/video/drivers/thread.h create mode 100644 include/pangolin/video/drivers/unpack.h create mode 100644 include/pangolin/video/drivers/uvc.h create mode 100644 include/pangolin/video/drivers/uvc_mediafoundation.h create mode 100644 include/pangolin/video/drivers/v4l.h create mode 100644 include/pangolin/video/iostream_operators.h create mode 100644 include/pangolin/video/stream_encoder_factory.h create mode 100644 include/pangolin/video/stream_info.h create mode 100644 include/pangolin/video/video.h create mode 100644 include/pangolin/video/video_exception.h create mode 100644 include/pangolin/video/video_input.h create mode 100644 include/pangolin/video/video_interface.h create mode 100644 include/pangolin/video/video_output.h create mode 100644 include/pangolin/video/video_output_interface.h create mode 100644 include/pangolin/video/video_record_repeat.h create mode 100644 python/CMakeLists.txt create mode 100644 python/blank.hpp create mode 100644 python/contrib.hpp create mode 100644 python/display/attach.hpp create mode 100644 python/display/display.hpp create mode 100644 python/display/image_view.hpp create mode 100644 python/display/opengl_render_state.hpp create mode 100644 python/display/user_app.hpp create mode 100644 python/display/view.hpp create mode 100644 python/display/viewport.hpp create mode 100644 python/display/widgets/widgets.hpp create mode 100644 python/display/window.hpp create mode 100644 python/examples/HelloPangolin.py create mode 100644 python/examples/SimpleDisplayImage.py create mode 100644 python/examples/SimpleDisplayMenu.py create mode 100644 python/examples/SimpleMultiDisplay.py create mode 100644 python/examples/SimplePlot.py create mode 100644 python/examples/SimplePlotDisplay.py create mode 100644 python/examples/SimpleScene.py create mode 100644 python/examples/app.cfg create mode 100644 python/examples/imgs/HelloPangolin.png create mode 100644 python/examples/imgs/HelloPangolinColorful.png create mode 100644 python/examples/imgs/SPTAM_KITTI00.png create mode 100644 python/examples/imgs/SPTAM_PointCloud.png create mode 100644 python/examples/imgs/SimpleDisplayImage.png create mode 100644 python/examples/imgs/SimpleDisplayMenu.png create mode 100644 python/examples/imgs/SimpleMultiDisplay.png create mode 100644 python/examples/imgs/SimplePlot.png create mode 100644 python/examples/imgs/SimplePlotDisplay.png create mode 100644 python/examples/imgs/SimpleScene.png create mode 100644 python/examples/imgs/simple_draw.png create mode 100644 python/examples/simple_draw.py create mode 100644 python/gl/colour.hpp create mode 100644 python/gl/gl.hpp create mode 100644 python/gl/gldraw.hpp create mode 100644 python/handler/blank.hpp create mode 100644 python/handler/handler.hpp create mode 100644 python/handler/handler_enums.hpp create mode 100644 python/handler/handler_image.hpp create mode 100644 python/pangolin.cpp create mode 100644 python/plot/datalog.hpp create mode 100644 python/plot/plotter.hpp create mode 100644 python/plot/range.hpp create mode 100644 python/scene/scene.hpp create mode 100644 python/utils/params.hpp create mode 100644 python/var/var.hpp create mode 100644 python/var/varextra.hpp create mode 100644 python/var/varvalue.hpp create mode 100644 python/var/varvaluegeneric.hpp create mode 100644 python/var/varvaluet.hpp create mode 100644 setup.py create mode 100644 src/CMakeLists.txt create mode 100644 src/Doxyfile.in create mode 100644 src/PangolinConfig.cmake.in create mode 100644 src/PangolinConfigVersion.cmake.in create mode 100644 src/_embed_/fonts/AnonymousPro.ttf create mode 100644 src/_embed_/fonts/AnonymousPro.txt create mode 100644 src/config.h.in create mode 100644 src/console/ConsoleView.cpp create mode 100644 src/display/device/PangolinNSApplication.mm create mode 100644 src/display/device/PangolinNSGLView.mm create mode 100644 src/display/device/display_android.cpp create mode 100644 src/display/device/display_glut.cpp create mode 100644 src/display/device/display_osx.mm create mode 100644 src/display/device/display_win.cpp create mode 100644 src/display/device/display_x11.cpp create mode 100644 src/display/display.cpp create mode 100644 src/display/image_view.cpp create mode 100644 src/display/opengl_render_state.cpp create mode 100644 src/display/view.cpp create mode 100644 src/display/viewport.cpp create mode 100644 src/display/widgets/widgets.cpp create mode 100644 src/gl/compat/gl2engine.cpp create mode 100644 src/gl/glchar.cpp create mode 100644 src/gl/gldraw.cpp create mode 100644 src/gl/glfont.cpp create mode 100644 src/gl/glpangoglu.cpp create mode 100644 src/gl/gltext.cpp create mode 100644 src/gl/gltexturecache.cpp create mode 100644 src/gl/stb_truetype.h create mode 100644 src/handler/handler.cpp create mode 100644 src/handler/handler_glbuffer.cpp create mode 100644 src/handler/handler_image.cpp create mode 100644 src/hud/oculus_hud.cpp create mode 100644 src/image/image_io.cpp create mode 100644 src/image/image_io_exr.cpp create mode 100644 src/image/image_io_jpg.cpp create mode 100644 src/image/image_io_pango.cpp create mode 100644 src/image/image_io_png.cpp create mode 100644 src/image/image_io_ppm.cpp create mode 100644 src/image/image_io_raw.cpp create mode 100644 src/image/image_io_tga.cpp create mode 100644 src/image/image_io_zstd.cpp create mode 100644 src/image/pixel_format.cpp create mode 100644 src/ios/PangolinAppDelegate.mm create mode 100644 src/ios/PangolinUIView.mm create mode 100644 src/log/packet.cpp create mode 100644 src/log/packetstream.cpp create mode 100644 src/log/packetstream_reader.cpp create mode 100644 src/log/packetstream_writer.cpp create mode 100644 src/log/playback_session.cpp create mode 100644 src/plot/datalog.cpp create mode 100644 src/plot/plotter.cpp create mode 100644 src/python/PyInterpreter.cpp create mode 100644 src/python/PyModulePangolin.cpp create mode 100644 src/tools/video_viewer.cpp create mode 100644 src/utils/file_extension.cpp create mode 100644 src/utils/file_utils.cpp create mode 100644 src/utils/posix/condition_variable.cpp create mode 100644 src/utils/posix/semaphore.cpp create mode 100644 src/utils/posix/shared_memory_buffer.cpp create mode 100644 src/utils/sigstate.cpp create mode 100644 src/utils/threadedfilebuf.cpp create mode 100644 src/utils/timer.cpp create mode 100644 src/utils/uri.cpp create mode 100644 src/var/input_record_repeat.cpp create mode 100644 src/var/vars.cpp create mode 100644 src/video/drivers/debayer.cpp create mode 100644 src/video/drivers/deinterlace.cpp create mode 100644 src/video/drivers/depthsense.cpp create mode 100644 src/video/drivers/ffmpeg.cpp create mode 100644 src/video/drivers/firewire.cpp create mode 100644 src/video/drivers/images.cpp create mode 100644 src/video/drivers/images_out.cpp create mode 100644 src/video/drivers/join.cpp create mode 100644 src/video/drivers/json.cpp create mode 100644 src/video/drivers/merge.cpp create mode 100644 src/video/drivers/mirror.cpp create mode 100644 src/video/drivers/openni.cpp create mode 100644 src/video/drivers/openni2.cpp create mode 100644 src/video/drivers/pango.cpp create mode 100644 src/video/drivers/pango_video_output.cpp create mode 100644 src/video/drivers/pleora.cpp create mode 100644 src/video/drivers/pvn.cpp create mode 100644 src/video/drivers/realsense.cpp create mode 100644 src/video/drivers/shared_memory.cpp create mode 100644 src/video/drivers/shift.cpp create mode 100644 src/video/drivers/split.cpp create mode 100644 src/video/drivers/teli.cpp create mode 100644 src/video/drivers/test.cpp create mode 100644 src/video/drivers/thread.cpp create mode 100644 src/video/drivers/unpack.cpp create mode 100644 src/video/drivers/uvc.cpp create mode 100644 src/video/drivers/uvc_mediafoundation.cpp create mode 100644 src/video/drivers/v4l.cpp create mode 100644 src/video/stream_encoder_factory.cpp create mode 100644 src/video/video.cpp create mode 100644 src/video/video_input.cpp create mode 100644 src/video/video_interface_factory.cpp create mode 100644 src/video/video_output.cpp create mode 100644 src/video/video_output_interface_factory.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/log/CMakeLists.txt create mode 100644 test/log/testlog.cpp create mode 100644 tools/CMakeLists.txt create mode 100644 tools/Plotter/CMakeLists.txt create mode 100644 tools/Plotter/application-x-pangoplot.xml create mode 100644 tools/Plotter/csv_data_loader.h create mode 100644 tools/Plotter/main.cpp create mode 100644 tools/VideoConvert/CMakeLists.txt create mode 100644 tools/VideoConvert/main.cpp create mode 100644 tools/VideoJson/CMakeLists.txt create mode 100644 tools/VideoJson/main-print.cpp create mode 100644 tools/VideoJson/main-transform.cpp create mode 100644 tools/VideoViewer/CMakeLists.txt create mode 100644 tools/VideoViewer/application-x-pango.svg create mode 100644 tools/VideoViewer/application-x-pango.xml create mode 100644 tools/VideoViewer/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4c66785 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,104 @@ +cmake_minimum_required(VERSION 2.6) +project("Pangolin") +set(PANGOLIN_VERSION_MAJOR 0) +set(PANGOLIN_VERSION_MINOR 5) +set(PANGOLIN_VERSION ${PANGOLIN_VERSION_MAJOR}.${PANGOLIN_VERSION_MINOR}) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/") + +# Platform configuration vars +include(SetPlatformVars) + +SET(CPACK_GENERATOR "DEB") +SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Steven Lovegrove") +SET(CPACK_PACKAGE_VERSION_MAJOR ${PANGOLIN_VERSION_MAJOR}) +SET(CPACK_PACKAGE_VERSION_MINOR ${PANGOLIN_VERSION_MINOR}) +SET(CPACK_PACKAGE_VERSION_PATCH "0") +include(CPack) + +option( BUILD_EXAMPLES "Build Examples" ON ) +option( BUILD_TESTS "Build Tests" ON ) + +if(_WIN_) + option( BUILD_SHARED_LIBS "Build Shared Library" OFF) + option( BUILD_EXTERN_GLEW "Automatically download, build and compile GLEW" ON) + option( BUILD_EXTERN_LIBPNG "Automatically download, build and compile libpng" ON) + option( BUILD_EXTERN_LIBJPEG "Automatically download, build and compile libjpeg" ON) + option( MSVC_USE_STATIC_CRT "Use static C Runtime with MSVC, /MT instead of /MD" ON) + + # Make sure there are no erroneous C Runtime flags + list(APPEND FLAG_VARS + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + ) + if(MSVC_USE_STATIC_CRT) + foreach(FLAG_VAR ${FLAG_VARS}) + string(REGEX REPLACE "/MD" "/MT" NEW_FLAGS "${${FLAG_VAR}}") + set(${FLAG_VAR} "${NEW_FLAGS}" CACHE STRING "" FORCE) + endforeach() + else() + foreach(FLAG_VAR ${FLAG_VARS}) + string(REGEX REPLACE "/MT" "/MD" NEW_FLAGS "${${FLAG_VAR}}") + set(${FLAG_VAR} "${NEW_FLAGS}" CACHE STRING "" FORCE) + endforeach() + endif() +else() + option( BUILD_SHARED_LIBS "Build Shared Library" ON) +endif() + +if(NOT MSVC) + set( CMAKE_CXX_FLAGS "-std=c++14 -Wall -Wextra ${CMAKE_CXX_FLAGS} -fPIC" ) # modified for python binding (add " -fPIC") + if(_CLANG_) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fPIC") # modified for python binding (add " -fPIC") + endif() +endif() + +if(ANDROID) + set(ANDROID_PACKAGE_NAME "com.github.stevenlovegrove.pangolin") + include(AndroidUtils) +endif() + +if(ANDROID OR IOS) + set(HAVE_GLES 1) + option(BUILD_FOR_GLES_2 "Build for OpenGL ES 2 instead of ES 1" ON ) + if(BUILD_FOR_GLES_2) + set(HAVE_GLES_2 1) + endif() +endif() + +if(_OSX_) + set(CMAKE_MACOSX_RPATH ON) +endif() + +# Overide with cmake -DCMAKE_BUILD_TYPE=Debug {dir} +if( NOT CMAKE_BUILD_TYPE AND NOT _WIN_ ) + message("Build type not set (defaults to release)") + message("-DCMAKE_BUILD_TYPE=Debug for debug") + set( CMAKE_BUILD_TYPE Release ) +endif() + +string(TOLOWER _${PROJECT_NAME} LIBRARY_NAME) # modified for python binding (add "_" to avoid naming conflict with python library "pangolin") + +# make an uninstall target +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY +) + +add_custom_target(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + +add_subdirectory("external") +add_subdirectory("src") +add_subdirectory("python") + +if(BUILD_TESTS) + add_subdirectory("test") +endif() + +if(BUILD_EXAMPLES) + set(Pangolin_DIR ${Pangolin_BINARY_DIR}/src) + add_subdirectory(examples) + add_subdirectory(tools) +endif() diff --git a/CMakeModules/AndroidUtils.cmake b/CMakeModules/AndroidUtils.cmake new file mode 100644 index 0000000..cfb13dc --- /dev/null +++ b/CMakeModules/AndroidUtils.cmake @@ -0,0 +1,245 @@ +if(NOT ANDROID_PACKAGE_NAME) + set(ANDROID_PACKAGE_NAME "com.github.stevenlovegrove.pangolin") +endif() + +if(NOT ANDROID_DEFERRED_ENTRY_SO) + set(ANDROID_DEFERRED_ENTRY_SO "libpangolin.so") +endif() + +# Configure build environment to automatically generate APK's instead of executables. +if(ANDROID AND NOT TARGET apk) + # virtual targets which we'll add apks and push actions to. + add_custom_target( apk ) + add_custom_target( push ) + add_custom_target( run ) + + # Reset output directories to be in binary folder (rather than source) + set(LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_CURRENT_BINARY_DIR}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}) + + macro( create_android_manifest_xml filename prog_name package_name activity_name) + file( WRITE ${filename} +" + + + + + + + + + + + + + + + + + + + + + + + + + + +" ) + endmacro() + + macro( create_bootstrap_library prog_name package_name) + set(bootstrap_cpp "${CMAKE_CURRENT_BINARY_DIR}/${prog_name}_start.cpp" ) + file( WRITE ${bootstrap_cpp} +"#include +#include +#include +#include +#include +#include + +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, \"AndroidUtils.cmake\", __VA_ARGS__)) +#define LIB_PATH \"/data/data/${package_name}.${prog_name}/lib/\" + +void * load_lib(const char * l) { + void * handle = dlopen(l, RTLD_NOW | RTLD_GLOBAL); + if (!handle) LOGE( \"dlopen('%s'): %s\", l, strerror(errno) ); + return handle; +} + +void ANativeActivity_onCreate(ANativeActivity * app, void * ud, size_t udsize) { + #include \"${prog_name}_shared_load.h\" + + // Look for standard entrypoint in user lib + void (*stdentrypoint)(ANativeActivity*, void*, size_t); + *(void **) (&stdentrypoint) = dlsym(load_lib( LIB_PATH \"lib${prog_name}.so\"), \"ANativeActivity_onCreate\"); + if (stdentrypoint) { + (*stdentrypoint)(app, ud, udsize); + }else{ + // Look for deferred load entry point + void (*exdentrypoint)(ANativeActivity*, void*, size_t, const char*); + *(void **) (&exdentrypoint) = dlsym(load_lib( LIB_PATH \"lib${prog_name}.so\"), \"DeferredNativeActivity_onCreate\"); + if (!exdentrypoint) { + // Look in specific shared lib + *(void **) (&exdentrypoint) = dlsym(load_lib( LIB_PATH \"${ANDROID_DEFERRED_ENTRY_SO}\"), \"DeferredNativeActivity_onCreate\"); + } + if(exdentrypoint) { + (*exdentrypoint)(app, ud, udsize, LIB_PATH \"lib${prog_name}.so\" ); + }else{ + LOGE( \"Unable to find compatible entry point\" ); + } + } +}" ) + add_library( "${prog_name}_start" SHARED ${bootstrap_cpp} ) + target_link_libraries( "${prog_name}_start" android log ) + add_dependencies( ${prog_name} "${prog_name}_start" ) + endmacro() + + macro( android_update android_project_name) + # Find which android platforms are available. + execute_process( + COMMAND android list targets -c + OUTPUT_VARIABLE android_target_list + ) + + # Pick first platform from this list. + string(REGEX MATCH "^[^\n]+" android_target "${android_target_list}" ) + message(STATUS "Android Target: ${android_target}") + + if( NOT "${android_target}" STREQUAL "" ) + # Generate ant build scripts for making APK + execute_process( + COMMAND android update project --name ${android_project_name} --path . --target ${android_target} --subprojects + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + else() + message( FATAL_ERROR "No Android SDK platforms found. Please install an Android platform SDK. On Linux, run 'android'." ) + endif() + endmacro() + + # Override add_executable to build android .so instead! + macro( add_executable prog_name) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}) + add_library( ${prog_name} SHARED ${ARGN} ) + + # Add required link libs for android + target_link_libraries(${prog_name} log android ) + + # Create manifest required for APK + create_android_manifest_xml( + "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" "${prog_name}" + "${ANDROID_PACKAGE_NAME}" "${prog_name}" + ) + + # Create library that will launch this program and load shared libs + create_bootstrap_library( ${prog_name} ${ANDROID_PACKAGE_NAME} ) + + # Generate ant build system for APK + android_update( ${prog_name} ) + + # Target to invoke ant build system for APK + set( APK_FILE "${CMAKE_CURRENT_BINARY_DIR}/bin/${prog_name}-debug.apk" ) + add_custom_command( + OUTPUT ${APK_FILE} + COMMAND ant debug + DEPENDS ${prog_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + + # Target to install on device + add_custom_target( ${prog_name}-apk + DEPENDS ${APK_FILE} + ) + add_dependencies(apk ${prog_name}-apk) + + # Target to install on device + add_custom_target( ${prog_name}-push + COMMAND adb install -r ${APK_FILE} + DEPENDS ${APK_FILE} + ) + add_dependencies(push ${prog_name}-push) + + # install and run on device + add_custom_target( ${prog_name}-run + COMMAND adb shell am start -n ${ANDROID_PACKAGE_NAME}.${prog_name}/android.app.NativeActivity + DEPENDS ${prog_name}-push + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + add_dependencies(run ${prog_name}-run) + + # Flag to package dependent libs + set_property(TARGET ${prog_name} APPEND PROPERTY MAKE_APK 1 ) + + # Clear shared library loading header + file( WRITE "${CMAKE_CURRENT_BINARY_DIR}/${prog_name}_shared_load.h" "") + endmacro() + + macro( package_with_target prog_name lib_path ) + # Mark lib_path as dependent of prog_name + set_property(TARGET ${prog_name} APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE ${lib_path} ) + + # If prog_name is to be packaged, add file copy command to package .so's. + get_target_property( package_dependent_libs ${prog_name} MAKE_APK ) + if( package_dependent_libs ) + get_filename_component(target_filename ${lib_path} NAME) + file( APPEND ${depend_file} "load_lib(LIB_PATH \"${target_filename}\" );\n") + add_custom_command(TARGET ${prog_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${lib_path} "${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}/" + ) + endif() + endmacro() + + macro( add_to_depend_libs prog_name depend_file lib_name ) + # Recursively Process dependents of lib_name + get_target_property(TARGET_LIBS ${lib_name} IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE) + if(NOT TARGET_LIBS) + get_target_property(TARGET_LIBS ${lib_name} IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG) + endif() + if(NOT TARGET_LIBS) + get_target_property(TARGET_LIBS ${lib_name} IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG) + endif() + + foreach(SUBLIB ${TARGET_LIBS}) + if(SUBLIB) + add_to_depend_libs( ${prog_name} ${depend_file} ${SUBLIB} ) + endif() + endforeach() + + # Check if lib itself is an external shared library + if("${lib_name}" MATCHES "\\.so$") + package_with_target( ${prog_name} ${lib_name} ) + endif() + + # Check if lib itself is an internal shared library + get_target_property(TARGET_LIB ${lib_name} LOCATION) + if("${TARGET_LIB}" MATCHES "\\.so$") + package_with_target( ${prog_name} ${TARGET_LIB} ) + endif() + endmacro() + + macro( target_link_libraries prog_name) + # _target_link_libraries corresponds to original + _target_link_libraries( ${prog_name} ${ARGN} ) + + # Recursively process dependencies + set(depend_file "${CMAKE_CURRENT_BINARY_DIR}/${prog_name}_shared_load.h" ) + foreach( LIB ${ARGN} ) + add_to_depend_libs( ${prog_name} ${depend_file} ${LIB} ) + endforeach() + endmacro() + +endif() diff --git a/CMakeModules/CreateMethodCallFile.cmake b/CMakeModules/CreateMethodCallFile.cmake new file mode 100644 index 0000000..c1ae95b --- /dev/null +++ b/CMakeModules/CreateMethodCallFile.cmake @@ -0,0 +1,11 @@ +macro( CreateMethodCallFile filename namespace function symbols) + file(WRITE ${filename} "// CMake generated file. Do Not Edit.\n\n#pragma once\n\nnamespace ${namespace} {\n\n") + foreach( symbol ${symbols} ) + file(APPEND ${filename} "void ${symbol}();\n") + endforeach() + file(APPEND ${filename} "\ninline bool ${function}()\n{\n") + foreach( symbol ${symbols} ) + file(APPEND ${filename} " ${symbol}();\n") + endforeach() + file(APPEND ${filename} " return true;\n}\n\n} // ${namespace}\n") +endmacro() diff --git a/CMakeModules/EmbedBinaryFiles.cmake b/CMakeModules/EmbedBinaryFiles.cmake new file mode 100644 index 0000000..d595165 --- /dev/null +++ b/CMakeModules/EmbedBinaryFiles.cmake @@ -0,0 +1,32 @@ +# Creates C resources file from files in given directory +# Based on http://stackoverflow.com/a/27206982 +function(embed_binary_files file_glob output) + # Collect input files + file(GLOB bins ${file_glob}) + # Stop when output file is newer than all binary files + set(output_newer_than_bins 1) + foreach(bin ${bins}) + if(bin IS_NEWER_THAN output) + set(output_newer_than_bins 0) + break() + endif() + endforeach() + if(output_newer_than_bins) + return() + endif() + # Create empty output file + file(WRITE ${output} "") + # Iterate through input files + foreach(bin ${bins}) + # Get short filename + string(REGEX MATCH "([^/]+)$" filename ${bin}) + # Replace filename spaces & extension separator for C compatibility + string(REGEX REPLACE "\\.| " "_" filename ${filename}) + # Read hex data from file + file(READ ${bin} filedata HEX) + # Convert hex data for C compatibility + string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," filedata ${filedata}) + # Append data to output file + file(APPEND ${output} "const unsigned char ${filename}[] = {${filedata}};\nconst unsigned ${filename}_size = sizeof(${filename});\n") + endforeach() +endfunction() diff --git a/CMakeModules/FindDC1394.cmake b/CMakeModules/FindDC1394.cmake new file mode 100644 index 0000000..1dbce01 --- /dev/null +++ b/CMakeModules/FindDC1394.cmake @@ -0,0 +1,32 @@ +# Try to find the dc1394 v2 lib and include files +# +# DC1394_INCLUDE_DIR +# DC1394_LIBRARIES +# DC1394_FOUND + +FIND_PATH( DC1394_INCLUDE_DIR dc1394/control.h + /usr/include + /usr/local/include +) + +FIND_LIBRARY( DC1394_LIBRARY dc1394 + /usr/lib64 + /usr/lib + /usr/local/lib +) + +IF(DC1394_INCLUDE_DIR AND DC1394_LIBRARY) + SET( DC1394_FOUND TRUE ) + SET( DC1394_LIBRARIES ${DC1394_LIBRARY} ) +ENDIF(DC1394_INCLUDE_DIR AND DC1394_LIBRARY) + +IF(DC1394_FOUND) + IF(NOT DC1394_FIND_QUIETLY) + MESSAGE(STATUS "Found DC1394: ${DC1394_LIBRARY}") + ENDIF(NOT DC1394_FIND_QUIETLY) +ELSE(DC1394_FOUND) + IF(DC1394_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find libdc1394") + ENDIF(DC1394_FIND_REQUIRED) +ENDIF(DC1394_FOUND) + diff --git a/CMakeModules/FindDepthSense.cmake b/CMakeModules/FindDepthSense.cmake new file mode 100644 index 0000000..ede0034 --- /dev/null +++ b/CMakeModules/FindDepthSense.cmake @@ -0,0 +1,43 @@ +# Try to find the DepthSense SDK For SoftKinetic Cameras +# +# DepthSense_INCLUDE_DIRS +# DepthSense_LIBRARIES +# DepthSense_FOUND + +FIND_PATH( DepthSense_INCLUDE_DIR DepthSense.hxx + PATHS + "${PROGRAM_FILES}/SoftKinetic/DepthSenseSDK/include" + "${PROGRAM_FILES}/Meta/DepthSenseSDK/include" + /usr/include + /usr/local/include + /opt/local/include + /opt/softkinetic/DepthSenseSDK/include +) + +FIND_LIBRARY( DepthSense_LIBRARY DepthSense + PATHS + "${PROGRAM_FILES}/SoftKinetic/DepthSenseSDK/lib" + "${PROGRAM_FILES}/Meta/DepthSenseSDK/lib" + /usr/lib64 + /usr/lib + /usr/local/lib + /opt/local/lib + /opt/softkinetic/DepthSenseSDK/lib +) + +IF(DepthSense_INCLUDE_DIR AND DepthSense_LIBRARY) + SET( DepthSense_FOUND TRUE ) + SET( DepthSense_LIBRARIES ${DepthSense_LIBRARY} ) + SET( DepthSense_INCLUDE_DIRS ${DepthSense_INCLUDE_DIR} ) +ENDIF() + +IF(DepthSense_FOUND) + IF(NOT DepthSense_FIND_QUIETLY) + MESSAGE(STATUS "Found DepthSense: ${DepthSense_LIBRARY}") + ENDIF() +ELSE() + IF(DepthSense_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find DepthSense") + ENDIF() +ENDIF() + diff --git a/CMakeModules/FindEigen.cmake b/CMakeModules/FindEigen.cmake new file mode 100644 index 0000000..1df56fd --- /dev/null +++ b/CMakeModules/FindEigen.cmake @@ -0,0 +1,83 @@ +# - Try to find Eigen lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen 3.1.2) +# to require version 3.1.2 or newer of Eigen. +# +# Once done this will define +# +# EIGEN_FOUND - system has eigen lib with correct version +# EIGEN_INCLUDE_DIR - the eigen include directory +# EIGEN_VERSION - eigen version + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen_FIND_VERSION) + if(NOT Eigen_FIND_VERSION_MAJOR) + set(Eigen_FIND_VERSION_MAJOR 2) + endif(NOT Eigen_FIND_VERSION_MAJOR) + if(NOT Eigen_FIND_VERSION_MINOR) + set(Eigen_FIND_VERSION_MINOR 91) + endif(NOT Eigen_FIND_VERSION_MINOR) + if(NOT Eigen_FIND_VERSION_PATCH) + set(Eigen_FIND_VERSION_PATCH 0) + endif(NOT Eigen_FIND_VERSION_PATCH) + + set(Eigen_FIND_VERSION "${Eigen_FIND_VERSION_MAJOR}.${Eigen_FIND_VERSION_MINOR}.${Eigen_FIND_VERSION_PATCH}") +endif(NOT Eigen_FIND_VERSION) + +macro(_eigen3_check_version) + file(READ "${EIGEN_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") + set(Eigen_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") + set(Eigen_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") + set(Eigen_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN_VERSION ${Eigen_WORLD_VERSION}.${Eigen_MAJOR_VERSION}.${Eigen_MINOR_VERSION}) + if(${EIGEN_VERSION} VERSION_LESS ${Eigen_FIND_VERSION}) + set(EIGEN_VERSION_OK FALSE) + else(${EIGEN_VERSION} VERSION_LESS ${Eigen_FIND_VERSION}) + set(EIGEN_VERSION_OK TRUE) + endif(${EIGEN_VERSION} VERSION_LESS ${Eigen_FIND_VERSION}) + + if(NOT EIGEN_VERSION_OK) + message(STATUS "Eigen version ${EIGEN_VERSION} found in ${EIGEN_INCLUDE_DIR}, " + "but at least version ${Eigen_FIND_VERSION} is required") + endif(NOT EIGEN_VERSION_OK) +endmacro(_eigen3_check_version) + +if (EIGEN_INCLUDE_DIR) + # in cache already + _eigen3_check_version() + set(EIGEN_FOUND ${EIGEN_VERSION_OK}) +else() + find_path(EIGEN_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + PATHS + third_party/eigen + ../eigen + ../../eigen + /usr/local/include + /opt/local/include + ${CMAKE_INSTALL_PREFIX}/include + ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen + ) + + if(EIGEN_INCLUDE_DIR) + _eigen3_check_version() + endif(EIGEN_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen DEFAULT_MSG EIGEN_INCLUDE_DIR EIGEN_VERSION_OK) + + mark_as_advanced(EIGEN_INCLUDE_DIR) +endif() + +# In case anyone relies on the plural form. +set(EIGEN_INCLUDE_DIRS "${EIGEN_INCLUDE_DIR}") diff --git a/CMakeModules/FindFFMPEG.cmake b/CMakeModules/FindFFMPEG.cmake new file mode 100644 index 0000000..4f77e5a --- /dev/null +++ b/CMakeModules/FindFFMPEG.cmake @@ -0,0 +1,88 @@ +# Try to find the ffmpeg libraries and headers for avcodec avformat swscale +# +# FFMPEG_INCLUDE_DIRS +# FFMPEG_LIBRARIES +# FFMPEG_FOUND + +# Find header files +FIND_PATH( + AVCODEC_INCLUDE_DIR libavcodec/avcodec.h + /usr/include /usr/local/include /opt/local/include +) +FIND_PATH( + AVFORMAT_INCLUDE_DIR libavformat/avformat.h + /usr/include /usr/local/include /opt/local/include +) +FIND_PATH( + AVUTIL_INCLUDE_DIR libavutil/avutil.h + /usr/include /usr/local/include /opt/local/include +) +FIND_PATH( + SWSCALE_INCLUDE_DIR libswscale/swscale.h + /usr/include /usr/local/include /opt/local/include +) + +# Find Library files +FIND_LIBRARY( + AVCODEC_LIBRARY + NAMES avcodec + PATH /usr/lib /usr/local/lib /opt/local/lib +) +FIND_LIBRARY( + AVFORMAT_LIBRARY + NAMES avformat + PATH /usr/lib /usr/local/lib /opt/local/lib +) +FIND_LIBRARY( + AVUTIL_LIBRARY + NAMES avutil + PATH /usr/lib /usr/local/lib /opt/local/lib +) +FIND_LIBRARY( + SWSCALE_LIBRARY + NAMES swscale + PATH /usr/lib /usr/local/lib /opt/local/lib +) + +IF( EXISTS "${AVUTIL_INCLUDE_DIR}/libavutil/pixdesc.h" ) + SET( AVUTIL_HAVE_PIXDESC TRUE) +endif() + +IF(AVCODEC_INCLUDE_DIR AND AVFORMAT_INCLUDE_DIR AND AVUTIL_INCLUDE_DIR AND SWSCALE_INCLUDE_DIR AND AVCODEC_LIBRARY AND AVFORMAT_LIBRARY AND AVUTIL_LIBRARY AND SWSCALE_LIBRARY AND AVUTIL_HAVE_PIXDESC) + SET(FFMPEG_FOUND TRUE) + SET(FFMPEG_LIBRARIES ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} ${AVUTIL_LIBRARY} ${SWSCALE_LIBRARY}) + SET(FFMPEG_INCLUDE_DIRS ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR} ${SWSCALE_INCLUDE_DIR}) + + include(CheckCXXSourceCompiles) + + SET(CMAKE_REQUIRED_INCLUDES ${FFMPEG_INCLUDE_DIRS}) + + CHECK_CXX_SOURCE_COMPILES( + "#include \"${AVCODEC_INCLUDE_DIR}/libavformat/avformat.h\" + int main() { + sizeof(AVFormatContext::max_analyze_duration2); + }" HAVE_FFMPEG_MAX_ANALYZE_DURATION2 + ) + CHECK_CXX_SOURCE_COMPILES( + "#include \"${AVCODEC_INCLUDE_DIR}/libavformat/avformat.h\" + int main() { + &avformat_alloc_output_context2; + }" HAVE_FFMPEG_AVFORMAT_ALLOC_OUTPUT_CONTEXT2 + ) + CHECK_CXX_SOURCE_COMPILES( + "#include \"${AVCODEC_INCLUDE_DIR}/libavutil/pixdesc.h\" + int main() { + AVPixelFormat test = AV_PIX_FMT_GRAY8; + }" HAVE_FFMPEG_AVPIXELFORMAT + ) +ENDIF() + +IF (FFMPEG_FOUND) + IF (NOT FFMPEG_FIND_QUIETLY) + MESSAGE(STATUS "Found FFMPEG: ${FFMPEG_LIBRARIES}") + ENDIF (NOT FFMPEG_FIND_QUIETLY) +ELSE (FFMPEG_FOUND) + IF (FFMPEG_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FFMPEG") + ENDIF (FFMPEG_FIND_REQUIRED) +ENDIF (FFMPEG_FOUND) diff --git a/CMakeModules/FindFREEGLUT.cmake b/CMakeModules/FindFREEGLUT.cmake new file mode 100644 index 0000000..b3e32b0 --- /dev/null +++ b/CMakeModules/FindFREEGLUT.cmake @@ -0,0 +1,44 @@ +# Try to find the FREEGLUT library +# +# FREEGLUT_INCLUDE_DIR +# FREEGLUT_LIBRARY +# FREEGLUT_FOUND + +FIND_PATH( + FREEGLUT_INCLUDE_DIR GL/freeglut.h + ${CMAKE_INCLUDE_PATH} + $ENV{include} + ${OPENGL_INCLUDE_DIR} + /usr/include + /usr/local/include +) + +SET(STORE_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) +SET(CMAKE_FIND_FRAMEWORK NEVER) + +FIND_LIBRARY( + FREEGLUT_LIBRARY + NAMES freeglut_static freeglut glut + PATH + /opt/local/lib + ${CMAKE_LIBRARY_PATH} + $ENV{lib} + /usr/lib + /usr/local/lib +) + +SET(CMAKE_FIND_FRAMEWORK ${STORE_CMAKE_FIND_FRAMEWORK}) + +IF (FREEGLUT_INCLUDE_DIR AND FREEGLUT_LIBRARY) + SET(FREEGLUT_FOUND TRUE) +ENDIF (FREEGLUT_INCLUDE_DIR AND FREEGLUT_LIBRARY) + +IF (FREEGLUT_FOUND) + IF (NOT FREEGLUT_FIND_QUIETLY) + MESSAGE(STATUS "Found FREEGLUT: ${FREEGLUT_LIBRARY}") + ENDIF (NOT FREEGLUT_FIND_QUIETLY) +ELSE (FREEGLUT_FOUND) + IF (FREEGLUT_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FREEGLUT") + ENDIF (FREEGLUT_FIND_REQUIRED) +ENDIF (FREEGLUT_FOUND) diff --git a/CMakeModules/FindGLEW.cmake b/CMakeModules/FindGLEW.cmake new file mode 100644 index 0000000..65ef6f9 --- /dev/null +++ b/CMakeModules/FindGLEW.cmake @@ -0,0 +1,53 @@ +# +# Try to find GLEW library and include path. +# Once done this will define +# +# GLEW_FOUND +# GLEW_INCLUDE_DIR +# GLEW_LIBRARY +# + +IF (WIN32) + FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h + $ENV{PROGRAMFILES}/GLEW/include + ${PROJECT_SOURCE_DIR}/src/nvgl/glew/include + DOC "The directory where GL/glew.h resides") + FIND_LIBRARY( GLEW_LIBRARY + NAMES glew GLEW glew32 glew32s + PATHS + $ENV{PROGRAMFILES}/GLEW/lib + ${PROJECT_SOURCE_DIR}/src/nvgl/glew/bin + ${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib + DOC "The GLEW library") +ELSE (WIN32) + FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h + /usr/include + /usr/local/include + /sw/include + /opt/local/include + DOC "The directory where GL/glew.h resides") + FIND_LIBRARY( GLEW_LIBRARY + NAMES GLEW glew + PATHS + /usr/lib64 + /usr/lib + /usr/local/lib64 + /usr/local/lib + /sw/lib + /opt/local/lib + DOC "The GLEW library") +ENDIF (WIN32) + +IF (GLEW_INCLUDE_DIR AND GLEW_LIBRARY) + SET( GLEW_FOUND TRUE ) +ENDIF (GLEW_INCLUDE_DIR AND GLEW_LIBRARY) + +IF (GLEW_FOUND) + IF (NOT GLEW_FIND_QUIETLY) + MESSAGE(STATUS "Found GLEW: ${GLEW_LIBRARY}") + ENDIF (NOT GLEW_FIND_QUIETLY) +ELSE (GLEW_FOUND) + IF (GLEW_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find GLEW") + ENDIF (GLEW_FIND_REQUIRED) +ENDIF (GLEW_FOUND) diff --git a/CMakeModules/FindGLUES.cmake b/CMakeModules/FindGLUES.cmake new file mode 100644 index 0000000..2c64fe8 --- /dev/null +++ b/CMakeModules/FindGLUES.cmake @@ -0,0 +1,38 @@ +# Try to find the GLUES lib and include files +# +# GLUES_INCLUDE_DIR +# GLUES_LIBRARIES +# GLUES_FOUND + +FIND_PATH( GLUES_INCLUDE_DIR glues/glues.h + /usr/include + /usr/local/include + /opt/include + /opt/local/include + ${CMAKE_INSTALL_PREFIX}/include +) + +FIND_LIBRARY( GLUES_LIBRARY glues + /usr/lib64 + /usr/lib + /usr/local/lib + /opt/local/lib + /opt/local/lib64 + ${CMAKE_INSTALL_PREFIX}/lib +) + +IF(GLUES_INCLUDE_DIR AND GLUES_LIBRARY) + SET( GLUES_FOUND TRUE ) + SET( GLUES_LIBRARIES ${GLUES_LIBRARY} ) +ENDIF(GLUES_INCLUDE_DIR AND GLUES_LIBRARY) + +IF(GLUES_FOUND) + IF(NOT GLUES_FIND_QUIETLY) + MESSAGE(STATUS "Found GLUES: ${GLUES_LIBRARY}") + ENDIF(NOT GLUES_FIND_QUIETLY) +ELSE(GLUES_FOUND) + IF(GLUES_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find GLUES") + ENDIF(GLUES_FIND_REQUIRED) +ENDIF(GLUES_FOUND) + diff --git a/CMakeModules/FindLibRealSense.cmake b/CMakeModules/FindLibRealSense.cmake new file mode 100644 index 0000000..fc8982f --- /dev/null +++ b/CMakeModules/FindLibRealSense.cmake @@ -0,0 +1,34 @@ +# -*- mode: cmake; -*- +############################################################################### +# Find librealsense https://github.com/IntelRealSense/librealsense +# +# This sets the following variables: +# LIBREALSENSE_FOUND - True if OPENNI was found. +# LIBREALSENSE_INCLUDE_DIRS - Directories containing the OPENNI include files. +# LIBREALSENSE_LIBRARIES - Libraries needed to use OPENNI. +# LIBREALSENSE_DEFINITIONS - Compiler flags for OPENNI. +# +# File forked from augmented_dev, project of alantrrs +# (https://github.com/alantrrs/augmented_dev). + +find_package(PkgConfig) +if(${CMAKE_VERSION} VERSION_LESS 2.8.2) +endif() + +#add a hint so that it can find it without the pkg-config +find_path(LIBREALSENSE_INCLUDE_DIR rs.h + HINTS /usr/include/ /usr/local/include + PATH_SUFFIXES librealsense) +#add a hint so that it can find it without the pkg-config +find_library(LIBREALSENSE_LIBRARY + NAMES librealsense.so + HINTS /usr/lib /usr/local/lib ) + + set(LIBREALSENSE_INCLUDE_DIRS ${LIBREALSENSE_INCLUDE_DIR}) + set(LIBREALSENSE_LIBRARIES ${LIBREALSENSE_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibRealSense DEFAULT_MSG + LIBREALSENSE_LIBRARY LIBREALSENSE_INCLUDE_DIR) + +mark_as_advanced(LIBREALSENSE_LIBRARY LIBREALSENSE_INCLUDE_DIR) diff --git a/CMakeModules/FindMediaFoundation.cmake b/CMakeModules/FindMediaFoundation.cmake new file mode 100644 index 0000000..107efa5 --- /dev/null +++ b/CMakeModules/FindMediaFoundation.cmake @@ -0,0 +1,20 @@ +# - Find MediaFoundation +# Find the Windows SDK MediaFoundation libraries +# +# MediaFoundation_LIBRARIES - List of libraries when using MediaFoundation +# MediaFoundation_FOUND - True if MediaFoundation found + +IF (MSVC) + SET( MediaFoundation_LIBRARIES mf.lib mfplat.lib mfreadwrite.lib mfuuid.lib strmiids.lib ) + SET( MediaFoundation_FOUND true ) +ENDIF (MSVC) + +IF (MediaFoundation_FOUND) + IF (NOT MediaFoundation_FIND_QUIETLY) + MESSAGE(STATUS "Found MediaFoundation: ${MediaFoundation_LIBRARIES}") + ENDIF (NOT MediaFoundation FIND_QUIETLY) +ELSE (MediaFoundation_FOUND) + IF (MediaFoundation_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find MediaFoundation") + ENDIF (MediaFoundation_FIND_REQUIRED) +ENDIF (MediaFoundation_FOUND) diff --git a/CMakeModules/FindOculus.cmake b/CMakeModules/FindOculus.cmake new file mode 100644 index 0000000..0408162 --- /dev/null +++ b/CMakeModules/FindOculus.cmake @@ -0,0 +1,63 @@ +# - Try to find Oculus Rift SDK +# +# Oculus_FOUND - system has libuvc +# Oculus_INCLUDE_DIRS - the libuvc include directories +# Oculus_LIBRARIES - link these to use libuvc + +FIND_PATH( + Oculus_INCLUDE_DIRS + NAMES OVR.h + PATHS + ${CMAKE_SOURCE_DIR}/../LibOVR/Include + ${CMAKE_SOURCE_DIR}/../OculusSDK/LibOVR/Include + /usr/include/LibOVR/Include + /usr/local/include/LibOVR/Include + /opt/local/include/LibOVR/Include + /usr/include/ + /usr/local/include + /opt/local/include +) + +FIND_LIBRARY( + Oculus_LIBRARIES + NAMES ovr + PATHS + ${CMAKE_SOURCE_DIR}/../LibOVR/Lib/MacOS/Release + ${CMAKE_SOURCE_DIR}/../OculusSDK/LibOVR/Lib/Linux/Release/x86_64 + /usr/include/LibOVR/Lib + /usr/local/include/LibOVR/Lib + /opt/local/include/LibOVR/Lib + /usr/lib + /usr/local/lib + /opt/local/lib +) + +IF(Oculus_INCLUDE_DIRS AND Oculus_LIBRARIES) + IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + find_library(CARBON_LIBRARIES NAMES Carbon) + find_library(IOKIT_LIBRARIES NAMES IOKit) + list(APPEND Oculus_LIBRARIES ${CARBON_LIBRARIES}) + list(APPEND Oculus_LIBRARIES ${IOKIT_LIBRARIES}) + SET(Oculus_FOUND TRUE) + ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + FIND_PACKAGE(Xrandr QUIET) + IF( Xrandr_FOUND ) + list(APPEND Oculus_LIBRARIES ${Xrandr_LIBRARIES} -ludev -lXrandr -lXinerama ) + SET(Oculus_FOUND TRUE) + ENDIF() + ENDIF() +ENDIF(Oculus_INCLUDE_DIRS AND Oculus_LIBRARIES) + + + +IF(Oculus_FOUND) + IF(NOT Oculus_FIND_QUIETLY) + MESSAGE(STATUS "Found Oculus: ${Oculus_LIBRARIES}") + MESSAGE(STATUS "Found Oculus: ${Oculus_INCLUDE_DIRS}") + ENDIF(NOT Oculus_FIND_QUIETLY) +ELSE(Oculus_FOUND) +message(STATUS "Oculus NOT found") + IF(Oculus_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Oculus") + ENDIF(Oculus_FIND_REQUIRED) +ENDIF(Oculus_FOUND) diff --git a/CMakeModules/FindOpenEXR.cmake b/CMakeModules/FindOpenEXR.cmake new file mode 100644 index 0000000..f4f5d8a --- /dev/null +++ b/CMakeModules/FindOpenEXR.cmake @@ -0,0 +1,33 @@ +# Try to find the OpenEXR v2 lib and include files +# +# OpenEXR_INCLUDE_DIR +# OpenEXR_LIBRARIES +# OpenEXR_FOUND + +FIND_PATH( OpenEXR_INCLUDE_DIR ImfHeader.h + /usr/include + /usr/local/include + PATH_SUFFIXES OpenEXR +) + +FIND_LIBRARY( OpenEXR_LIBRARY IlmImf + /usr/lib64 + /usr/lib + /usr/local/lib +) + +IF(OpenEXR_INCLUDE_DIR AND OpenEXR_LIBRARY) + SET( OpenEXR_FOUND TRUE ) + SET( OpenEXR_LIBRARIES ${OpenEXR_LIBRARY} ) +ENDIF() + +IF(OpenEXR_FOUND) + IF(NOT OpenEXR_FIND_QUIETLY) + MESSAGE(STATUS "Found OpenEXR: ${OpenEXR_LIBRARY}") + ENDIF(NOT OpenEXR_FIND_QUIETLY) +ELSE(OpenEXR_FOUND) + IF(OpenEXR_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find libOpenEXR") + ENDIF(OpenEXR_FIND_REQUIRED) +ENDIF(OpenEXR_FOUND) + diff --git a/CMakeModules/FindOpenNI.cmake b/CMakeModules/FindOpenNI.cmake new file mode 100644 index 0000000..b5e9d9c --- /dev/null +++ b/CMakeModules/FindOpenNI.cmake @@ -0,0 +1,49 @@ +# -*- mode: cmake; -*- +############################################################################### +# Find OpenNI +# +# This sets the following variables: +# OPENNI_FOUND - True if OPENNI was found. +# OPENNI_INCLUDE_DIRS - Directories containing the OPENNI include files. +# OPENNI_LIBRARIES - Libraries needed to use OPENNI. +# OPENNI_DEFINITIONS - Compiler flags for OPENNI. +# +# File forked from augmented_dev, project of alantrrs +# (https://github.com/alantrrs/augmented_dev). + +find_package(PkgConfig) +if(${CMAKE_VERSION} VERSION_LESS 2.8.2) + pkg_check_modules(PC_OPENNI openni-dev) +else() + pkg_check_modules(PC_OPENNI QUIET openni-dev) +endif() + +set(OPENNI_DEFINITIONS ${PC_OPENNI_CFLAGS_OTHER}) + +#using the 64bit version of OpenNi if generating for 64bit +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROGRAMFILES_ "$ENV{PROGRAMW6432}") + set(OPENNI_SUFFIX "64") +else(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROGRAMFILES_ "$ENV{PROGRAMFILES}") + set(OPENNI_SUFFIX "") +endif(CMAKE_SIZEOF_VOID_P EQUAL 8) + +#add a hint so that it can find it without the pkg-config +find_path(OPENNI_INCLUDE_DIR XnStatus.h + HINTS ${PC_OPENNI_INCLUDEDIR} ${PC_OPENNI_INCLUDE_DIRS} /usr/include/ni /usr/include/openni + "${PROGRAMFILES_}/OpenNI/Include" + PATH_SUFFIXES openni) +#add a hint so that it can find it without the pkg-config +find_library(OPENNI_LIBRARY + NAMES OpenNI64 OpenNI + HINTS ${PC_OPENNI_LIBDIR} ${PC_OPENNI_LIBRARY_DIRS} /usr/lib "${PROGRAMFILES_}/OpenNI/Lib${OPENNI_SUFFIX}") + +set(OPENNI_INCLUDE_DIRS ${OPENNI_INCLUDE_DIR}) +set(OPENNI_LIBRARIES ${OPENNI_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OpenNI DEFAULT_MSG + OPENNI_LIBRARY OPENNI_INCLUDE_DIR) + +mark_as_advanced(OPENNI_LIBRARY OPENNI_INCLUDE_DIR) diff --git a/CMakeModules/FindOpenNI2.cmake b/CMakeModules/FindOpenNI2.cmake new file mode 100644 index 0000000..2beb7aa --- /dev/null +++ b/CMakeModules/FindOpenNI2.cmake @@ -0,0 +1,59 @@ +############################################################################### +# Find OpenNI2 +# +# This sets the following variables: +# OPENNI2_FOUND - True if OPENNI was found. +# OPENNI2_INCLUDE_DIRS - Directories containing the OPENNI include files. +# OPENNI2_LIBRARIES - Libraries needed to use OPENNI. + +find_package(PkgConfig) +if(${CMAKE_VERSION} VERSION_LESS 2.8.2) + pkg_check_modules(PC_OPENNI openni2-dev) +else() + pkg_check_modules(PC_OPENNI QUIET openni2-dev) +endif() + +set(OPENNI2_DEFINITIONS ${PC_OPENNI_CFLAGS_OTHER}) + +#add a hint so that it can find it without the pkg-config +find_path(OPENNI2_INCLUDE_DIR OpenNI.h + HINTS + ${PC_OPENNI_INCLUDEDIR} + ${PC_OPENNI_INCLUDE_DIRS} + PATHS + "${PROGRAM_FILES}/OpenNI2/Include" + "${CMAKE_SOURCE_DIR}/../OpenNI2/Include" + /usr/include + /user/include + PATH_SUFFIXES openni2 ni2 +) + +if(${CMAKE_CL_64}) + set(OPENNI_PATH_SUFFIXES lib64 lib) +else() + set(OPENNI_PATH_SUFFIXES lib) +endif() + +#add a hint so that it can find it without the pkg-config +find_library(OPENNI2_LIBRARY + NAMES OpenNI2 + HINTS + ${PC_OPENNI_LIBDIR} + ${PC_OPENNI_LIBRARY_DIRS} + PATHS + "${PROGRAM_FILES}/OpenNI2/Redist" + "${PROGRAM_FILES}/OpenNI2" + "${CMAKE_SOURCE_DIR}/../OpenNI2/Bin/x64-Release" + /usr/lib + /user/lib + PATH_SUFFIXES ${OPENNI_PATH_SUFFIXES} +) + +set(OPENNI2_INCLUDE_DIRS ${OPENNI2_INCLUDE_DIR}) +set(OPENNI2_LIBRARIES ${OPENNI2_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OpenNI2 DEFAULT_MSG + OPENNI2_LIBRARY OPENNI2_INCLUDE_DIR) + +mark_as_advanced(OPENNI2_LIBRARY OPENNI2_INCLUDE_DIR) diff --git a/CMakeModules/FindPleora.cmake b/CMakeModules/FindPleora.cmake new file mode 100644 index 0000000..6a4a94d --- /dev/null +++ b/CMakeModules/FindPleora.cmake @@ -0,0 +1,143 @@ +# - Try to find Pleora SDK +# +# Pleora_FOUND - system has pleora eUSB SDK +# Pleora_INCLUDE_DIRS - the pleora eUSB SDK include directories +# Pleora_LIBRARIES - link these to use pleora eUSB SDK +# Pleora_BASE_DIR - set env varivales to this to use pleora eUSB SDK + +set( INCLUDE_SEARCH_PATHS + "/opt/pleora/ebus_sdk/Ubuntu-12.04-x86_64/include" + "/opt/pleora/ebus_sdk/Ubuntu-14.04-x86_64/include" + "$ENV{ProgramFiles}/Pleora Technologies Inc/eBUS SDK/Includes" +) + +set( LIBRARIES_SEARCH_PATHS + "/opt/pleora/ebus_sdk/Ubuntu-12.04-x86_64/lib" + "/opt/pleora/ebus_sdk/Ubuntu-14.04-x86_64/lib" + "$ENV{ProgramFiles}/Pleora Technologies Inc/eBUS SDK/Libraries" +) + +set( GENAPI_SEARCH_PATHS + "/opt/pleora/ebus_sdk/Ubuntu-12.04-x86_64/lib/genicam/bin/Linux64_x64" + "/opt/pleora/ebus_sdk/Ubuntu-12.04-x86_64/lib/genicam/bin/Linux32_ARM" + "/opt/pleora/ebus_sdk/Ubuntu-14.04-x86_64/lib/genicam/bin/Linux64_x64" + "/opt/pleora/ebus_sdk/Ubuntu-14.04-x86_64/lib/genicam/bin/Linux32_ARM" + "$ENV{ProgramW6432}/GenICam_v2_4/library/CPP/lib/Win64_x64" +) + +IF (${CMAKE_CL_64}) + set (LIB_NAME_SUFFIX "64") +ELSE() + set (LIB_NAME_SUFFIX "") +ENDIF() + +# Find header files +FIND_PATH( + PVBASE_INCLUDE_DIR PvBase.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVDEVICE_INCLUDE_DIR PvDevice.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVBUFFER_INCLUDE_DIR PvBuffer.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVGENICAM_INCLUDE_DIR PvGenICamLib.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVSTREAM_INCLUDE_DIR PvStream.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVTRANSMITTER_INCLUDE_DIR PvTransmitterLib.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVVIRTUALDEVICE_INCLUDE_DIR PvVirtualDeviceLib.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) +FIND_PATH( + PVSAMPLEUTILS_INCLUDE_DIR PvSampleUtils.h + HINTS ${PC_PLEORA_DIR}/include + PATHS ${INCLUDE_SEARCH_PATHS} +) + +# Find Library files +FIND_LIBRARY( + PVBASE_LIBRARY + NAMES "PvBase${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) +FIND_LIBRARY( + PVDEVICE_LIBRARY + NAMES "PvDevice${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) + +FIND_LIBRARY( + PVBUFFER_LIBRARY + NAMES "PvBuffer${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) +FIND_LIBRARY( + PVGENICAM_LIBRARY + NAMES "PvGenICam${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) +FIND_LIBRARY( + PVSTREAM_LIBRARY + NAMES "PvStream${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) +FIND_LIBRARY( + PVTRANSMITTER_LIBRARY + NAMES "PvTransmitter${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) +FIND_LIBRARY( + PVVIRTUALDEVICE_LIBRARY + NAMES "PvVirtualDevice${LIB_NAME_SUFFIX}" + HINTS ${PC_PLEORA_DIR}/lib + PATH ${LIBRARIES_SEARCH_PATHS} +) +FIND_LIBRARY( + GENAPI_LIBRARY + NAMES GenApi_gcc40_v2_4 GenApi_gcc43_v2_4 GenApi_MD_VC80_v2_4 + HINTS ${PC_GENAPI_LIBRARY_DIR} + PATH ${GENAPI_SEARCH_PATHS} +) + +IF(PVBASE_INCLUDE_DIR AND PVDEVICE_INCLUDE_DIR AND PVBUFFER_INCLUDE_DIR AND PVGENICAM_INCLUDE_DIR AND PVSTREAM_INCLUDE_DIR AND PVTRANSMITTER_INCLUDE_DIR AND PVVIRTUALDEVICE_INCLUDE_DIR AND PVSAMPLEUTILS_INCLUDE_DIR AND PVBASE_LIBRARY AND PVDEVICE_LIBRARY AND PVBUFFER_LIBRARY AND PVGENICAM_LIBRARY AND PVSTREAM_LIBRARY AND PVTRANSMITTER_LIBRARY AND PVVIRTUALDEVICE_LIBRARY AND GENAPI_LIBRARY) + SET(Pleora_FOUND TRUE) + string(REGEX REPLACE "include$" "" Pleora_BASE_DIR ${PVBASE_INCLUDE_DIR}) + SET(Pleora_LIBRARIES ${PVBASE_LIBRARY} ${PVDEVICE_LIBRARY} ${PVBUFFER_LIBRARY} ${PVGENICAM_LIBRARY} ${PVSTREAM_LIBRARY} ${PVTRANSMITTER_LIBRARY} ${PVVIRTUALDEVICE_LIBRARY} ${GENAPI_LIBRARY}) + SET(Pleora_INCLUDE_DIRS ${PVBASE_INCLUDE_DIR} ${PVDEVICE_INCLUDE_DIR} ${PVBUFFER_INCLUDE_DIR} ${PVGENICAM_INCLUDE_DIR} ${PVSTREAM_INCLUDE_DIR} ${PVTRANSMITTER_INCLUDE_DIR} ${PVVIRTUALDEVICE_INCLUDE_DIR} ${PVSAMPLEUTILS_INCLUDE_DIR}) +ENDIF() + + +IF (Pleora_FOUND) + IF (NOT Pleora_FIND_QUIETLY) + message(STATUS "Found Pleora: ${Pleora_LIBRARIES}") + ENDIF (NOT Pleora_FIND_QUIETLY) +ELSE (Pleora_FOUND) + IF (Pleora_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Pleora") + ENDIF (Pleora_FIND_REQUIRED) +ENDIF (Pleora_FOUND) diff --git a/CMakeModules/FindROBOTVISION.cmake b/CMakeModules/FindROBOTVISION.cmake new file mode 100644 index 0000000..ea600d1 --- /dev/null +++ b/CMakeModules/FindROBOTVISION.cmake @@ -0,0 +1,31 @@ +# - Try to find librobotvision +# +# ROBOTVISION_FOUND - system has librobotvision +# ROBOTVISION_INCLUDE_DIR - the librobotvision include directories +# ROBOTVISION_LIBRARY - link these to use librobotvision + +FIND_PATH( + ROBOTVISION_INCLUDE_DIR + NAMES robotvision/bundle_adjuster.h + PATHS ${CMAKE_SOURCE_DIR}/.. /usr/include/robotvision /usr/local/include/robotvision +) + +FIND_LIBRARY( + ROBOTVISION_LIBRARY + NAMES robotvision + PATHS ${CMAKE_SOURCE_DIR}/../robotvision/release /usr/lib /usr/local/lib +) + +IF (ROBOTVISION_INCLUDE_DIR AND ROBOTVISION_LIBRARY) + SET(ROBOTVISION_FOUND TRUE) +ENDIF (ROBOTVISION_INCLUDE_DIR AND ROBOTVISION_LIBRARY) + +IF (ROBOTVISION_FOUND) + IF (NOT ROBOTVISION_FIND_QUIETLY) + MESSAGE(STATUS "Found ROBOTVISION: ${ROBOTVISION_LIBRARY}") + ENDIF (NOT ROBOTVISION_FIND_QUIETLY) +ELSE (ROBOTVISION_FOUND) + IF (ROBOTVISION_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find ROBOTVISION") + ENDIF (ROBOTVISION_FIND_REQUIRED) +ENDIF (ROBOTVISION_FOUND) diff --git a/CMakeModules/FindTeliCam.cmake b/CMakeModules/FindTeliCam.cmake new file mode 100644 index 0000000..d2f7032 --- /dev/null +++ b/CMakeModules/FindTeliCam.cmake @@ -0,0 +1,58 @@ +############################################################################### +# Find Toshiba TeliCam +# +# This sets the following variables: +# TeliCam_FOUND - True if TeliCam was found. +# TeliCam_INCLUDE_DIRS - Directories containing the TeliCam include files. +# TeliCam_LIBRARIES - Libraries needed to use TeliCam. + +find_path( + TeliCam_INCLUDE_DIR TeliCamApi.h + PATHS + "${PROGRAM_FILES}/Toshiba Teli/TeliCamSDK/TeliCamApi/Include" + "${CMAKE_SOURCE_DIR}/../TeliCamSDK/TeliCamApi/Include" + /usr/include + /user/include + /opt/TeliCamSDK/include + PATH_SUFFIXES TeliCam +) + +if(${CMAKE_CL_64}) + set(TELI_PATH_SUFFIXES x64) +else() + set(TELI_PATH_SUFFIXES x86) +endif() + +find_library( + TeliCamApi_LIBRARY + NAMES TeliCamApi TeliCamApi64 TeliCamApi_64 + PATHS + "${PROGRAM_FILES}/Toshiba Teli/TeliCamSDK/TeliCamApi/lib" + "${CMAKE_SOURCE_DIR}/../TeliCamSDK/TeliCamApi/lib" + /usr/lib + /user/lib + /opt/TeliCamSDK/lib + PATH_SUFFIXES ${TELI_PATH_SUFFIXES} +) + +find_library( + TeliCamUtl_LIBRARY + NAMES TeliCamUtl TeliCamUtl64 TeliCamUtl_64 + PATHS + "${PROGRAM_FILES}/Toshiba Teli/TeliCamSDK/TeliCamApi/lib" + "${CMAKE_SOURCE_DIR}/../TeliCamSDK/TeliCamApi/lib" + /usr/lib + /user/lib + /opt/TeliCamSDK/lib + PATH_SUFFIXES ${TELI_PATH_SUFFIXES} +) + +set(TeliCam_INCLUDE_DIRS ${TeliCam_INCLUDE_DIR}) +set(TeliCam_LIBRARY "${TeliCamApi_LIBRARY}" "${TeliCamUtl_LIBRARY}") +set(TeliCam_LIBRARIES ${TeliCam_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( TeliCam + FOUND_VAR TeliCam_FOUND + REQUIRED_VARS TeliCamApi_LIBRARY TeliCamUtl_LIBRARY TeliCam_INCLUDE_DIR +) diff --git a/CMakeModules/FindTooN.cmake b/CMakeModules/FindTooN.cmake new file mode 100644 index 0000000..13ce3d5 --- /dev/null +++ b/CMakeModules/FindTooN.cmake @@ -0,0 +1,28 @@ +# - Try to find libTooN +# +# TooN_FOUND - system has libTooN +# TooN_INCLUDE_DIR - the libTooN include directories + +FIND_PATH( + TooN_INCLUDE_DIR + NAMES TooN/TooN.h + PATHS + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/.. + /usr/include + /usr/local/include +) + +IF(TooN_INCLUDE_DIR) + SET(TooN_FOUND TRUE) +ENDIF() + +IF(TooN_FOUND) + IF(NOT TooN_FIND_QUIETLY) + MESSAGE(STATUS "Found TooN: ${TooN_INCLUDE_DIR}") + ENDIF() +ELSE() + IF(TooN_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find TooN") + ENDIF() +ENDIF() diff --git a/CMakeModules/FindXrandr.cmake b/CMakeModules/FindXrandr.cmake new file mode 100644 index 0000000..0ec8440 --- /dev/null +++ b/CMakeModules/FindXrandr.cmake @@ -0,0 +1,32 @@ +# - Try to find Xrandr +# +# Xrandr_FOUND - system has libXrandr +# Xrandr_INCLUDE_DIRS - the libXrandr include directories +# Xrandr_LIBRARIES - link these to use libXrandr + +FIND_PATH( + Xrandr_INCLUDE_DIRS + NAMES X11/extensions/Xrandr.h + PATH_SUFFIXES X11/extensions + DOC "The Xrandr include directory" +) + +FIND_LIBRARY( + Xrandr_LIBRARIES + NAMES Xrandr + DOC "The Xrandr library" +) + +IF (Xrandr_INCLUDE_DIRS AND Xrandr_LIBRARIES) + SET(Xrandr_FOUND TRUE) +ENDIF (Xrandr_INCLUDE_DIRS AND Xrandr_LIBRARIES) + +IF (Xrandr_FOUND) + IF (NOT Xrandr_FIND_QUIETLY) + MESSAGE(STATUS "Found Xrandr: ${Xrandr_LIBRARIES}") + ENDIF (NOT Xrandr_FIND_QUIETLY) +ELSE (Xrandr_FOUND) + IF (Xrandr_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Xrandr") + ENDIF (Xrandr_FIND_REQUIRED) +ENDIF (Xrandr_FOUND) diff --git a/CMakeModules/Findlibusb1.cmake b/CMakeModules/Findlibusb1.cmake new file mode 100644 index 0000000..a208728 --- /dev/null +++ b/CMakeModules/Findlibusb1.cmake @@ -0,0 +1,42 @@ +# - Try to find libusb1 +# +# libusb1_FOUND - system has libusb1 +# libusb1_INCLUDE_DIRS - the libusb1 include directories +# libusb1_LIBRARIES - link these to use libusb1 + +FIND_PATH( + libusb1_INCLUDE_DIRS + NAMES libusb-1.0/libusb.h + PATHS + c:/dev/sysroot32/usr/include + ${CMAKE_SOURCE_DIR}/../libusb1/include + /usr/include/ + /usr/local/include + /opt/local/include +) + +FIND_LIBRARY( + libusb1_LIBRARIES + NAMES libusb-1.0 + PATHS + c:/dev/sysroot32/usr/lib + ${CMAKE_SOURCE_DIR}/../libusb1/lib + /usr/lib + /usr/local/lib + /opt/local/lib +) + +IF(libusb1_INCLUDE_DIRS AND libusb1_LIBRARIES) + SET(libusb1_FOUND TRUE) +ENDIF(libusb1_INCLUDE_DIRS AND libusb1_LIBRARIES) + +IF(libusb1_FOUND) + IF(NOT libusb1_FIND_QUIETLY) + MESSAGE(STATUS "Found libusb1: ${libusb1_LIBRARIES}") + ENDIF(NOT libusb1_FIND_QUIETLY) +ELSE(libusb1_FOUND) +message(STATUS "libusb1 NOT found") + IF(libusb1_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find libusb1") + ENDIF(libusb1_FIND_REQUIRED) +ENDIF(libusb1_FOUND) diff --git a/CMakeModules/Findpthread.cmake b/CMakeModules/Findpthread.cmake new file mode 100644 index 0000000..40a76f2 --- /dev/null +++ b/CMakeModules/Findpthread.cmake @@ -0,0 +1,42 @@ +# - Try to find pthread +# +# pthread_FOUND - system has pthread +# pthread_INCLUDE_DIRS - the pthread include directories +# pthread_LIBRARIES - link these to use pthread + +FIND_PATH( + pthread_INCLUDE_DIRS + NAMES pthread.h + PATHS + c:/dev/sysroot32/usr/include + ${CMAKE_SOURCE_DIR}/../pthread/include + /usr/include/ + /usr/local/include + /opt/local/include +) + +FIND_LIBRARY( + pthread_LIBRARIES + NAMES pthreadVSE2 pthread + PATHS + c:/dev/sysroot32/usr/lib + ${CMAKE_SOURCE_DIR}/../pthread/lib + /usr/lib + /usr/local/lib + /opt/local/lib +) + +IF(pthread_INCLUDE_DIRS AND pthread_LIBRARIES) + SET(pthread_FOUND TRUE) +ENDIF(pthread_INCLUDE_DIRS AND pthread_LIBRARIES) + +IF(pthread_FOUND) + IF(NOT pthread_FIND_QUIETLY) + MESSAGE(STATUS "Found pthread: ${pthread_LIBRARIES}") + ENDIF(NOT pthread_FIND_QUIETLY) +ELSE(pthread_FOUND) +message(STATUS "pthread NOT found") + IF(pthread_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find pthread") + ENDIF(pthread_FIND_REQUIRED) +ENDIF(pthread_FOUND) diff --git a/CMakeModules/Finduvc.cmake b/CMakeModules/Finduvc.cmake new file mode 100644 index 0000000..fefe72f --- /dev/null +++ b/CMakeModules/Finduvc.cmake @@ -0,0 +1,39 @@ +# - Try to find uvc +# +# uvc_FOUND - system has libuvc +# uvc_INCLUDE_DIRS - the libuvc include directories +# uvc_LIBRARIES - link these to use libuvc + +FIND_PATH( + uvc_INCLUDE_DIRS + NAMES libuvc/libuvc.h + PATHS + ${CMAKE_SOURCE_DIR}/.. + /usr/include/ + /usr/local/include + /opt/local/include +) + +FIND_LIBRARY( + uvc_LIBRARIES + NAMES uvc + PATHS + ${CMAKE_SOURCE_DIR}/../uvc/build + /usr/lib + /usr/local/lib + /opt/local/lib +) + +IF (uvc_INCLUDE_DIRS AND uvc_LIBRARIES) + SET(uvc_FOUND TRUE) +ENDIF (uvc_INCLUDE_DIRS AND uvc_LIBRARIES) + +IF (uvc_FOUND) + IF (NOT uvc_FIND_QUIETLY) + MESSAGE(STATUS "Found uvc: ${uvc_LIBRARIES}") + ENDIF (NOT uvc_FIND_QUIETLY) +ELSE (uvc_FOUND) + IF (uvc_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find uvc") + ENDIF (uvc_FIND_REQUIRED) +ENDIF (uvc_FOUND) diff --git a/CMakeModules/Findzstd.cmake b/CMakeModules/Findzstd.cmake new file mode 100644 index 0000000..f4faabb --- /dev/null +++ b/CMakeModules/Findzstd.cmake @@ -0,0 +1,35 @@ +############################################################################### +# Find Toshiba TeliCam +# +# This sets the following variables: +# TeliCam_FOUND - True if TeliCam was found. +# TeliCam_INCLUDE_DIRS - Directories containing the TeliCam include files. +# TeliCam_LIBRARIES - Libraries needed to use TeliCam. + +find_path( + zstd_INCLUDE_DIR zstd.h + PATHS + /opt/local/include + /usr/local/include + /usr/include + PATH_SUFFIXES TeliCam +) + +find_library( + zstd_LIBRARY + NAMES zstd + PATHS + /opt/local/lib + /user/local/lib + /usr/lib +) + +# Plural forms +set(zstd_INCLUDE_DIRS ${zstd_INCLUDE_DIR}) +set(zstd_LIBRARIES ${zstd_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( zstd + FOUND_VAR zstd_FOUND + REQUIRED_VARS zstd_INCLUDE_DIR zstd_LIBRARY +) diff --git a/CMakeModules/SetPlatformVars.cmake b/CMakeModules/SetPlatformVars.cmake new file mode 100644 index 0000000..a07b35a --- /dev/null +++ b/CMakeModules/SetPlatformVars.cmake @@ -0,0 +1,56 @@ +## Compiler configuration +IF(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC) + SET(_GCC_ 1) +ENDIF() + +IF(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + SET(_CLANG_ 1) +ENDIF() + +IF(MSVC) + SET(_MSVC_ 1) +ENDIF() + +## Platform configuration + +IF(WIN32 OR WIN64) + SET(_WIN_ 1) +ENDIF() + +IF(UNIX) + SET(_UNIX_ 1) +ENDIF() + +IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + SET(_OSX_ 1) +ENDIF() + +IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + SET(_LINUX_ 1) +ENDIF() + +IF(ANDROID) + SET(_ANDROID_ 1) +ENDIF() + +IF(IOS) + SET(_APPLE_IOS_ 1) +ENDIF() + + + +## Default search paths + +IF(_WIN_) + IF(${CMAKE_CL_64}) + LIST(APPEND CMAKE_INCLUDE_PATH "c:/dev/sysroot64/usr/include") + LIST(APPEND CMAKE_LIBRARY_PATH "c:/dev/sysroot64/usr/lib") + LIST(APPEND CMAKE_LIBRARY_PATH "c:/dev/sysroot64/usr/bin") + set(PROGRAM_FILES "$ENV{PROGRAMW6432}" ) + ELSE() + LIST(APPEND CMAKE_INCLUDE_PATH "c:/dev/sysroot32/usr/include") + LIST(APPEND CMAKE_LIBRARY_PATH "c:/dev/sysroot32/usr/lib") + LIST(APPEND CMAKE_LIBRARY_PATH "c:/dev/sysroot32/usr/bin") + set(PROGRAM_FILES "$ENV{PROGRAMFILES}" ) + ENDIF() +ENDIF() diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..9b7376c --- /dev/null +++ b/LICENCE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Steven Lovegrove and Richard Newcombe + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000..6dc0777 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,25 @@ +# ----------------------------------------------- +# File that provides "make uninstall" target +# We use the file 'install_manifest.txt' +# ----------------------------------------------- +IF(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..1b81a80 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,31 @@ +# All examples depend on Pangolin GUI +if(BUILD_PANGOLIN_GUI) + add_subdirectory(HelloPangolin) + add_subdirectory(SimpleMultiDisplay) + add_subdirectory(SimpleDisplayImage) + add_subdirectory(SimpleScene) + + if(NOT HAVE_GLES OR HAVE_GLES_2) + add_subdirectory(SimplePlot) + endif() + + ## These samples require Pangolin Var support + if(BUILD_PANGOLIN_VARS) + add_subdirectory(SimpleDisplay) + + ## Video Samples require Pangolin Video support + if(BUILD_PANGOLIN_VIDEO) + add_subdirectory(SimpleVideo) + add_subdirectory(SimpleRecord) + add_subdirectory(SharedMemoryCamera) + endif() + +# # This sample fails on many platforms, so exclude it for now, +# # until we can create a better cmake test for support. +# find_package(CUDA QUIET) +# if( CUDA_FOUND ) +# add_subdirectory(VBODisplay) +# endif() + + endif() +endif() diff --git a/examples/HelloPangolin/CMakeLists.txt b/examples/HelloPangolin/CMakeLists.txt new file mode 100644 index 0000000..f37389a --- /dev/null +++ b/examples/HelloPangolin/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(HelloPangolin main.cpp) +target_link_libraries(HelloPangolin ${Pangolin_LIBRARIES}) diff --git a/examples/HelloPangolin/main.cpp b/examples/HelloPangolin/main.cpp new file mode 100644 index 0000000..2d7c582 --- /dev/null +++ b/examples/HelloPangolin/main.cpp @@ -0,0 +1,34 @@ +#include + +int main( int /*argc*/, char** /*argv*/ ) +{ + pangolin::CreateWindowAndBind("Main",640,480); + glEnable(GL_DEPTH_TEST); + + // Define Projection and initial ModelView matrix + pangolin::OpenGlRenderState s_cam( + pangolin::ProjectionMatrix(640,480,420,420,320,240,0.2,100), + pangolin::ModelViewLookAt(-2,2,-2, 0,0,0, pangolin::AxisY) + ); + + // Create Interactive View in window + pangolin::Handler3D handler(s_cam); + pangolin::View& d_cam = pangolin::CreateDisplay() + .SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f/480.0f) + .SetHandler(&handler); + + while( !pangolin::ShouldQuit() ) + { + // Clear screen and activate view to render into + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + d_cam.Activate(s_cam); + + // Render OpenGL Cube + pangolin::glDrawColouredCube(); + + // Swap frames and Process Events + pangolin::FinishFrame(); + } + + return 0; +} diff --git a/examples/SharedMemoryCamera/CMakeLists.txt b/examples/SharedMemoryCamera/CMakeLists.txt new file mode 100644 index 0000000..567aeed --- /dev/null +++ b/examples/SharedMemoryCamera/CMakeLists.txt @@ -0,0 +1,7 @@ +if(UNIX) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SharedMemoryCamera main.cpp) +target_link_libraries(SharedMemoryCamera ${Pangolin_LIBRARIES}) +endif() diff --git a/examples/SharedMemoryCamera/main.cpp b/examples/SharedMemoryCamera/main.cpp new file mode 100644 index 0000000..29ba6ae --- /dev/null +++ b/examples/SharedMemoryCamera/main.cpp @@ -0,0 +1,53 @@ +#include + +#include +#include +#include + +#include +#include + +// This sample acts as a soft camera. It writes a pattern of GRAY8 pixels to the +// shared memory space. It can be seen in Pangolin's SimpleVideo sample using +// the shmem:[size=640x480]//example video URI. + +using namespace pangolin; + +unsigned char generate_value(double t) +{ + // 10s sinusoid + const double d = std::sin(t * 10.0 / M_PI) * 128.0 + 128.0; + return static_cast(d); +} + +int main(/*int argc, char *argv[]*/) +{ + std::string shmem_name = "/example"; + + std::shared_ptr shmem_buffer = + create_named_shared_memory_buffer(shmem_name, 640 * 480); + if (!shmem_buffer) { + perror("Unable to create shared memory buffer"); + exit(1); + } + + std::string cond_name = shmem_name + "_cond"; + std::shared_ptr buffer_full = + create_named_condition_variable(cond_name); + + // Sit in a loop and write gray values based on some timing pattern. + while (true) { + shmem_buffer->lock(); + unsigned char *ptr = shmem_buffer->ptr(); + unsigned char value = generate_value(std::chrono::system_clock::now().time_since_epoch().count()); + + for (int i = 0; i < 640*480; ++i) { + ptr[i] = value; + } + + shmem_buffer->unlock(); + buffer_full->signal(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} diff --git a/examples/SimpleDisplay/CMakeLists.txt b/examples/SimpleDisplay/CMakeLists.txt new file mode 100644 index 0000000..fe85858 --- /dev/null +++ b/examples/SimpleDisplay/CMakeLists.txt @@ -0,0 +1,7 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimpleDisplay main.cpp) +target_link_libraries(SimpleDisplay ${Pangolin_LIBRARIES} ) + diff --git a/examples/SimpleDisplay/app.cfg b/examples/SimpleDisplay/app.cfg new file mode 100644 index 0000000..cf3661b --- /dev/null +++ b/examples/SimpleDisplay/app.cfg @@ -0,0 +1,26 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Pangolin Sample configuration file +% Comments start with '%' or '#' +% +% Declarations are name value pairs, +% seperated with '=' and terminated with ';' + +% We can set any variable referenced in code directly +ui.A Double = 3.2; +ui.A Checkbox = false; + +% We can set unreferenced variables too +a.b = 1; +a.c = 2; +z.b = 3; +z.c = 4; +start = z; + +% Which we might refer to by reference +ui.An Int = ${${start}.c}; + +% Declarations can span multiple lines +M = +[1, 0, 0 + 0, 1, 0 + 0, 0, 1]; diff --git a/examples/SimpleDisplay/main.cpp b/examples/SimpleDisplay/main.cpp new file mode 100644 index 0000000..850bde4 --- /dev/null +++ b/examples/SimpleDisplay/main.cpp @@ -0,0 +1,129 @@ +#include +#include + +struct CustomType +{ + CustomType() + : x(0), y(0.0f) {} + + CustomType(int x, float y, std::string z) + : x(x), y(y), z(z) {} + + int x; + float y; + std::string z; +}; + +std::ostream& operator<< (std::ostream& os, const CustomType& o){ + os << o.x << " " << o.y << " " << o.z; + return os; +} + +std::istream& operator>> (std::istream& is, CustomType& o){ + is >> o.x; + is >> o.y; + is >> o.z; + return is; +} + +void SampleMethod() +{ + std::cout << "You typed ctrl-r or pushed reset" << std::endl; +} + + +int main(/*int argc, char* argv[]*/) +{ + // Load configuration data + pangolin::ParseVarsFile("app.cfg"); + + // Create OpenGL window in single line + pangolin::CreateWindowAndBind("Main",640,480); + + // 3D Mouse handler requires depth testing to be enabled + glEnable(GL_DEPTH_TEST); + + // Define Camera Render Object (for view / scene browsing) + pangolin::OpenGlRenderState s_cam( + pangolin::ProjectionMatrix(640,480,420,420,320,240,0.1,1000), + pangolin::ModelViewLookAt(-0,0.5,-3, 0,0,0, pangolin::AxisY) + ); + + const int UI_WIDTH = 180; + + // Add named OpenGL viewport to window and provide 3D Handler + pangolin::View& d_cam = pangolin::CreateDisplay() + .SetBounds(0.0, 1.0, pangolin::Attach::Pix(UI_WIDTH), 1.0, -640.0f/480.0f) + .SetHandler(new pangolin::Handler3D(s_cam)); + + // Add named Panel and bind to variables beginning 'ui' + // A Panel is just a View with a default layout and input handling + pangolin::CreatePanel("ui") + .SetBounds(0.0, 1.0, 0.0, pangolin::Attach::Pix(UI_WIDTH)); + + // Safe and efficient binding of named variables. + // Specialisations mean no conversions take place for exact types + // and conversions between scalar types are cheap. + pangolin::Var a_button("ui.A_Button",false,false); + pangolin::Var a_double("ui.A_Double",3,0,5); + pangolin::Var an_int("ui.An_Int",2,0,5); + pangolin::Var a_double_log("ui.Log_scale var",3,1,1E4, true); + pangolin::Var a_checkbox("ui.A_Checkbox",false,true); + pangolin::Var an_int_no_input("ui.An_Int_No_Input",2); + pangolin::Var any_type("ui.Some_Type", CustomType(0,1.2f,"Hello") ); + + pangolin::Var save_window("ui.Save_Window",false,false); + pangolin::Var save_cube("ui.Save_Cube",false,false); + + pangolin::Var record_cube("ui.Record_Cube",false,false); + + // std::function objects can be used for Var's too. These work great with C++11 closures. + pangolin::Var > reset("ui.Reset", SampleMethod); + + // Demonstration of how we can register a keyboard hook to alter a Var + pangolin::RegisterKeyPressCallback(pangolin::PANGO_CTRL + 'b', pangolin::SetVarFunctor("ui.A_Double", 3.5)); + + // Demonstration of how we can register a keyboard hook to trigger a method + pangolin::RegisterKeyPressCallback(pangolin::PANGO_CTRL + 'r', SampleMethod); + + // Default hooks for exiting (Esc) and fullscreen (tab). + while( !pangolin::ShouldQuit() ) + { + // Clear entire screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if( pangolin::Pushed(a_button) ) + std::cout << "You Pushed a button!" << std::endl; + + // Overloading of Var operators allows us to treat them like + // their wrapped types, eg: + if( a_checkbox ) + an_int = (int)a_double; + + if( !any_type->z.compare("robot")) + any_type = CustomType(1,2.3f,"Boogie"); + + an_int_no_input = an_int; + + if( pangolin::Pushed(save_window) ) + pangolin::SaveWindowOnRender("window"); + + if( pangolin::Pushed(save_cube) ) + d_cam.SaveOnRender("cube"); + + if( pangolin::Pushed(record_cube) ) + pangolin::DisplayBase().RecordOnRender("ffmpeg:[fps=50,bps=8388608,unique_filename]//screencap.avi"); + + // Activate efficiently by object + d_cam.Activate(s_cam); + + // Render some stuff + glColor3f(1.0,1.0,1.0); + pangolin::glDrawColouredCube(); + + // Swap frames and Process Events + pangolin::FinishFrame(); + } + + return 0; +} diff --git a/examples/SimpleDisplayImage/CMakeLists.txt b/examples/SimpleDisplayImage/CMakeLists.txt new file mode 100644 index 0000000..57f37b0 --- /dev/null +++ b/examples/SimpleDisplayImage/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimpleDisplayImage main.cpp) +target_link_libraries(SimpleDisplayImage ${Pangolin_LIBRARIES}) diff --git a/examples/SimpleDisplayImage/main.cpp b/examples/SimpleDisplayImage/main.cpp new file mode 100644 index 0000000..5dd87b4 --- /dev/null +++ b/examples/SimpleDisplayImage/main.cpp @@ -0,0 +1,72 @@ +#include +#include +#include + +void setImageData(unsigned char * imageArray, int size){ + for(int i = 0 ; i < size;i++) { + imageArray[i] = (unsigned char)(rand()/(RAND_MAX/255.0)); + } +} + +int main(/*int argc, char* argv[]*/) +{ + // Create OpenGL window in single line + pangolin::CreateWindowAndBind("Main",640,480); + + // 3D Mouse handler requires depth testing to be enabled + glEnable(GL_DEPTH_TEST); + + pangolin::OpenGlRenderState s_cam( + pangolin::ProjectionMatrix(640,480,420,420,320,240,0.1,1000), + pangolin::ModelViewLookAt(-1,1,-1, 0,0,0, pangolin::AxisY) + ); + + // Aspect ratio allows us to constrain width and height whilst fitting within specified + // bounds. A positive aspect ratio makes a view 'shrink to fit' (introducing empty bars), + // whilst a negative ratio makes the view 'grow to fit' (cropping the view). + pangolin::View& d_cam = pangolin::Display("cam") + .SetBounds(0,1.0f,0,1.0f,-640/480.0) + .SetHandler(new pangolin::Handler3D(s_cam)); + + // This view will take up no more than a third of the windows width or height, and it + // will have a fixed aspect ratio to match the image that it will display. When fitting + // within the specified bounds, push to the top-left (as specified by SetLock). + pangolin::View& d_image = pangolin::Display("image") + .SetBounds(2/3.0f,1.0f,0,1/3.0f,640.0/480) + .SetLock(pangolin::LockLeft, pangolin::LockTop); + + std::cout << "Resize the window to experiment with SetBounds, SetLock and SetAspect." << std::endl; + std::cout << "Notice that the cubes aspect is maintained even though it covers the whole screen." << std::endl; + + const int width = 64; + const int height = 48; + + unsigned char* imageArray = new unsigned char[3*width*height]; + pangolin::GlTexture imageTexture(width,height,GL_RGB,false,0,GL_RGB,GL_UNSIGNED_BYTE); + + // Default hooks for exiting (Esc) and fullscreen (tab). + while(!pangolin::ShouldQuit()) + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + d_cam.Activate(s_cam); + + glColor3f(1.0,1.0,1.0); + pangolin::glDrawColouredCube(); + + //Set some random image data and upload to GPU + setImageData(imageArray,3*width*height); + imageTexture.Upload(imageArray,GL_RGB,GL_UNSIGNED_BYTE); + + //display the image + d_image.Activate(); + glColor3f(1.0,1.0,1.0); + imageTexture.RenderToViewport(); + + pangolin::FinishFrame(); + } + + delete[] imageArray; + + return 0; +} diff --git a/examples/SimpleMultiDisplay/CMakeLists.txt b/examples/SimpleMultiDisplay/CMakeLists.txt new file mode 100644 index 0000000..3d91937 --- /dev/null +++ b/examples/SimpleMultiDisplay/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimpleMultiDisplay main.cpp) +target_link_libraries(SimpleMultiDisplay ${Pangolin_LIBRARIES}) diff --git a/examples/SimpleMultiDisplay/app.cfg b/examples/SimpleMultiDisplay/app.cfg new file mode 100644 index 0000000..08c9c8c --- /dev/null +++ b/examples/SimpleMultiDisplay/app.cfg @@ -0,0 +1,28 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Pangolin Sample configuration file +% Comments start with '%' or '#' +% +% Declarations are name value pairs, +% seperated with '=' and terminated with ';' + +% We can set any variable referenced in code directly +ui.A Double = 3.2; + +% We can set unreferenced variables too +a.b = 1; +a.c = 2; +z.b = 3; +z.c = 4; +start = z; + +% Which we might refer to by reference +ui.An Int = ${${start}.c}; + +% Declarations can span multiple lines +M = +[1, 0, 0 + 0, 1, 0 + 0, 0, 1]; + +ui.Aliased Double = @ui.A Double; +ui.Aliased Double = 2.0; diff --git a/examples/SimpleMultiDisplay/main.cpp b/examples/SimpleMultiDisplay/main.cpp new file mode 100644 index 0000000..79bb22b --- /dev/null +++ b/examples/SimpleMultiDisplay/main.cpp @@ -0,0 +1,106 @@ +#include +#include + +void setImageData(unsigned char * imageArray, int size){ + for(int i = 0 ; i < size;i++) { + imageArray[i] = (unsigned char)(rand()/(RAND_MAX/255.0)); + } +} + +int main(/*int argc, char* argv[]*/) +{ + // Create OpenGL window in single line + pangolin::CreateWindowAndBind("Main",640,480); + + // 3D Mouse handler requires depth testing to be enabled + glEnable(GL_DEPTH_TEST); + + // Issue specific OpenGl we might need + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Define Camera Render Object (for view / scene browsing) + pangolin::OpenGlMatrix proj = pangolin::ProjectionMatrix(640,480,420,420,320,240,0.1,1000); + pangolin::OpenGlRenderState s_cam(proj, pangolin::ModelViewLookAt(1,0.5,-2,0,0,0, pangolin::AxisY) ); + pangolin::OpenGlRenderState s_cam2(proj, pangolin::ModelViewLookAt(0,0,-2,0,0,0, pangolin::AxisY) ); + + // Add named OpenGL viewport to window and provide 3D Handler + pangolin::View& d_cam1 = pangolin::Display("cam1") + .SetAspect(640.0f/480.0f) + .SetHandler(new pangolin::Handler3D(s_cam)); + + pangolin::View& d_cam2 = pangolin::Display("cam2") + .SetAspect(640.0f/480.0f) + .SetHandler(new pangolin::Handler3D(s_cam2)); + + pangolin::View& d_cam3 = pangolin::Display("cam3") + .SetAspect(640.0f/480.0f) + .SetHandler(new pangolin::Handler3D(s_cam)); + + pangolin::View& d_cam4 = pangolin::Display("cam4") + .SetAspect(640.0f/480.0f) + .SetHandler(new pangolin::Handler3D(s_cam2)); + + pangolin::View& d_img1 = pangolin::Display("img1") + .SetAspect(640.0f/480.0f); + + pangolin::View& d_img2 = pangolin::Display("img2") + .SetAspect(640.0f/480.0f); + + // LayoutEqual is an EXPERIMENTAL feature - it requires that all sub-displays + // share the same aspect ratio, placing them in a raster fasion in the + // viewport so as to maximise display size. + pangolin::Display("multi") + .SetBounds(0.0, 1.0, 0.0, 1.0) + .SetLayout(pangolin::LayoutEqual) + .AddDisplay(d_cam1) + .AddDisplay(d_img1) + .AddDisplay(d_cam2) + .AddDisplay(d_img2) + .AddDisplay(d_cam3) + .AddDisplay(d_cam4); + + const int width = 64; + const int height = 48; + unsigned char* imageArray = new unsigned char[3*width*height]; + pangolin::GlTexture imageTexture(width,height,GL_RGB,false,0,GL_RGB,GL_UNSIGNED_BYTE); + + // Default hooks for exiting (Esc) and fullscreen (tab). + while( !pangolin::ShouldQuit() ) + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Generate random image and place in texture memory for display + setImageData(imageArray,3*width*height); + imageTexture.Upload(imageArray,GL_RGB,GL_UNSIGNED_BYTE); + + glColor3f(1.0,1.0,1.0); + + d_cam1.Activate(s_cam); + pangolin::glDrawColouredCube(); + + d_cam2.Activate(s_cam2); + pangolin::glDrawColouredCube(); + + d_cam3.Activate(s_cam); + pangolin::glDrawColouredCube(); + + d_cam4.Activate(s_cam2); + pangolin::glDrawColouredCube(); + + d_img1.Activate(); + glColor4f(1.0f,1.0f,1.0f,1.0f); + imageTexture.RenderToViewport(); + + d_img2.Activate(); + glColor4f(1.0f,1.0f,1.0f,1.0f); + imageTexture.RenderToViewport(); + + // Swap frames and Process Events + pangolin::FinishFrame(); + } + + delete[] imageArray; + + return 0; +} diff --git a/examples/SimplePlot/CMakeLists.txt b/examples/SimplePlot/CMakeLists.txt new file mode 100644 index 0000000..62a6de7 --- /dev/null +++ b/examples/SimplePlot/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimplePlot main.cpp) +target_link_libraries(SimplePlot ${Pangolin_LIBRARIES}) diff --git a/examples/SimplePlot/main.cpp b/examples/SimplePlot/main.cpp new file mode 100644 index 0000000..a931e4e --- /dev/null +++ b/examples/SimplePlot/main.cpp @@ -0,0 +1,49 @@ +#include + +#include + +int main(/*int argc, char* argv[]*/) +{ + // Create OpenGL window in single line + pangolin::CreateWindowAndBind("Main",640,480); + + // Data logger object + pangolin::DataLog log; + + // Optionally add named labels + std::vector labels; + labels.push_back(std::string("sin(t)")); + labels.push_back(std::string("cos(t)")); + labels.push_back(std::string("sin(t)+cos(t)")); + log.SetLabels(labels); + + const float tinc = 0.01f; + + // OpenGL 'view' of data. We might have many views of the same data. + pangolin::Plotter plotter(&log,0.0f,4.0f*(float)M_PI/tinc,-2.0f,2.0f,(float)M_PI/(4.0f*tinc),0.5f); + plotter.SetBounds(0.0, 1.0, 0.0, 1.0); + plotter.Track("$i"); + + // Add some sample annotations to the plot + plotter.AddMarker(pangolin::Marker::Vertical, -1000, pangolin::Marker::LessThan, pangolin::Colour::Blue().WithAlpha(0.2f) ); + plotter.AddMarker(pangolin::Marker::Horizontal, 100, pangolin::Marker::GreaterThan, pangolin::Colour::Red().WithAlpha(0.2f) ); + plotter.AddMarker(pangolin::Marker::Horizontal, 10, pangolin::Marker::Equal, pangolin::Colour::Green().WithAlpha(0.2f) ); + + pangolin::DisplayBase().AddDisplay(plotter); + + float t = 0; + + // Default hooks for exiting (Esc) and fullscreen (tab). + while( !pangolin::ShouldQuit() ) + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + log.Log(sin(t),cos(t),sin(t)+cos(t)); + t += tinc; + + // Render graph, Swap frames and Process Events + pangolin::FinishFrame(); + } + + return 0; +} diff --git a/examples/SimpleRecord/CMakeLists.txt b/examples/SimpleRecord/CMakeLists.txt new file mode 100644 index 0000000..8977c0e --- /dev/null +++ b/examples/SimpleRecord/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimpleRecord main.cpp) +target_link_libraries(SimpleRecord ${Pangolin_LIBRARIES}) diff --git a/examples/SimpleRecord/main.cpp b/examples/SimpleRecord/main.cpp new file mode 100644 index 0000000..d05ab5d --- /dev/null +++ b/examples/SimpleRecord/main.cpp @@ -0,0 +1,94 @@ +#include + +void RecordSample(const std::string input_uri, const std::string record_uri) +{ + // Setup Video Source + pangolin::VideoInput video(input_uri); + const pangolin::PixelFormat vid_fmt = video.PixFormat(); + const unsigned w = video.Width(); + const unsigned h = video.Height(); + + pangolin::VideoOutput recorder( record_uri ); + recorder.SetStreams(video.Streams()); + + // Create OpenGL window + pangolin::CreateWindowAndBind("Main",w,h); + + // Create viewport for video with fixed aspect + pangolin::View vVideo((float)w/h); + + // OpenGl Texture for video frame + pangolin::GlTexture texVideo(w,h,GL_RGBA); + + // Allocate image buffer. The +1 is to give ffmpeg some alignment slack + // swscale seems to have a bug which goes over the array by 1... + unsigned char* img = new unsigned char[video.SizeBytes() + 1]; + + while( !pangolin::ShouldQuit() ) + { + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + if( video.GrabNext(img,true) ) + { + // Upload to GPU as texture for display + texVideo.Upload(img, vid_fmt.channels==1 ? GL_LUMINANCE:GL_RGB, GL_UNSIGNED_BYTE); + + // Record video frame + recorder.WriteStreams(img); + } + + // Activate video viewport and render texture + vVideo.Activate(); + texVideo.RenderToViewportFlipY(); + + // Swap back buffer with front and process window events via GLUT + pangolin::FinishFrame(); + } + + delete[] img; +} + +int main( int argc, char* argv[] ) +{ + std::string record_uri = "ffmpeg:[fps=30,bps=8388608]//video.avi"; + + std::string input_uris[] = { + "dc1394:[fps=30,dma=10,size=640x480,iso=400]//0", + "convert:[fmt=RGB24]//v4l:///dev/video0", + "convert:[fmt=RGB24]//v4l:///dev/video1", + "" + }; + + if( argc >= 2 ) { + const std::string uri = std::string(argv[1]); + if( argc == 3 ) { + record_uri = std::string(argv[2]); + } + RecordSample(uri, record_uri); + }else{ + std::cout << "Usage : SimpleRecord [video-uri] [output-uri]" << std::endl << std::endl; + std::cout << "Where video-uri describes a stream or file resource, e.g." << std::endl; + std::cout << "\tfile:[realtime=1]///home/user/video/movie.pvn" << std::endl; + std::cout << "\tfile:///home/user/video/movie.avi" << std::endl; + std::cout << "\tfiles:///home/user/seqiemce/foo%03d.jpeg" << std::endl; + std::cout << "\tdc1394:[fmt=RGB24,size=640x480,fps=30,iso=400,dma=10]//0" << std::endl; + std::cout << "\tdc1394:[fmt=FORMAT7_1,size=640x480,pos=2+2,iso=400,dma=10]//0" << std::endl; + std::cout << "\tv4l:///dev/video0" << std::endl; + std::cout << "\tconvert:[fmt=RGB24]//v4l:///dev/video0" << std::endl; + std::cout << "\tmjpeg://http://127.0.0.1/?action=stream" << std::endl; + std::cout << std::endl; + + // Try to open some video device + for(int i=0; !input_uris[i].empty(); ++i ) + { + try{ + std::cout << "Trying: " << input_uris[i] << std::endl; + RecordSample(input_uris[i], record_uri); + return 0; + }catch(pangolin::VideoException) {} + } + } + + return 0; + +} diff --git a/examples/SimpleScene/CMakeLists.txt b/examples/SimpleScene/CMakeLists.txt new file mode 100644 index 0000000..2ad2549 --- /dev/null +++ b/examples/SimpleScene/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimpleScene main.cpp) +target_link_libraries(SimpleScene ${Pangolin_LIBRARIES}) diff --git a/examples/SimpleScene/main.cpp b/examples/SimpleScene/main.cpp new file mode 100644 index 0000000..2423dc6 --- /dev/null +++ b/examples/SimpleScene/main.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +int main( int /*argc*/, char** /*argv*/ ) +{ + pangolin::CreateWindowAndBind("Main",640,480); + glEnable(GL_DEPTH_TEST); + + // Define Projection and initial ModelView matrix + pangolin::OpenGlRenderState s_cam( + pangolin::ProjectionMatrix(640,480,420,420,320,240,0.2,100), + pangolin::ModelViewLookAt(-2,2,-2, 0,0,0, pangolin::AxisY) + ); + + pangolin::Renderable tree; + tree.Add( std::make_shared() ); + + // Create Interactive View in window + pangolin::SceneHandler handler(tree, s_cam); + pangolin::View& d_cam = pangolin::CreateDisplay() + .SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f/480.0f) + .SetHandler(&handler); + + d_cam.SetDrawFunction([&](pangolin::View& view){ + view.Activate(s_cam); + tree.Render(); + }); + + while( !pangolin::ShouldQuit() ) + { + // Clear screen and activate view to render into + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Swap frames and Process Events + pangolin::FinishFrame(); + } + + return 0; +} diff --git a/examples/SimpleVideo/CMakeLists.txt b/examples/SimpleVideo/CMakeLists.txt new file mode 100644 index 0000000..0be7165 --- /dev/null +++ b/examples/SimpleVideo/CMakeLists.txt @@ -0,0 +1,6 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) + +add_executable(SimpleVideo main.cpp) +target_link_libraries(SimpleVideo ${Pangolin_LIBRARIES}) diff --git a/examples/SimpleVideo/main.cpp b/examples/SimpleVideo/main.cpp new file mode 100644 index 0000000..3085b95 --- /dev/null +++ b/examples/SimpleVideo/main.cpp @@ -0,0 +1,111 @@ +/** + * @author Steven Lovegrove + * Copyright (C) 2010 Steven Lovegrove + * Imperial College London + **/ + +#include + +void SetGlFormat(GLint& glformat, GLenum& gltype, const pangolin::PixelFormat& fmt) +{ + switch( fmt.channels) { + case 1: glformat = GL_LUMINANCE; break; + case 3: glformat = GL_RGB; break; + case 4: glformat = GL_RGBA; break; + default: throw std::runtime_error("Unable to display video format"); + } + + switch (fmt.channel_bits[0]) { + case 8: gltype = GL_UNSIGNED_BYTE; break; + case 16: gltype = GL_UNSIGNED_SHORT; break; + case 32: gltype = GL_FLOAT; break; + default: throw std::runtime_error("Unknown channel format"); + } +} + +void VideoSample(const std::string uri) +{ + // Setup Video Source + pangolin::VideoInput video(uri); + const pangolin::PixelFormat vid_fmt = video.PixFormat(); + const unsigned w = video.Width(); + const unsigned h = video.Height(); + + // Work out appropriate GL channel and format options + GLint glformat; + GLenum gltype; + SetGlFormat(glformat, gltype, vid_fmt); + + // Create OpenGL window + pangolin::CreateWindowAndBind("Main",w,h); + + // Create viewport for video with fixed aspect + pangolin::View& vVideo = pangolin::Display("Video").SetAspect((float)w/h); + + // OpenGl Texture for video frame. + pangolin::GlTexture texVideo(w,h,glformat,false,0,glformat,gltype); + + unsigned char* img = new unsigned char[video.SizeBytes()]; + + for(int frame=0; !pangolin::ShouldQuit(); ++frame) + { + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + if( video.GrabNext(img,true) ) { + texVideo.Upload( img, glformat, gltype ); + } + + // Activate video viewport and render texture + vVideo.Activate(); + texVideo.RenderToViewportFlipY(); + + // Swap back buffer with front and process window events via GLUT + pangolin::FinishFrame(); + } + + delete[] img; +} + + +int main( int argc, char* argv[] ) +{ + std::string uris[] = { + "dc1394:[fps=30,dma=10,size=640x480,iso=400]//0", + "convert:[fmt=RGB24]//v4l:///dev/video0", + "convert:[fmt=RGB24]//v4l:///dev/video1", + "openni:[img1=rgb]//", + "pleora:[sn=00000215,size=640x480,pos=64x64]//", + "test:[size=160x120,n=1,fmt=RGB24]//" + "" + }; + + if( argc > 1 ) { + const std::string uri = std::string(argv[1]); + VideoSample(uri); + }else{ + std::cout << "Usage : SimpleRecord [video-uri]" << std::endl << std::endl; + std::cout << "Where video-uri describes a stream or file resource, e.g." << std::endl; + std::cout << "\tfile:[realtime=1]///home/user/video/movie.pvn" << std::endl; + std::cout << "\tfile:///home/user/video/movie.avi" << std::endl; + std::cout << "\tfiles:///home/user/seqiemce/foo%03d.jpeg" << std::endl; + std::cout << "\tdc1394:[fmt=RGB24,size=640x480,fps=30,iso=400,dma=10]//0" << std::endl; + std::cout << "\tdc1394:[fmt=FORMAT7_1,size=640x480,pos=2+2,iso=400,dma=10]//0" << std::endl; + std::cout << "\tv4l:///dev/video0" << std::endl; + std::cout << "\tconvert:[fmt=RGB24]//v4l:///dev/video0" << std::endl; + std::cout << "\tmjpeg://http://127.0.0.1/?action=stream" << std::endl; + std::cout << "\topenni:[img1=rgb]//" << std::endl; + std::cout << std::endl; + + // Try to open some video device + for(int i=0; !uris[i].empty(); ++i ) + { + try{ + std::cout << "Trying: " << uris[i] << std::endl; + VideoSample(uris[i]); + return 0; + }catch(pangolin::VideoException) { } + } + } + + return 0; +} diff --git a/examples/VBODisplay/CMakeLists.txt b/examples/VBODisplay/CMakeLists.txt new file mode 100644 index 0000000..28a5b7e --- /dev/null +++ b/examples/VBODisplay/CMakeLists.txt @@ -0,0 +1,19 @@ +# Find Pangolin (https://github.com/stevenlovegrove/Pangolin) +find_package(Pangolin 0.4 REQUIRED) +include_directories(${Pangolin_INCLUDE_DIRS}) +link_directories(${Pangolin_LIBRARY_DIRS}) +link_libraries(${Pangolin_LIBRARIES}) + +find_package(CUDA QUIET) + +# This example could be made to work with C++11, but the kernel code must be +# compiled without it. +if(CUDA_FOUND) + cuda_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + + cuda_add_executable( + VBODisplay + main.cpp kernal.cu + ) + +endif() diff --git a/examples/VBODisplay/kernal.cu b/examples/VBODisplay/kernal.cu new file mode 100644 index 0000000..07d06ba --- /dev/null +++ b/examples/VBODisplay/kernal.cu @@ -0,0 +1,30 @@ +// Colour Sine wave Kernal +// Based on kernal_colour in kernelVBO.cpp by Rob Farber +__global__ void kernel(float4* dVertexArray, uchar4 *dColorArray, + unsigned int width, unsigned int height, float time) +{ + unsigned int x = blockIdx.x*blockDim.x + threadIdx.x; + unsigned int y = blockIdx.y*blockDim.y + threadIdx.y; + + // Each thread is unique point (u,v) in interval [-1,1],[-1,1] + const float u = 2.0f* (x/(float)width) - 1.0f; + const float v = 2.0f* (y/(float)height) - 1.0f; + const float w = 0.5f * sinf(4.0f*u + time) * cosf(4.0f*v + time); + + // Update vertex array for point + dVertexArray[y*width+x] = make_float4(u, w, v, 1.0f); + + // Update colour array for point + dColorArray[y*width+x].w = 0.0f; + dColorArray[y*width+x].x = 255.0f *0.5f*(1.f+sinf(w+x)); + dColorArray[y*width+x].y = 255.0f *0.5f*(1.f+sinf(x)*cosf(y)); + dColorArray[y*width+x].z = 255.0f *0.5f*(1.f+sinf(w+time/10.0f)); +} + +extern "C" void launch_kernel(float4* dVertexArray, uchar4* dColourArray, + unsigned int width, unsigned int height, float time) +{ + dim3 block(8, 8, 1); + dim3 grid(width / block.x, height / block.y, 1); + kernel<<< grid, block>>>(dVertexArray, dColourArray, width, height, time); +} diff --git a/examples/VBODisplay/main.cpp b/examples/VBODisplay/main.cpp new file mode 100644 index 0000000..6b7f778 --- /dev/null +++ b/examples/VBODisplay/main.cpp @@ -0,0 +1,84 @@ +#include + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace pangolin; +using namespace std; + +// Mesh size +const int mesh_width=256; +const int mesh_height=256; + +extern "C" void launch_kernel(float4* dVertexArray, uchar4* dColourArray, unsigned int width, unsigned int height, float time); + +int main( int /*argc*/, char* argv[] ) +{ +// cudaGLSetGLDevice(0); + + pangolin::CreateWindowAndBind("Main",640,480); + glewInit(); + + // 3D Mouse handler requires depth testing to be enabled + glEnable(GL_DEPTH_TEST); + + // Create vertex and colour buffer objects and register them with CUDA + GlBufferCudaPtr vertex_array( + GlArrayBuffer, mesh_width*mesh_height, GL_FLOAT, 4, + cudaGraphicsMapFlagsWriteDiscard, GL_STREAM_DRAW + ); + GlBufferCudaPtr colour_array( + GlArrayBuffer, mesh_width*mesh_height, GL_UNSIGNED_BYTE, 4, + cudaGraphicsMapFlagsWriteDiscard, GL_STREAM_DRAW + ); + + // Define Camera Render Object (for view / scene browsing) + pangolin::OpenGlRenderState s_cam( + ProjectionMatrix(640,480,420,420,320,240,0.1,1000), + ModelViewLookAt(-0,2,-2, 0,0,0, AxisY) + ); + const int UI_WIDTH = 180; + + // Add named OpenGL viewport to window and provide 3D Handler + View& d_cam = pangolin::Display("cam") + .SetBounds(0.0, 1.0, Attach::Pix(UI_WIDTH), 1.0, -640.0f/480.0f) + .SetHandler(new Handler3D(s_cam)); + + // Add named Panel and bind to variables beginning 'ui' + // A Panel is just a View with a default layout and input handling + View& d_panel = pangolin::CreatePanel("ui") + .SetBounds(0.0, 1.0, 0.0, Attach::Pix(UI_WIDTH)); + + // Default hooks for exiting (Esc) and fullscreen (tab). + for(int frame=0; !pangolin::ShouldQuit(); ++frame) + { + static double time = 0; + static Var delta("ui.time delta", 0.001, 0, 0.005); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + d_cam.Activate(s_cam); + glColor3f(1.0,1.0,1.0); + + { + CudaScopedMappedPtr var(vertex_array); + CudaScopedMappedPtr car(colour_array); + launch_kernel((float4*)*var,(uchar4*)*car,mesh_width,mesh_height,time); + time += delta; + } + + pangolin::RenderVboCbo(vertex_array, colour_array); + + // Swap frames and Process Events + pangolin::FinishFrame(); + } + + return 0; +} diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 0000000..e21cf33 --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,157 @@ +include(ExternalProject) + +set(ExternConfig "") + +if( BUILD_EXTERN_GLEW ) + +######################################################### +# GLEW +######################################################### +set(glew_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/glew") +ExternalProject_Add( __glew + PREFIX "${glew_PREFIX}" + GIT_REPOSITORY https://github.com/Perlmint/glew-cmake.git + GIT_TAG 7574ab4d00b683e56adbfdec7da636529dfe65d8 + INSTALL_DIR ${glew_PREFIX} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${glew_PREFIX} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE} + -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG} + -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL} + -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO} +) + +add_library(_glew STATIC IMPORTED GLOBAL) +add_dependencies(_glew __glew) +set_target_properties(_glew PROPERTIES + IMPORTED_LOCATION_RELWITHDEBINFO ${glew_PREFIX}/lib/glew.lib + IMPORTED_LOCATION_RELEASE ${glew_PREFIX}/lib/glew.lib + IMPORTED_LOCATION_DEBUG ${glew_PREFIX}/lib/glewd.lib +) + +set(GLEW_FOUND true PARENT_SCOPE) +set(GLEW_INCLUDE_DIR "${glew_PREFIX}/include" PARENT_SCOPE) +set(GLEW_LIBRARY _glew PARENT_SCOPE) +set(GLEW_STATIC 1 PARENT_SCOPE) +set(ExternConfig "${ExternConfig} + add_library(_glew STATIC IMPORTED) + set_target_properties(_glew PROPERTIES + IMPORTED_LOCATION_RELWITHDEBINFO ${glew_PREFIX}/lib/glew.lib + IMPORTED_LOCATION_RELEASE ${glew_PREFIX}/lib/glew.lib + IMPORTED_LOCATION_DEBUG ${glew_PREFIX}/lib/glewd.lib + )") +endif() + +if( BUILD_EXTERN_LIBPNG ) + +######################################################### +# zlib +######################################################### + +set(zlib_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/zlib") +ExternalProject_Add( __zlib + PREFIX "${zlib_PREFIX}" + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG v1.2.8 + INSTALL_DIR ${zlib_PREFIX} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${zlib_PREFIX} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE} + -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG} + -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL} + -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO} +) +add_library(_zlib STATIC IMPORTED GLOBAL) +add_dependencies(_zlib __zlib) +set_target_properties(_zlib PROPERTIES + IMPORTED_LOCATION_RELEASE ${zlib_PREFIX}/lib/zlibstatic.lib + IMPORTED_LOCATION_RELWITHDEBINFO ${zlib_PREFIX}/lib/zlibstatic.lib + IMPORTED_LOCATION_DEBUG ${zlib_PREFIX}/lib/zlibstaticd.lib +) + +######################################################### +# libpng +######################################################### + +set(libpng_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libpng") +ExternalProject_Add( __libpng + PREFIX "${libpng_PREFIX}" + GIT_REPOSITORY https://github.com/glennrp/libpng.git + GIT_TAG v1.6.18 + INSTALL_DIR ${libpng_PREFIX} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${libpng_PREFIX} + -DZLIB_INCLUDE_DIR=${zlib_PREFIX}/include + -DZLIB_LIBRARY=${zlib_PREFIX}/lib/zlibstatic*.lib + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE} + -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG} + -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL} + -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO} + DEPENDS __zlib +) + +add_library(_libpng STATIC IMPORTED GLOBAL) +add_dependencies(_libpng __libpng) +set_target_properties(_libpng PROPERTIES + IMPORTED_LOCATION_RELWITHDEBINFO ${libpng_PREFIX}/lib/libpng16_static.lib + IMPORTED_LOCATION_RELEASE ${libpng_PREFIX}/lib/libpng16_static.lib + IMPORTED_LOCATION_DEBUG ${libpng_PREFIX}/lib/libpng16_staticd.lib +) + +set(PNG_FOUND true PARENT_SCOPE) +set(PNG_INCLUDE_DIR "${libpng_PREFIX}/include" PARENT_SCOPE) +set(PNG_LIBRARY _libpng PARENT_SCOPE) +set(ZLIB_LIBRARY _zlib PARENT_SCOPE) +set(ExternConfig "${ExternConfig} + add_library(_zlib STATIC IMPORTED) + set_target_properties(_zlib PROPERTIES + IMPORTED_LOCATION_RELEASE ${zlib_PREFIX}/lib/zlibstatic.lib + IMPORTED_LOCATION_RELWITHDEBINFO ${zlib_PREFIX}/lib/zlibstatic.lib + IMPORTED_LOCATION_DEBUG ${zlib_PREFIX}/lib/zlibstaticd.lib + ) + add_library(_libpng STATIC IMPORTED) + set_target_properties(_libpng PROPERTIES + IMPORTED_LOCATION_RELEASE ${libpng_PREFIX}/lib/libpng16_static.lib + IMPORTED_LOCATION_RELWITHDEBINFO ${libpng_PREFIX}/lib/libpng16_static.lib + IMPORTED_LOCATION_DEBUG ${libpng_PREFIX}/lib/libpng16_staticd.lib + )") +endif() + +if( BUILD_EXTERN_LIBJPEG ) + +######################################################### +# libjpg +######################################################### + +set(libjpeg_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libjpeg") +ExternalProject_Add( __libjpeg + PREFIX "${libjpeg_PREFIX}" + GIT_REPOSITORY https://github.com/LuaDist/libjpeg.git + GIT_TAG bc8f8be222287fec977ec3f47a5cb065cceb2ee9 + INSTALL_DIR ${libjpeg_PREFIX} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${libjpeg_PREFIX} + -DBUILD_SHARED_LIBS=false + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE} + -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG} + -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL} + -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO} +) + +add_library(_libjpeg STATIC IMPORTED GLOBAL) +add_dependencies(_libjpeg __libjpeg) +set_target_properties(_libjpeg PROPERTIES + IMPORTED_LOCATION ${libjpeg_PREFIX}/lib/jpeg.lib +) + +set(JPEG_FOUND true PARENT_SCOPE) +set(JPEG_INCLUDE_DIR "${libjpeg_PREFIX}/include" PARENT_SCOPE) +set(JPEG_LIBRARY _libjpeg PARENT_SCOPE) +set(ExternConfig "${ExternConfig} + add_library(_libjpeg STATIC IMPORTED) + set_target_properties(_libjpeg PROPERTIES + IMPORTED_LOCATION ${libjpeg_PREFIX}/lib/jpeg.lib + )") +endif() + +set(ExternConfig "${ExternConfig}" PARENT_SCOPE) diff --git a/external/pybind11/.appveyor.yml b/external/pybind11/.appveyor.yml new file mode 100644 index 0000000..b150f10 --- /dev/null +++ b/external/pybind11/.appveyor.yml @@ -0,0 +1,66 @@ +version: 1.0.{build} +image: +- Visual Studio 2017 +- Visual Studio 2015 +test: off +build: + parallel: true +platform: +- x64 +- x86 +environment: + matrix: + - PYTHON: 36 + CPP: 14 + CONFIG: Debug + - PYTHON: 27 + CPP: 14 + CONFIG: Debug + - CONDA: 36 + CPP: latest + CONFIG: Release +matrix: + exclude: + - image: Visual Studio 2015 + platform: x86 + - image: Visual Studio 2015 + CPP: latest + - image: Visual Studio 2017 + CPP: latest + platform: x86 +install: +- ps: | + if ($env:PLATFORM -eq "x64") { $env:CMAKE_ARCH = "x64" } + if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { + $env:CMAKE_GENERATOR = "Visual Studio 15 2017" + $env:CMAKE_INCLUDE_PATH = "C:\Libraries\boost_1_64_0" + } else { + $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + } + if ($env:PYTHON) { + if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } + $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" + pip install --disable-pip-version-check --user --upgrade pip wheel + pip install pytest numpy + } elseif ($env:CONDA) { + if ($env:CONDA -eq "27") { $env:CONDA = "" } + if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } + $env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH" + $env:PYTHONHOME = "C:\Miniconda$env:CONDA" + conda install -y -q pytest numpy scipy + } +- ps: | + Start-FileDownload 'http://bitbucket.org/eigen/eigen/get/3.3.3.zip' + 7z x 3.3.3.zip -y > $null + $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f;$env:CMAKE_INCLUDE_PATH" +build_script: +- cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%" + -DPYBIND11_CPP_STANDARD=/std:c++%CPP% + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DCMAKE_SUPPRESS_REGENERATION=1 +- set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" +- cmake --build . --config %CONFIG% --target pytest -- /m /v:m /logger:%MSBuildLogger% +- cmake --build . --config %CONFIG% --target cpptest -- /m /v:m /logger:%MSBuildLogger% +- if "%CPP%"=="latest" (cmake --build . --config %CONFIG% --target test_cmake_build -- /m /v:m /logger:%MSBuildLogger%) +on_failure: if exist "tests\test_cmake_build" type tests\test_cmake_build\*.log* diff --git a/external/pybind11/.gitignore b/external/pybind11/.gitignore new file mode 100644 index 0000000..c444c17 --- /dev/null +++ b/external/pybind11/.gitignore @@ -0,0 +1,37 @@ +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake +.DS_Store +*.so +*.pyd +*.dll +*.sln +*.sdf +*.opensdf +*.vcxproj +*.filters +example.dir +Win32 +x64 +Release +Debug +.vs +CTestTestfile.cmake +Testing +autogen +MANIFEST +/.ninja_* +/*.ninja +/docs/.build +*.py[co] +*.egg-info +*~ +.DS_Store +/dist +/build +/cmake/ +.cache/ +sosize-*.txt +pybind11Config*.cmake +pybind11Targets.cmake diff --git a/external/pybind11/.gitmodules b/external/pybind11/.gitmodules new file mode 100644 index 0000000..5191885 --- /dev/null +++ b/external/pybind11/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools/clang"] + path = tools/clang + url = https://github.com/wjakob/clang-cindex-python3 diff --git a/external/pybind11/.readthedocs.yml b/external/pybind11/.readthedocs.yml new file mode 100644 index 0000000..c9c6161 --- /dev/null +++ b/external/pybind11/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: docs/requirements.txt diff --git a/external/pybind11/.travis.yml b/external/pybind11/.travis.yml new file mode 100644 index 0000000..2853ac7 --- /dev/null +++ b/external/pybind11/.travis.yml @@ -0,0 +1,212 @@ +language: cpp +dist: trusty +sudo: false +matrix: + include: + # This config does a few things: + # - Checks C++ and Python code styles (check-style.sh and flake8). + # - Makes sure sphinx can build the docs without any errors or warnings. + # - Tests setup.py sdist and install (all header files should be present). + # - Makes sure that everything still works without optional deps (numpy/scipy/eigen) and + # also tests the automatic discovery functions in CMake (Python version, C++ standard). + - os: linux + env: STYLE DOCS PIP + cache: false + before_install: + - pyenv global $(pyenv whence 2to3) # activate all python versions + - PY_CMD=python3 + - $PY_CMD -m pip install --user --upgrade pip wheel + install: + - $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe flake8 pep8-naming pytest + - curl -fsSL ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.12.linux.bin.tar.gz | tar xz + - export PATH="$PWD/doxygen-1.8.12/bin:$PATH" + script: + - tools/check-style.sh + - flake8 + - $PY_CMD -m sphinx -W -b html docs docs/.build + - | + # Make sure setup.py distributes and installs all the headers + $PY_CMD setup.py sdist + $PY_CMD -m pip install --user -U ./dist/* + installed=$($PY_CMD -c "import pybind11; print(pybind11.get_include(True) + '/pybind11')") + diff -rq $installed ./include/pybind11 + - | + # Barebones build + cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON + make pytest -j 2 + make cpptest -j 2 + # The following are regular test configurations, including optional dependencies. + # With regard to each other they differ in Python version, C++ standard and compiler. + - os: linux + env: PYTHON=2.7 CPP=11 GCC=4.8 + addons: + apt: + packages: [cmake=2.\*, cmake-data=2.\*] + - os: linux + env: PYTHON=3.6 CPP=11 GCC=4.8 + addons: + apt: + sources: [deadsnakes] + packages: [python3.6-dev python3.6-venv, cmake=2.\*, cmake-data=2.\*] + - sudo: true + services: docker + env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1 + - sudo: true + services: docker + env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 + - sudo: true + services: docker + env: PYTHON=3.6 CPP=17 GCC=7 + - os: linux + env: PYTHON=3.6 CPP=17 CLANG=5.0 + addons: + apt: + sources: [deadsnakes, llvm-toolchain-trusty-5.0, ubuntu-toolchain-r-test] + packages: [python3.6-dev python3.6-venv clang-5.0 llvm-5.0-dev, lld-5.0] + - os: osx + osx_image: xcode7.3 + env: PYTHON=2.7 CPP=14 CLANG CMAKE=1 + - os: osx + osx_image: xcode8.3 + env: PYTHON=3.6 CPP=14 CLANG DEBUG=1 + # Test a PyPy 2.7 build + - os: linux + env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8 + addons: + apt: + packages: [libblas-dev, liblapack-dev, gfortran] + # Build in 32-bit mode and tests against the CMake-installed version + - sudo: true + services: docker + env: ARCH=i386 PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 + script: + - | + $SCRIPT_RUN_PREFIX sh -c "set -e + cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 + make install + cp -a tests /pybind11-tests + mkdir /build-tests && cd /build-tests + cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON + make pytest -j 2" +cache: + directories: + - $HOME/.local/bin + - $HOME/.local/lib + - $HOME/.local/include + - $HOME/Library/Python +before_install: +- | + # Configure build variables + if [ "$TRAVIS_OS_NAME" = "linux" ]; then + if [ -n "$CLANG" ]; then + export CXX=clang++-$CLANG CC=clang-$CLANG + COMPILER_PACKAGES="clang-$CLANG llvm-$CLANG-dev" + else + if [ -z "$GCC" ]; then GCC=4.8 + else COMPILER_PACKAGES=g++-$GCC + fi + export CXX=g++-$GCC CC=gcc-$GCC + fi + if [ "$GCC" = "6" ]; then DOCKER=${ARCH:+$ARCH/}debian:stretch + elif [ "$GCC" = "7" ]; then DOCKER=debian:buster + fi + elif [ "$TRAVIS_OS_NAME" = "osx" ]; then + export CXX=clang++ CC=clang; + fi + if [ -n "$CPP" ]; then CPP=-std=c++$CPP; fi + if [ "${PYTHON:0:1}" = "3" ]; then PY=3; fi + if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DCMAKE_BUILD_TYPE=Debug"; fi +- | + # Initialize environment + set -e + if [ -n "$DOCKER" ]; then + docker pull $DOCKER + + containerid=$(docker run --detach --tty \ + --volume="$PWD":/pybind11 --workdir=/pybind11 \ + --env="CC=$CC" --env="CXX=$CXX" --env="DEBIAN_FRONTEND=$DEBIAN_FRONTEND" \ + --env=GCC_COLORS=\ \ + $DOCKER) + SCRIPT_RUN_PREFIX="docker exec --tty $containerid" + $SCRIPT_RUN_PREFIX sh -c 'for s in 0 15; do sleep $s; apt-get update && apt-get -qy dist-upgrade && break; done' + else + if [ "$PYPY" = "5.8" ]; then + curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux64.tar.bz2 | tar xj + PY_CMD=$(echo `pwd`/pypy2-v5.8.0-linux64/bin/pypy) + CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD" + else + PY_CMD=python$PYTHON + if [ "$TRAVIS_OS_NAME" = "osx" ]; then + if [ "$PY" = "3" ]; then + brew install python$PY; + else + curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user + fi + fi + fi + if [ "$PY" = 3 ] || [ -n "$PYPY" ]; then + $PY_CMD -m ensurepip --user + fi + $PY_CMD -m pip install --user --upgrade pip wheel + fi + set +e +install: +- | + # Install dependencies + set -e + if [ -n "$DOCKER" ]; then + if [ -n "$DEBUG" ]; then + PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg" + CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DPYTHON_EXECUTABLE=/usr/bin/python${PYTHON}dm" + fi + $SCRIPT_RUN_PREFIX sh -c "for s in 0 15; do sleep \$s; \ + apt-get -qy --no-install-recommends install \ + $PY_DEBUG python$PYTHON-dev python$PY-pytest python$PY-scipy \ + libeigen3-dev libboost-dev cmake make ${COMPILER_PACKAGES} && break; done" + else + + if [ "$CLANG" = "5.0" ]; then + if ! [ -d ~/.local/include/c++/v1 ]; then + # Neither debian nor llvm provide a libc++ 5.0 deb; luckily it's fairly quick + # to build, install (and cache), so do it ourselves: + git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source + git clone https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx -b release_50 + git clone https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi -b release_50 + mkdir llvm-build && cd llvm-build + # Building llvm requires a newer cmake than is provided by the trusty container: + CMAKE_VER=cmake-3.8.0-Linux-x86_64 + curl https://cmake.org/files/v3.8/$CMAKE_VER.tar.gz | tar xz + ./$CMAKE_VER/bin/cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local ../llvm-source + make -j2 install-cxxabi install-cxx + cp -a include/c++/v1/*cxxabi*.h ~/.local/include/c++/v1 + cd .. + fi + export CXXFLAGS="-isystem $HOME/.local/include/c++/v1 -stdlib=libc++" + export LDFLAGS="-L$HOME/.local/lib -fuse-ld=lld-$CLANG" + export LD_LIBRARY_PATH="$HOME/.local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + if [ "$CPP" = "-std=c++17" ]; then CPP="-std=c++1z"; fi + fi + + export NPY_NUM_BUILD_JOBS=2 + echo "Installing pytest, numpy, scipy..." + ${PYPY:+travis_wait 30} $PY_CMD -m pip install --user --upgrade pytest numpy scipy \ + ${PYPY:+--extra-index-url https://imaginary.ca/trusty-pypi} + echo "done." + + wget -q -O eigen.tar.gz https://bitbucket.org/eigen/eigen/get/3.3.3.tar.gz + tar xzf eigen.tar.gz + export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+:}$PWD/eigen-eigen-67e894c6cd8f" + fi + set +e +script: +- $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} + -DPYBIND11_PYTHON_VERSION=$PYTHON + -DPYBIND11_CPP_STANDARD=$CPP + -DPYBIND11_WERROR=${WERROR:-ON} + -DDOWNLOAD_CATCH=ON +- $SCRIPT_RUN_PREFIX make pytest -j 2 +- $SCRIPT_RUN_PREFIX make cpptest -j 2 +- if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi +after_failure: cat tests/test_cmake_build/*.log* +after_script: +- if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi diff --git a/external/pybind11/CMakeLists.txt b/external/pybind11/CMakeLists.txt new file mode 100644 index 0000000..4280ba7 --- /dev/null +++ b/external/pybind11/CMakeLists.txt @@ -0,0 +1,155 @@ +# CMakeLists.txt -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +if (POLICY CMP0048) + # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning: + cmake_policy(SET CMP0048 NEW) +endif() + +# CMake versions < 3.4.0 do not support try_compile/pthread checks without C as active language. +if(CMAKE_VERSION VERSION_LESS 3.4.0) + project(pybind11) +else() + project(pybind11 CXX) +endif() + +# Check if pybind11 is being used directly or via add_subdirectory +set(PYBIND11_MASTER_PROJECT OFF) +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(PYBIND11_MASTER_PROJECT ON) +endif() + +option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools") + +include(pybind11Tools) + +# Cache variables so pybind11_add_module can be used in parent projects +set(PYBIND11_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include" CACHE INTERNAL "") +set(PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} CACHE INTERNAL "") +set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "") +set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "") +set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "") + +# NB: when adding a header don't forget to also add it to setup.py +set(PYBIND11_HEADERS + include/pybind11/detail/class.h + include/pybind11/detail/common.h + include/pybind11/detail/descr.h + include/pybind11/detail/init.h + include/pybind11/detail/internals.h + include/pybind11/detail/typeid.h + include/pybind11/attr.h + include/pybind11/buffer_info.h + include/pybind11/cast.h + include/pybind11/chrono.h + include/pybind11/common.h + include/pybind11/complex.h + include/pybind11/options.h + include/pybind11/eigen.h + include/pybind11/embed.h + include/pybind11/eval.h + include/pybind11/functional.h + include/pybind11/numpy.h + include/pybind11/operators.h + include/pybind11/pybind11.h + include/pybind11/pytypes.h + include/pybind11/stl.h + include/pybind11/stl_bind.h +) +string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" + PYBIND11_HEADERS "${PYBIND11_HEADERS}") + +if (PYBIND11_TEST) + add_subdirectory(tests) +endif() + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# extract project version from source +file(STRINGS "${PYBIND11_INCLUDE_DIR}/pybind11/detail/common.h" pybind11_version_defines + REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") +foreach(ver ${pybind11_version_defines}) + if (ver MATCHES "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") + set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") + endif() +endforeach() +set(${PROJECT_NAME}_VERSION ${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}) +message(STATUS "pybind11 v${${PROJECT_NAME}_VERSION}") + +option (USE_PYTHON_INCLUDE_DIR "Install pybind11 headers in Python include directory instead of default installation prefix" OFF) +if (USE_PYTHON_INCLUDE_DIR) + file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) +endif() + +if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0 + # Build an interface library target: + add_library(pybind11 INTERFACE) + add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target + target_include_directories(pybind11 INTERFACE $ + $ + $) + target_compile_options(pybind11 INTERFACE $) + + add_library(module INTERFACE) + add_library(pybind11::module ALIAS module) + if(NOT MSVC) + target_compile_options(module INTERFACE -fvisibility=hidden) + endif() + target_link_libraries(module INTERFACE pybind11::pybind11) + if(WIN32 OR CYGWIN) + target_link_libraries(module INTERFACE $) + elseif(APPLE) + target_link_libraries(module INTERFACE "-undefined dynamic_lookup") + endif() + + add_library(embed INTERFACE) + add_library(pybind11::embed ALIAS embed) + target_link_libraries(embed INTERFACE pybind11::pybind11 $) +endif() + +if (PYBIND11_INSTALL) + install(DIRECTORY ${PYBIND11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + # GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share". + set(PYBIND11_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for pybind11Config.cmake") + + configure_package_config_file(tools/${PROJECT_NAME}Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + # Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does + # not depend on architecture specific settings or libraries. + set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) + unset(CMAKE_SIZEOF_VOID_P) + write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${${PROJECT_NAME}_VERSION} + COMPATIBILITY AnyNewerVersion) + set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + tools/FindPythonLibsNew.cmake + tools/pybind11Tools.cmake + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) + if(NOT PYBIND11_EXPORT_NAME) + set(PYBIND11_EXPORT_NAME "${PROJECT_NAME}Targets") + endif() + + install(TARGETS pybind11 module embed + EXPORT "${PYBIND11_EXPORT_NAME}") + if(PYBIND11_MASTER_PROJECT) + install(EXPORT "${PYBIND11_EXPORT_NAME}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + endif() + endif() +endif() diff --git a/external/pybind11/CONTRIBUTING.md b/external/pybind11/CONTRIBUTING.md new file mode 100644 index 0000000..2beaf8d --- /dev/null +++ b/external/pybind11/CONTRIBUTING.md @@ -0,0 +1,37 @@ +Thank you for your interest in this project! Please refer to the following +sections on how to contribute code and bug reports. + +### Reporting bugs + +At the moment, this project is run in the spare time of a single person +([Wenzel Jakob](http://rgl.epfl.ch/people/wjakob)) with very limited resources +for issue tracker tickets. Thus, before submitting a question or bug report, +please take a moment of your time and ensure that your issue isn't already +discussed in the project documentation provided at +[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest). + +Assuming that you have identified a previously unknown problem or an important +question, it's essential that you submit a self-contained and minimal piece of +code that reproduces the problem. In other words: no external dependencies, +isolate the function(s) that cause breakage, submit matched and complete C++ +and Python snippets that can be easily compiled and run on my end. + +## Pull requests +Contributions are submitted, reviewed, and accepted using Github pull requests. +Please refer to [this +article](https://help.github.com/articles/using-pull-requests) for details and +adhere to the following rules to make the process as smooth as possible: + +* Make a new branch for every feature you're working on. +* Make small and clean pull requests that are easy to review but make sure they + do add value by themselves. +* Add tests for any new functionality and run the test suite (``make pytest``) + to ensure that no existing features break. +* This project has a strong focus on providing general solutions using a + minimal amount of code, thus small pull requests are greatly preferred. + +### License + +pybind11 is provided under a BSD-style license that can be found in the +``LICENSE`` file. By using, distributing, or contributing to this project, you +agree to the terms and conditions of this license. diff --git a/external/pybind11/ISSUE_TEMPLATE.md b/external/pybind11/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..75df399 --- /dev/null +++ b/external/pybind11/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +Make sure you've completed the following steps before submitting your issue -- thank you! + +1. Check if your question has already been answered in the [FAQ](http://pybind11.readthedocs.io/en/latest/faq.html) section. +2. Make sure you've read the [documentation](http://pybind11.readthedocs.io/en/latest/). Your issue may be addressed there. +3. If those resources didn't help and you only have a short question (not a bug report), consider asking in the [Gitter chat room](https://gitter.im/pybind/Lobby). +4. If you have a genuine bug report or a more complex question which is not answered in the previous items (or not suitable for chat), please fill in the details below. +5. Include a self-contained and minimal piece of code that reproduces the problem. If that's not possible, try to make the description as clear as possible. + +*After reading, remove this checklist and the template text in parentheses below.* + +## Issue description + +(Provide a short description, state the expected behavior and what actually happens.) + +## Reproducible example code + +(The code should be minimal, have no external dependencies, isolate the function(s) that cause breakage. Submit matched and complete C++ and Python snippets that can be easily compiled and run to diagnose the issue.) diff --git a/external/pybind11/LICENSE b/external/pybind11/LICENSE new file mode 100644 index 0000000..ccf4e97 --- /dev/null +++ b/external/pybind11/LICENSE @@ -0,0 +1,36 @@ +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +You are under no obligation whatsoever to provide any bug fixes, patches, or +upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to the author of this software, without +imposing a separate written license agreement for such Enhancements, then you +hereby grant the following license: a non-exclusive, royalty-free perpetual +license to install, use, modify, prepare derivative works, incorporate into +other computer software, distribute, and sublicense such enhancements or +derivative works thereof, in binary and source code form. diff --git a/external/pybind11/MANIFEST.in b/external/pybind11/MANIFEST.in new file mode 100644 index 0000000..6e57bae --- /dev/null +++ b/external/pybind11/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include include/pybind11 *.h +include LICENSE README.md CONTRIBUTING.md diff --git a/external/pybind11/README.md b/external/pybind11/README.md new file mode 100644 index 0000000..4477882 --- /dev/null +++ b/external/pybind11/README.md @@ -0,0 +1,129 @@ +![pybind11 logo](https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png) + +# pybind11 — Seamless operability between C++11 and Python + +[![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=master)](http://pybind11.readthedocs.org/en/master/?badge=master) +[![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=stable)](http://pybind11.readthedocs.org/en/stable/?badge=stable) +[![Gitter chat](https://img.shields.io/gitter/room/gitterHQ/gitter.svg)](https://gitter.im/pybind/Lobby) +[![Build Status](https://travis-ci.org/pybind/pybind11.svg?branch=master)](https://travis-ci.org/pybind/pybind11) +[![Build status](https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true)](https://ci.appveyor.com/project/wjakob/pybind11) + +**pybind11** is a lightweight header-only library that exposes C++ types in Python +and vice versa, mainly to create Python bindings of existing C++ code. Its +goals and syntax are similar to the excellent +[Boost.Python](http://www.boost.org/doc/libs/1_58_0/libs/python/doc/) library +by David Abrahams: to minimize boilerplate code in traditional extension +modules by inferring type information using compile-time introspection. + +The main issue with Boost.Python—and the reason for creating such a similar +project—is Boost. Boost is an enormously large and complex suite of utility +libraries that works with almost every C++ compiler in existence. This +compatibility has its cost: arcane template tricks and workarounds are +necessary to support the oldest and buggiest of compiler specimens. Now that +C++11-compatible compilers are widely available, this heavy machinery has +become an excessively large and unnecessary dependency. + +Think of this library as a tiny self-contained version of Boost.Python with +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~4K lines of code and depend on +Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This +compact implementation was possible thanks to some of the new C++11 language +features (specifically: tuples, lambda functions and variadic templates). Since +its creation, this library has grown beyond Boost.Python in many ways, leading +to dramatically simpler binding code in many common situations. + +Tutorial and reference documentation is provided at +[http://pybind11.readthedocs.org/en/master](http://pybind11.readthedocs.org/en/master). +A PDF version of the manual is available +[here](https://media.readthedocs.org/pdf/pybind11/master/pybind11.pdf). + +## Core features +pybind11 can map the following core C++ features to Python + +- Functions accepting and returning custom data structures per value, reference, or pointer +- Instance methods and static methods +- Overloaded functions +- Instance attributes and static attributes +- Arbitrary exception types +- Enumerations +- Callbacks +- Iterators and ranges +- Custom operators +- Single and multiple inheritance +- STL data structures +- Iterators and ranges +- Smart pointers with reference counting like ``std::shared_ptr`` +- Internal references with correct reference counting +- C++ classes with virtual (and pure virtual) methods can be extended in Python + +## Goodies +In addition to the core functionality, pybind11 provides some extra goodies: + +- Python 2.7, 3.x, and PyPy (PyPy2.7 >= 5.7) are supported with an + implementation-agnostic interface. + +- It is possible to bind C++11 lambda functions with captured variables. The + lambda capture data is stored inside the resulting Python function object. + +- pybind11 uses C++11 move constructors and move assignment operators whenever + possible to efficiently transfer custom data types. + +- It's easy to expose the internal storage of custom data types through + Pythons' buffer protocols. This is handy e.g. for fast conversion between + C++ matrix classes like Eigen and NumPy without expensive copy operations. + +- pybind11 can automatically vectorize functions so that they are transparently + applied to all entries of one or more NumPy array arguments. + +- Python's slice-based access and assignment operations can be supported with + just a few lines of code. + +- Everything is contained in just a few header files; there is no need to link + against any additional libraries. + +- Binaries are generally smaller by a factor of at least 2 compared to + equivalent bindings generated by Boost.Python. A recent pybind11 conversion + of PyRosetta, an enormous Boost.Python binding project, + [reported](http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf) a binary + size reduction of **5.4x** and compile time reduction by **5.8x**. + +- When supported by the compiler, two new C++14 features (relaxed constexpr and + return value deduction) are used to precompute function signatures at compile + time, leading to smaller binaries. + +- With little extra effort, C++ types can be pickled and unpickled similar to + regular Python objects. + +## Supported compilers + +1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer) +2. GCC 4.8 or newer +3. Microsoft Visual Studio 2015 Update 3 or newer +4. Intel C++ compiler 16 or newer (15 with a [workaround](https://github.com/pybind/pybind11/issues/276)) +5. Cygwin/GCC (tested on 2.5.1) + +## About + +This project was created by [Wenzel Jakob](http://rgl.epfl.ch/people/wjakob). +Significant features and/or improvements to the code were contributed by +Jonas Adler, +Sylvain Corlay, +Trent Houliston, +Axel Huebl, +@hulucc, +Sergey Lyskov +Johan Mabille, +Tomasz Miąsko, +Dean Moldovan, +Ben Pritchard, +Jason Rhinelander, +Boris Schäling, +Pim Schellart, +Ivan Smirnov, and +Patrick Stewart. + +### License + +pybind11 is provided under a BSD-style license that can be found in the +``LICENSE`` file. By using, distributing, or contributing to this project, +you agree to the terms and conditions of this license. diff --git a/external/pybind11/docs/Doxyfile b/external/pybind11/docs/Doxyfile new file mode 100644 index 0000000..1b9d129 --- /dev/null +++ b/external/pybind11/docs/Doxyfile @@ -0,0 +1,20 @@ +PROJECT_NAME = pybind11 +INPUT = ../include/pybind11/ +RECURSIVE = YES + +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_OUTPUT = .build/doxygenxml +XML_PROGRAMLISTING = YES + +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +EXPAND_AS_DEFINED = PYBIND11_RUNTIME_EXCEPTION + +ALIASES = "rst=\verbatim embed:rst" +ALIASES += "endrst=\endverbatim" + +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO diff --git a/external/pybind11/docs/_static/theme_overrides.css b/external/pybind11/docs/_static/theme_overrides.css new file mode 100644 index 0000000..1071809 --- /dev/null +++ b/external/pybind11/docs/_static/theme_overrides.css @@ -0,0 +1,11 @@ +.wy-table-responsive table td, +.wy-table-responsive table th { + white-space: initial !important; +} +.rst-content table.docutils td { + vertical-align: top !important; +} +div[class^='highlight'] pre { + white-space: pre; + white-space: pre-wrap; +} diff --git a/external/pybind11/docs/advanced/cast/chrono.rst b/external/pybind11/docs/advanced/cast/chrono.rst new file mode 100644 index 0000000..8c6b3d7 --- /dev/null +++ b/external/pybind11/docs/advanced/cast/chrono.rst @@ -0,0 +1,81 @@ +Chrono +====== + +When including the additional header file :file:`pybind11/chrono.h` conversions +from C++11 chrono datatypes to python datetime objects are automatically enabled. +This header also enables conversions of python floats (often from sources such +as ``time.monotonic()``, ``time.perf_counter()`` and ``time.process_time()``) +into durations. + +An overview of clocks in C++11 +------------------------------ + +A point of confusion when using these conversions is the differences between +clocks provided in C++11. There are three clock types defined by the C++11 +standard and users can define their own if needed. Each of these clocks have +different properties and when converting to and from python will give different +results. + +The first clock defined by the standard is ``std::chrono::system_clock``. This +clock measures the current date and time. However, this clock changes with to +updates to the operating system time. For example, if your time is synchronised +with a time server this clock will change. This makes this clock a poor choice +for timing purposes but good for measuring the wall time. + +The second clock defined in the standard is ``std::chrono::steady_clock``. +This clock ticks at a steady rate and is never adjusted. This makes it excellent +for timing purposes, however the value in this clock does not correspond to the +current date and time. Often this clock will be the amount of time your system +has been on, although it does not have to be. This clock will never be the same +clock as the system clock as the system clock can change but steady clocks +cannot. + +The third clock defined in the standard is ``std::chrono::high_resolution_clock``. +This clock is the clock that has the highest resolution out of the clocks in the +system. It is normally a typedef to either the system clock or the steady clock +but can be its own independent clock. This is important as when using these +conversions as the types you get in python for this clock might be different +depending on the system. +If it is a typedef of the system clock, python will get datetime objects, but if +it is a different clock they will be timedelta objects. + +Provided conversions +-------------------- + +.. rubric:: C++ to Python + +- ``std::chrono::system_clock::time_point`` → ``datetime.datetime`` + System clock times are converted to python datetime instances. They are + in the local timezone, but do not have any timezone information attached + to them (they are naive datetime objects). + +- ``std::chrono::duration`` → ``datetime.timedelta`` + Durations are converted to timedeltas, any precision in the duration + greater than microseconds is lost by rounding towards zero. + +- ``std::chrono::[other_clocks]::time_point`` → ``datetime.timedelta`` + Any clock time that is not the system clock is converted to a time delta. + This timedelta measures the time from the clocks epoch to now. + +.. rubric:: Python to C++ + +- ``datetime.datetime`` → ``std::chrono::system_clock::time_point`` + Date/time objects are converted into system clock timepoints. Any + timezone information is ignored and the type is treated as a naive + object. + +- ``datetime.timedelta`` → ``std::chrono::duration`` + Time delta are converted into durations with microsecond precision. + +- ``datetime.timedelta`` → ``std::chrono::[other_clocks]::time_point`` + Time deltas that are converted into clock timepoints are treated as + the amount of time from the start of the clocks epoch. + +- ``float`` → ``std::chrono::duration`` + Floats that are passed to C++ as durations be interpreted as a number of + seconds. These will be converted to the duration using ``duration_cast`` + from the float. + +- ``float`` → ``std::chrono::[other_clocks]::time_point`` + Floats that are passed to C++ as time points will be interpreted as the + number of seconds from the start of the clocks epoch. diff --git a/external/pybind11/docs/advanced/cast/custom.rst b/external/pybind11/docs/advanced/cast/custom.rst new file mode 100644 index 0000000..e4f99ac --- /dev/null +++ b/external/pybind11/docs/advanced/cast/custom.rst @@ -0,0 +1,91 @@ +Custom type casters +=================== + +In very rare cases, applications may require custom type casters that cannot be +expressed using the abstractions provided by pybind11, thus requiring raw +Python C API calls. This is fairly advanced usage and should only be pursued by +experts who are familiar with the intricacies of Python reference counting. + +The following snippets demonstrate how this works for a very simple ``inty`` +type that that should be convertible from Python types that provide a +``__int__(self)`` method. + +.. code-block:: cpp + + struct inty { long long_value; }; + + void print(inty s) { + std::cout << s.long_value << std::endl; + } + +The following Python snippet demonstrates the intended usage from the Python side: + +.. code-block:: python + + class A: + def __int__(self): + return 123 + + from example import print + print(A()) + +To register the necessary conversion routines, it is necessary to add +a partial overload to the ``pybind11::detail::type_caster`` template. +Although this is an implementation detail, adding partial overloads to this +type is explicitly allowed. + +.. code-block:: cpp + + namespace pybind11 { namespace detail { + template <> struct type_caster { + public: + /** + * This macro establishes the name 'inty' in + * function signatures and declares a local variable + * 'value' of type inty + */ + PYBIND11_TYPE_CASTER(inty, _("inty")); + + /** + * Conversion part 1 (Python->C++): convert a PyObject into a inty + * instance or return false upon failure. The second argument + * indicates whether implicit conversions should be applied. + */ + bool load(handle src, bool) { + /* Extract PyObject from handle */ + PyObject *source = src.ptr(); + /* Try converting into a Python integer value */ + PyObject *tmp = PyNumber_Long(source); + if (!tmp) + return false; + /* Now try to convert into a C++ int */ + value.long_value = PyLong_AsLong(tmp); + Py_DECREF(tmp); + /* Ensure return code was OK (to avoid out-of-range errors etc) */ + return !(value.long_value == -1 && !PyErr_Occurred()); + } + + /** + * Conversion part 2 (C++ -> Python): convert an inty instance into + * a Python object. The second and third arguments are used to + * indicate the return value policy and parent object (for + * ``return_value_policy::reference_internal``) and are generally + * ignored by implicit casters. + */ + static handle cast(inty src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLong(src.long_value); + } + }; + }} // namespace pybind11::detail + +.. note:: + + A ``type_caster`` defined with ``PYBIND11_TYPE_CASTER(T, ...)`` requires + that ``T`` is default-constructible (``value`` is first default constructed + and then ``load()`` assigns to it). + +.. warning:: + + When using custom type casters, it's important to declare them consistently + in every compilation unit of the Python extension module. Otherwise, + undefined behavior can ensue. diff --git a/external/pybind11/docs/advanced/cast/eigen.rst b/external/pybind11/docs/advanced/cast/eigen.rst new file mode 100644 index 0000000..acdb51d --- /dev/null +++ b/external/pybind11/docs/advanced/cast/eigen.rst @@ -0,0 +1,310 @@ +Eigen +##### + +`Eigen `_ is C++ header-based library for dense and +sparse linear algebra. Due to its popularity and widespread adoption, pybind11 +provides transparent conversion and limited mapping support between Eigen and +Scientific Python linear algebra data types. + +To enable the built-in Eigen support you must include the optional header file +:file:`pybind11/eigen.h`. + +Pass-by-value +============= + +When binding a function with ordinary Eigen dense object arguments (for +example, ``Eigen::MatrixXd``), pybind11 will accept any input value that is +already (or convertible to) a ``numpy.ndarray`` with dimensions compatible with +the Eigen type, copy its values into a temporary Eigen variable of the +appropriate type, then call the function with this temporary variable. + +Sparse matrices are similarly copied to or from +``scipy.sparse.csr_matrix``/``scipy.sparse.csc_matrix`` objects. + +Pass-by-reference +================= + +One major limitation of the above is that every data conversion implicitly +involves a copy, which can be both expensive (for large matrices) and disallows +binding functions that change their (Matrix) arguments. Pybind11 allows you to +work around this by using Eigen's ``Eigen::Ref`` class much as you +would when writing a function taking a generic type in Eigen itself (subject to +some limitations discussed below). + +When calling a bound function accepting a ``Eigen::Ref`` +type, pybind11 will attempt to avoid copying by using an ``Eigen::Map`` object +that maps into the source ``numpy.ndarray`` data: this requires both that the +data types are the same (e.g. ``dtype='float64'`` and ``MatrixType::Scalar`` is +``double``); and that the storage is layout compatible. The latter limitation +is discussed in detail in the section below, and requires careful +consideration: by default, numpy matrices and eigen matrices are *not* storage +compatible. + +If the numpy matrix cannot be used as is (either because its types differ, e.g. +passing an array of integers to an Eigen paramater requiring doubles, or +because the storage is incompatible), pybind11 makes a temporary copy and +passes the copy instead. + +When a bound function parameter is instead ``Eigen::Ref`` (note the +lack of ``const``), pybind11 will only allow the function to be called if it +can be mapped *and* if the numpy array is writeable (that is +``a.flags.writeable`` is true). Any access (including modification) made to +the passed variable will be transparently carried out directly on the +``numpy.ndarray``. + +This means you can can write code such as the following and have it work as +expected: + +.. code-block:: cpp + + void scale_by_2(Eigen::Ref v) { + v *= 2; + } + +Note, however, that you will likely run into limitations due to numpy and +Eigen's difference default storage order for data; see the below section on +:ref:`storage_orders` for details on how to bind code that won't run into such +limitations. + +.. note:: + + Passing by reference is not supported for sparse types. + +Returning values to Python +========================== + +When returning an ordinary dense Eigen matrix type to numpy (e.g. +``Eigen::MatrixXd`` or ``Eigen::RowVectorXf``) pybind11 keeps the matrix and +returns a numpy array that directly references the Eigen matrix: no copy of the +data is performed. The numpy array will have ``array.flags.owndata`` set to +``False`` to indicate that it does not own the data, and the lifetime of the +stored Eigen matrix will be tied to the returned ``array``. + +If you bind a function with a non-reference, ``const`` return type (e.g. +``const Eigen::MatrixXd``), the same thing happens except that pybind11 also +sets the numpy array's ``writeable`` flag to false. + +If you return an lvalue reference or pointer, the usual pybind11 rules apply, +as dictated by the binding function's return value policy (see the +documentation on :ref:`return_value_policies` for full details). That means, +without an explicit return value policy, lvalue references will be copied and +pointers will be managed by pybind11. In order to avoid copying, you should +explictly specify an appropriate return value policy, as in the following +example: + +.. code-block:: cpp + + class MyClass { + Eigen::MatrixXd big_mat = Eigen::MatrixXd::Zero(10000, 10000); + public: + Eigen::MatrixXd &getMatrix() { return big_mat; } + const Eigen::MatrixXd &viewMatrix() { return big_mat; } + }; + + // Later, in binding code: + py::class_(m, "MyClass") + .def(py::init<>()) + .def("copy_matrix", &MyClass::getMatrix) // Makes a copy! + .def("get_matrix", &MyClass::getMatrix, py::return_value_policy::reference_internal) + .def("view_matrix", &MyClass::viewMatrix, py::return_value_policy::reference_internal) + ; + +.. code-block:: python + + a = MyClass() + m = a.get_matrix() # flags.writeable = True, flags.owndata = False + v = a.view_matrix() # flags.writeable = False, flags.owndata = False + c = a.copy_matrix() # flags.writeable = True, flags.owndata = True + # m[5,6] and v[5,6] refer to the same element, c[5,6] does not. + +Note in this example that ``py::return_value_policy::reference_internal`` is +used to tie the life of the MyClass object to the life of the returned arrays. + +You may also return an ``Eigen::Ref``, ``Eigen::Map`` or other map-like Eigen +object (for example, the return value of ``matrix.block()`` and related +methods) that map into a dense Eigen type. When doing so, the default +behaviour of pybind11 is to simply reference the returned data: you must take +care to ensure that this data remains valid! You may ask pybind11 to +explicitly *copy* such a return value by using the +``py::return_value_policy::copy`` policy when binding the function. You may +also use ``py::return_value_policy::reference_internal`` or a +``py::keep_alive`` to ensure the data stays valid as long as the returned numpy +array does. + +When returning such a reference of map, pybind11 additionally respects the +readonly-status of the returned value, marking the numpy array as non-writeable +if the reference or map was itself read-only. + +.. note:: + + Sparse types are always copied when returned. + +.. _storage_orders: + +Storage orders +============== + +Passing arguments via ``Eigen::Ref`` has some limitations that you must be +aware of in order to effectively pass matrices by reference. First and +foremost is that the default ``Eigen::Ref`` class requires +contiguous storage along columns (for column-major types, the default in Eigen) +or rows if ``MatrixType`` is specifically an ``Eigen::RowMajor`` storage type. +The former, Eigen's default, is incompatible with ``numpy``'s default row-major +storage, and so you will not be able to pass numpy arrays to Eigen by reference +without making one of two changes. + +(Note that this does not apply to vectors (or column or row matrices): for such +types the "row-major" and "column-major" distinction is meaningless). + +The first approach is to change the use of ``Eigen::Ref`` to the +more general ``Eigen::Ref>`` (or similar type with a fully dynamic stride type in the +third template argument). Since this is a rather cumbersome type, pybind11 +provides a ``py::EigenDRef`` type alias for your convenience (along +with EigenDMap for the equivalent Map, and EigenDStride for just the stride +type). + +This type allows Eigen to map into any arbitrary storage order. This is not +the default in Eigen for performance reasons: contiguous storage allows +vectorization that cannot be done when storage is not known to be contiguous at +compile time. The default ``Eigen::Ref`` stride type allows non-contiguous +storage along the outer dimension (that is, the rows of a column-major matrix +or columns of a row-major matrix), but not along the inner dimension. + +This type, however, has the added benefit of also being able to map numpy array +slices. For example, the following (contrived) example uses Eigen with a numpy +slice to multiply by 2 all coefficients that are both on even rows (0, 2, 4, +...) and in columns 2, 5, or 8: + +.. code-block:: cpp + + m.def("scale", [](py::EigenDRef m, double c) { m *= c; }); + +.. code-block:: python + + # a = np.array(...) + scale_by_2(myarray[0::2, 2:9:3]) + +The second approach to avoid copying is more intrusive: rearranging the +underlying data types to not run into the non-contiguous storage problem in the +first place. In particular, that means using matrices with ``Eigen::RowMajor`` +storage, where appropriate, such as: + +.. code-block:: cpp + + using RowMatrixXd = Eigen::Matrix; + // Use RowMatrixXd instead of MatrixXd + +Now bound functions accepting ``Eigen::Ref`` arguments will be +callable with numpy's (default) arrays without involving a copying. + +You can, alternatively, change the storage order that numpy arrays use by +adding the ``order='F'`` option when creating an array: + +.. code-block:: python + + myarray = np.array(source, order='F') + +Such an object will be passable to a bound function accepting an +``Eigen::Ref`` (or similar column-major Eigen type). + +One major caveat with this approach, however, is that it is not entirely as +easy as simply flipping all Eigen or numpy usage from one to the other: some +operations may alter the storage order of a numpy array. For example, ``a2 = +array.transpose()`` results in ``a2`` being a view of ``array`` that references +the same data, but in the opposite storage order! + +While this approach allows fully optimized vectorized calculations in Eigen, it +cannot be used with array slices, unlike the first approach. + +When *returning* a matrix to Python (either a regular matrix, a reference via +``Eigen::Ref<>``, or a map/block into a matrix), no special storage +consideration is required: the created numpy array will have the required +stride that allows numpy to properly interpret the array, whatever its storage +order. + +Failing rather than copying +=========================== + +The default behaviour when binding ``Eigen::Ref`` eigen +references is to copy matrix values when passed a numpy array that does not +conform to the element type of ``MatrixType`` or does not have a compatible +stride layout. If you want to explicitly avoid copying in such a case, you +should bind arguments using the ``py::arg().noconvert()`` annotation (as +described in the :ref:`nonconverting_arguments` documentation). + +The following example shows an example of arguments that don't allow data +copying to take place: + +.. code-block:: cpp + + // The method and function to be bound: + class MyClass { + // ... + double some_method(const Eigen::Ref &matrix) { /* ... */ } + }; + float some_function(const Eigen::Ref &big, + const Eigen::Ref &small) { + // ... + } + + // The associated binding code: + using namespace pybind11::literals; // for "arg"_a + py::class_(m, "MyClass") + // ... other class definitions + .def("some_method", &MyClass::some_method, py::arg().noconvert()); + + m.def("some_function", &some_function, + "big"_a.noconvert(), // <- Don't allow copying for this arg + "small"_a // <- This one can be copied if needed + ); + +With the above binding code, attempting to call the the ``some_method(m)`` +method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)`` +will raise a ``RuntimeError`` rather than making a temporary copy of the array. +It will, however, allow the ``m2`` argument to be copied into a temporary if +necessary. + +Note that explicitly specifying ``.noconvert()`` is not required for *mutable* +Eigen references (e.g. ``Eigen::Ref`` without ``const`` on the +``MatrixXd``): mutable references will never be called with a temporary copy. + +Vectors versus column/row matrices +================================== + +Eigen and numpy have fundamentally different notions of a vector. In Eigen, a +vector is simply a matrix with the number of columns or rows set to 1 at +compile time (for a column vector or row vector, respectively). Numpy, in +contast, has comparable 2-dimensional 1xN and Nx1 arrays, but *also* has +1-dimensional arrays of size N. + +When passing a 2-dimensional 1xN or Nx1 array to Eigen, the Eigen type must +have matching dimensions: That is, you cannot pass a 2-dimensional Nx1 numpy +array to an Eigen value expecting a row vector, or a 1xN numpy array as a +column vector argument. + +On the other hand, pybind11 allows you to pass 1-dimensional arrays of length N +as Eigen parameters. If the Eigen type can hold a column vector of length N it +will be passed as such a column vector. If not, but the Eigen type constraints +will accept a row vector, it will be passed as a row vector. (The column +vector takes precendence when both are supported, for example, when passing a +1D numpy array to a MatrixXd argument). Note that the type need not be +expicitly a vector: it is permitted to pass a 1D numpy array of size 5 to an +Eigen ``Matrix``: you would end up with a 1x5 Eigen matrix. +Passing the same to an ``Eigen::MatrixXd`` would result in a 5x1 Eigen matrix. + +When returning an eigen vector to numpy, the conversion is ambiguous: a row +vector of length 4 could be returned as either a 1D array of length 4, or as a +2D array of size 1x4. When encoutering such a situation, pybind11 compromises +by considering the returned Eigen type: if it is a compile-time vector--that +is, the type has either the number of rows or columns set to 1 at compile +time--pybind11 converts to a 1D numpy array when returning the value. For +instances that are a vector only at run-time (e.g. ``MatrixXd``, +``Matrix``), pybind11 returns the vector as a 2D array to +numpy. If this isn't want you want, you can use ``array.reshape(...)`` to get +a view of the same data in the desired dimensions. + +.. seealso:: + + The file :file:`tests/test_eigen.cpp` contains a complete example that + shows how to pass Eigen sparse and dense data types in more detail. diff --git a/external/pybind11/docs/advanced/cast/functional.rst b/external/pybind11/docs/advanced/cast/functional.rst new file mode 100644 index 0000000..d9b4605 --- /dev/null +++ b/external/pybind11/docs/advanced/cast/functional.rst @@ -0,0 +1,109 @@ +Functional +########## + +The following features must be enabled by including :file:`pybind11/functional.h`. + + +Callbacks and passing anonymous functions +========================================= + +The C++11 standard brought lambda functions and the generic polymorphic +function wrapper ``std::function<>`` to the C++ programming language, which +enable powerful new ways of working with functions. Lambda functions come in +two flavors: stateless lambda function resemble classic function pointers that +link to an anonymous piece of code, while stateful lambda functions +additionally depend on captured variables that are stored in an anonymous +*lambda closure object*. + +Here is a simple example of a C++ function that takes an arbitrary function +(stateful or stateless) with signature ``int -> int`` as an argument and runs +it with the value 10. + +.. code-block:: cpp + + int func_arg(const std::function &f) { + return f(10); + } + +The example below is more involved: it takes a function of signature ``int -> int`` +and returns another function of the same kind. The return value is a stateful +lambda function, which stores the value ``f`` in the capture object and adds 1 to +its return value upon execution. + +.. code-block:: cpp + + std::function func_ret(const std::function &f) { + return [f](int i) { + return f(i) + 1; + }; + } + +This example demonstrates using python named parameters in C++ callbacks which +requires using ``py::cpp_function`` as a wrapper. Usage is similar to defining +methods of classes: + +.. code-block:: cpp + + py::cpp_function func_cpp() { + return py::cpp_function([](int i) { return i+1; }, + py::arg("number")); + } + +After including the extra header file :file:`pybind11/functional.h`, it is almost +trivial to generate binding code for all of these functions. + +.. code-block:: cpp + + #include + + PYBIND11_MODULE(example, m) { + m.def("func_arg", &func_arg); + m.def("func_ret", &func_ret); + m.def("func_cpp", &func_cpp); + } + +The following interactive session shows how to call them from Python. + +.. code-block:: pycon + + $ python + >>> import example + >>> def square(i): + ... return i * i + ... + >>> example.func_arg(square) + 100L + >>> square_plus_1 = example.func_ret(square) + >>> square_plus_1(4) + 17L + >>> plus_1 = func_cpp() + >>> plus_1(number=43) + 44L + +.. warning:: + + Keep in mind that passing a function from C++ to Python (or vice versa) + will instantiate a piece of wrapper code that translates function + invocations between the two languages. Naturally, this translation + increases the computational cost of each function call somewhat. A + problematic situation can arise when a function is copied back and forth + between Python and C++ many times in a row, in which case the underlying + wrappers will accumulate correspondingly. The resulting long sequence of + C++ -> Python -> C++ -> ... roundtrips can significantly decrease + performance. + + There is one exception: pybind11 detects case where a stateless function + (i.e. a function pointer or a lambda function without captured variables) + is passed as an argument to another C++ function exposed in Python. In this + case, there is no overhead. Pybind11 will extract the underlying C++ + function pointer from the wrapped function to sidestep a potential C++ -> + Python -> C++ roundtrip. This is demonstrated in :file:`tests/test_callbacks.cpp`. + +.. note:: + + This functionality is very useful when generating bindings for callbacks in + C++ libraries (e.g. GUI libraries, asynchronous networking libraries, etc.). + + The file :file:`tests/test_callbacks.cpp` contains a complete example + that demonstrates how to work with callbacks and anonymous functions in + more detail. diff --git a/external/pybind11/docs/advanced/cast/index.rst b/external/pybind11/docs/advanced/cast/index.rst new file mode 100644 index 0000000..54c1057 --- /dev/null +++ b/external/pybind11/docs/advanced/cast/index.rst @@ -0,0 +1,42 @@ +Type conversions +################ + +Apart from enabling cross-language function calls, a fundamental problem +that a binding tool like pybind11 must address is to provide access to +native Python types in C++ and vice versa. There are three fundamentally +different ways to do this—which approach is preferable for a particular type +depends on the situation at hand. + +1. Use a native C++ type everywhere. In this case, the type must be wrapped + using pybind11-generated bindings so that Python can interact with it. + +2. Use a native Python type everywhere. It will need to be wrapped so that + C++ functions can interact with it. + +3. Use a native C++ type on the C++ side and a native Python type on the + Python side. pybind11 refers to this as a *type conversion*. + + Type conversions are the most "natural" option in the sense that native + (non-wrapped) types are used everywhere. The main downside is that a copy + of the data must be made on every Python ↔ C++ transition: this is + needed since the C++ and Python versions of the same type generally won't + have the same memory layout. + + pybind11 can perform many kinds of conversions automatically. An overview + is provided in the table ":ref:`conversion_table`". + +The following subsections discuss the differences between these options in more +detail. The main focus in this section is on type conversions, which represent +the last case of the above list. + +.. toctree:: + :maxdepth: 1 + + overview + strings + stl + functional + chrono + eigen + custom + diff --git a/external/pybind11/docs/advanced/cast/overview.rst b/external/pybind11/docs/advanced/cast/overview.rst new file mode 100644 index 0000000..2ac7d30 --- /dev/null +++ b/external/pybind11/docs/advanced/cast/overview.rst @@ -0,0 +1,163 @@ +Overview +######## + +.. rubric:: 1. Native type in C++, wrapper in Python + +Exposing a custom C++ type using :class:`py::class_` was covered in detail +in the :doc:`/classes` section. There, the underlying data structure is +always the original C++ class while the :class:`py::class_` wrapper provides +a Python interface. Internally, when an object like this is sent from C++ to +Python, pybind11 will just add the outer wrapper layer over the native C++ +object. Getting it back from Python is just a matter of peeling off the +wrapper. + +.. rubric:: 2. Wrapper in C++, native type in Python + +This is the exact opposite situation. Now, we have a type which is native to +Python, like a ``tuple`` or a ``list``. One way to get this data into C++ is +with the :class:`py::object` family of wrappers. These are explained in more +detail in the :doc:`/advanced/pycpp/object` section. We'll just give a quick +example here: + +.. code-block:: cpp + + void print_list(py::list my_list) { + for (auto item : my_list) + std::cout << item << " "; + } + +.. code-block:: pycon + + >>> print_list([1, 2, 3]) + 1 2 3 + +The Python ``list`` is not converted in any way -- it's just wrapped in a C++ +:class:`py::list` class. At its core it's still a Python object. Copying a +:class:`py::list` will do the usual reference-counting like in Python. +Returning the object to Python will just remove the thin wrapper. + +.. rubric:: 3. Converting between native C++ and Python types + +In the previous two cases we had a native type in one language and a wrapper in +the other. Now, we have native types on both sides and we convert between them. + +.. code-block:: cpp + + void print_vector(const std::vector &v) { + for (auto item : v) + std::cout << item << "\n"; + } + +.. code-block:: pycon + + >>> print_vector([1, 2, 3]) + 1 2 3 + +In this case, pybind11 will construct a new ``std::vector`` and copy each +element from the Python ``list``. The newly constructed object will be passed +to ``print_vector``. The same thing happens in the other direction: a new +``list`` is made to match the value returned from C++. + +Lots of these conversions are supported out of the box, as shown in the table +below. They are very convenient, but keep in mind that these conversions are +fundamentally based on copying data. This is perfectly fine for small immutable +types but it may become quite expensive for large data structures. This can be +avoided by overriding the automatic conversion with a custom wrapper (i.e. the +above-mentioned approach 1). This requires some manual effort and more details +are available in the :ref:`opaque` section. + +.. _conversion_table: + +List of all builtin conversions +------------------------------- + +The following basic data types are supported out of the box (some may require +an additional extension header to be included). To pass other data structures +as arguments and return values, refer to the section on binding :ref:`classes`. + ++------------------------------------+---------------------------+-------------------------------+ +| Data type | Description | Header file | ++====================================+===========================+===============================+ +| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``char`` | Character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` | +| ``std::u16string_view``, etc. | | | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::pair`` | Pair of two custom types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::complex`` | Complex numbers | :file:`pybind11/complex.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::array`` | STL static array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::vector`` | STL dynamic array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::valarray`` | STL value array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::list`` | STL linked list | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::map`` | STL ordered map | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::unordered_map`` | STL unordered map | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::set`` | STL ordered set | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::unordered_set`` | STL unordered set | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::optional`` | STL optional type (C++17) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::experimental::optional`` | STL optional type (exp.) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-------------------------------+ +| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-------------------------------+ diff --git a/external/pybind11/docs/advanced/cast/stl.rst b/external/pybind11/docs/advanced/cast/stl.rst new file mode 100644 index 0000000..3f30c02 --- /dev/null +++ b/external/pybind11/docs/advanced/cast/stl.rst @@ -0,0 +1,243 @@ +STL containers +############## + +Automatic conversion +==================== + +When including the additional header file :file:`pybind11/stl.h`, conversions +between ``std::vector<>``/``std::list<>``/``std::array<>``, +``std::set<>``/``std::unordered_set<>``, and +``std::map<>``/``std::unordered_map<>`` and the Python ``list``, ``set`` and +``dict`` data structures are automatically enabled. The types ``std::pair<>`` +and ``std::tuple<>`` are already supported out of the box with just the core +:file:`pybind11/pybind11.h` header. + +The major downside of these implicit conversions is that containers must be +converted (i.e. copied) on every Python->C++ and C++->Python transition, which +can have implications on the program semantics and performance. Please read the +next sections for more details and alternative approaches that avoid this. + +.. note:: + + Arbitrary nesting of any of these types is possible. + +.. seealso:: + + The file :file:`tests/test_stl.cpp` contains a complete + example that demonstrates how to pass STL data types in more detail. + +.. _cpp17_container_casters: + +C++17 library containers +======================== + +The :file:`pybind11/stl.h` header also includes support for ``std::optional<>`` +and ``std::variant<>``. These require a C++17 compiler and standard library. +In C++14 mode, ``std::experimental::optional<>`` is supported if available. + +Various versions of these containers also exist for C++11 (e.g. in Boost). +pybind11 provides an easy way to specialize the ``type_caster`` for such +types: + +.. code-block:: cpp + + // `boost::optional` as an example -- can be any `std::optional`-like container + namespace pybind11 { namespace detail { + template + struct type_caster> : optional_caster> {}; + }} + +The above should be placed in a header file and included in all translation units +where automatic conversion is needed. Similarly, a specialization can be provided +for custom variant types: + +.. code-block:: cpp + + // `boost::variant` as an example -- can be any `std::variant`-like container + namespace pybind11 { namespace detail { + template + struct type_caster> : variant_caster> {}; + + // Specifies the function used to visit the variant -- `apply_visitor` instead of `visit` + template <> + struct visit_helper { + template + static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) { + return boost::apply_visitor(args...); + } + }; + }} // namespace pybind11::detail + +The ``visit_helper`` specialization is not required if your ``name::variant`` provides +a ``name::visit()`` function. For any other function name, the specialization must be +included to tell pybind11 how to visit the variant. + +.. note:: + + pybind11 only supports the modern implementation of ``boost::variant`` + which makes use of variadic templates. This requires Boost 1.56 or newer. + Additionally, on Windows, MSVC 2017 is required because ``boost::variant`` + falls back to the old non-variadic implementation on MSVC 2015. + +.. _opaque: + +Making opaque types +=================== + +pybind11 heavily relies on a template matching mechanism to convert parameters +and return values that are constructed from STL data types such as vectors, +linked lists, hash tables, etc. This even works in a recursive manner, for +instance to deal with lists of hash maps of pairs of elementary and custom +types, etc. + +However, a fundamental limitation of this approach is that internal conversions +between Python and C++ types involve a copy operation that prevents +pass-by-reference semantics. What does this mean? + +Suppose we bind the following function + +.. code-block:: cpp + + void append_1(std::vector &v) { + v.push_back(1); + } + +and call it from Python, the following happens: + +.. code-block:: pycon + + >>> v = [5, 6] + >>> append_1(v) + >>> print(v) + [5, 6] + +As you can see, when passing STL data structures by reference, modifications +are not propagated back the Python side. A similar situation arises when +exposing STL data structures using the ``def_readwrite`` or ``def_readonly`` +functions: + +.. code-block:: cpp + + /* ... definition ... */ + + class MyClass { + std::vector contents; + }; + + /* ... binding code ... */ + + py::class_(m, "MyClass") + .def(py::init<>()) + .def_readwrite("contents", &MyClass::contents); + +In this case, properties can be read and written in their entirety. However, an +``append`` operation involving such a list type has no effect: + +.. code-block:: pycon + + >>> m = MyClass() + >>> m.contents = [5, 6] + >>> print(m.contents) + [5, 6] + >>> m.contents.append(7) + >>> print(m.contents) + [5, 6] + +Finally, the involved copy operations can be costly when dealing with very +large lists. To deal with all of the above situations, pybind11 provides a +macro named ``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based +conversion machinery of types, thus rendering them *opaque*. The contents of +opaque objects are never inspected or extracted, hence they *can* be passed by +reference. For instance, to turn ``std::vector`` into an opaque type, add +the declaration + +.. code-block:: cpp + + PYBIND11_MAKE_OPAQUE(std::vector); + +before any binding code (e.g. invocations to ``class_::def()``, etc.). This +macro must be specified at the top level (and outside of any namespaces), since +it instantiates a partial template overload. If your binding code consists of +multiple compilation units, it must be present in every file (typically via a +common header) preceding any usage of ``std::vector``. Opaque types must +also have a corresponding ``class_`` declaration to associate them with a name +in Python, and to define a set of available operations, e.g.: + +.. code-block:: cpp + + py::class_>(m, "IntVector") + .def(py::init<>()) + .def("clear", &std::vector::clear) + .def("pop_back", &std::vector::pop_back) + .def("__len__", [](const std::vector &v) { return v.size(); }) + .def("__iter__", [](std::vector &v) { + return py::make_iterator(v.begin(), v.end()); + }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ + // .... + +Please take a look at the :ref:`macro_notes` before using the +``PYBIND11_MAKE_OPAQUE`` macro. + +.. seealso:: + + The file :file:`tests/test_opaque_types.cpp` contains a complete + example that demonstrates how to create and expose opaque types using + pybind11 in more detail. + +.. _stl_bind: + +Binding STL containers +====================== + +The ability to expose STL containers as native Python objects is a fairly +common request, hence pybind11 also provides an optional header file named +:file:`pybind11/stl_bind.h` that does exactly this. The mapped containers try +to match the behavior of their native Python counterparts as much as possible. + +The following example showcases usage of :file:`pybind11/stl_bind.h`: + +.. code-block:: cpp + + // Don't forget this + #include + + PYBIND11_MAKE_OPAQUE(std::vector); + PYBIND11_MAKE_OPAQUE(std::map); + + // ... + + // later in binding code: + py::bind_vector>(m, "VectorInt"); + py::bind_map>(m, "MapStringDouble"); + +When binding STL containers pybind11 considers the types of the container's +elements to decide whether the container should be confined to the local module +(via the :ref:`module_local` feature). If the container element types are +anything other than already-bound custom types bound without +``py::module_local()`` the container binding will have ``py::module_local()`` +applied. This includes converting types such as numeric types, strings, Eigen +types; and types that have not yet been bound at the time of the stl container +binding. This module-local binding is designed to avoid potential conflicts +between module bindings (for example, from two separate modules each attempting +to bind ``std::vector`` as a python type). + +It is possible to override this behavior to force a definition to be either +module-local or global. To do so, you can pass the attributes +``py::module_local()`` (to make the binding module-local) or +``py::module_local(false)`` (to make the binding global) into the +``py::bind_vector`` or ``py::bind_map`` arguments: + +.. code-block:: cpp + + py::bind_vector>(m, "VectorInt", py::module_local(false)); + +Note, however, that such a global binding would make it impossible to load this +module at the same time as any other pybind module that also attempts to bind +the same container type (``std::vector`` in the above example). + +See :ref:`module_local` for more details on module-local bindings. + +.. seealso:: + + The file :file:`tests/test_stl_binders.cpp` shows how to use the + convenience STL container wrappers. diff --git a/external/pybind11/docs/advanced/cast/strings.rst b/external/pybind11/docs/advanced/cast/strings.rst new file mode 100644 index 0000000..2cdbade --- /dev/null +++ b/external/pybind11/docs/advanced/cast/strings.rst @@ -0,0 +1,303 @@ +Strings, bytes and Unicode conversions +###################################### + +.. note:: + + This section discusses string handling in terms of Python 3 strings. For + Python 2.7, replace all occurrences of ``str`` with ``unicode`` and + ``bytes`` with ``str``. Python 2.7 users may find it best to use ``from + __future__ import unicode_literals`` to avoid unintentionally using ``str`` + instead of ``unicode``. + +Passing Python strings to C++ +============================= + +When a Python ``str`` is passed from Python to a C++ function that accepts +``std::string`` or ``char *`` as arguments, pybind11 will encode the Python +string to UTF-8. All Python ``str`` can be encoded in UTF-8, so this operation +does not fail. + +The C++ language is encoding agnostic. It is the responsibility of the +programmer to track encodings. It's often easiest to simply `use UTF-8 +everywhere `_. + +.. code-block:: c++ + + m.def("utf8_test", + [](const std::string &s) { + cout << "utf-8 is icing on the cake.\n"; + cout << s; + } + ); + m.def("utf8_charptr", + [](const char *s) { + cout << "My favorite food is\n"; + cout << s; + } + ); + +.. code-block:: python + + >>> utf8_test('🎂') + utf-8 is icing on the cake. + 🎂 + + >>> utf8_charptr('🍕') + My favorite food is + 🍕 + +.. note:: + + Some terminal emulators do not support UTF-8 or emoji fonts and may not + display the example above correctly. + +The results are the same whether the C++ function accepts arguments by value or +reference, and whether or not ``const`` is used. + +Passing bytes to C++ +-------------------- + +A Python ``bytes`` object will be passed to C++ functions that accept +``std::string`` or ``char*`` *without* conversion. + + +Returning C++ strings to Python +=============================== + +When a C++ function returns a ``std::string`` or ``char*`` to a Python caller, +**pybind11 will assume that the string is valid UTF-8** and will decode it to a +native Python ``str``, using the same API as Python uses to perform +``bytes.decode('utf-8')``. If this implicit conversion fails, pybind11 will +raise a ``UnicodeDecodeError``. + +.. code-block:: c++ + + m.def("std_string_return", + []() { + return std::string("This string needs to be UTF-8 encoded"); + } + ); + +.. code-block:: python + + >>> isinstance(example.std_string_return(), str) + True + + +Because UTF-8 is inclusive of pure ASCII, there is never any issue with +returning a pure ASCII string to Python. If there is any possibility that the +string is not pure ASCII, it is necessary to ensure the encoding is valid +UTF-8. + +.. warning:: + + Implicit conversion assumes that a returned ``char *`` is null-terminated. + If there is no null terminator a buffer overrun will occur. + +Explicit conversions +-------------------- + +If some C++ code constructs a ``std::string`` that is not a UTF-8 string, one +can perform a explicit conversion and return a ``py::str`` object. Explicit +conversion has the same overhead as implicit conversion. + +.. code-block:: c++ + + // This uses the Python C API to convert Latin-1 to Unicode + m.def("str_output", + []() { + std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1 + py::str py_s = PyUnicode_DecodeLatin1(s.data(), s.length()); + return py_s; + } + ); + +.. code-block:: python + + >>> str_output() + 'Send your résumé to Alice in HR' + +The `Python C API +`_ provides +several built-in codecs. + + +One could also use a third party encoding library such as libiconv to transcode +to UTF-8. + +Return C++ strings without conversion +------------------------------------- + +If the data in a C++ ``std::string`` does not represent text and should be +returned to Python as ``bytes``, then one can return the data as a +``py::bytes`` object. + +.. code-block:: c++ + + m.def("return_bytes", + []() { + std::string s("\xba\xd0\xba\xd0"); // Not valid UTF-8 + return py::bytes(s); // Return the data without transcoding + } + ); + +.. code-block:: python + + >>> example.return_bytes() + b'\xba\xd0\xba\xd0' + + +Note the asymmetry: pybind11 will convert ``bytes`` to ``std::string`` without +encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly. + +.. code-block:: c++ + + m.def("asymmetry", + [](std::string s) { // Accepts str or bytes from Python + return s; // Looks harmless, but implicitly converts to str + } + ); + +.. code-block:: python + + >>> isinstance(example.asymmetry(b"have some bytes"), str) + True + + >>> example.asymmetry(b"\xba\xd0\xba\xd0") # invalid utf-8 as bytes + UnicodeDecodeError: 'utf-8' codec can't decode byte 0xba in position 0: invalid start byte + + +Wide character strings +====================== + +When a Python ``str`` is passed to a C++ function expecting ``std::wstring``, +``wchar_t*``, ``std::u16string`` or ``std::u32string``, the ``str`` will be +encoded to UTF-16 or UTF-32 depending on how the C++ compiler implements each +type, in the platform's native endianness. When strings of these types are +returned, they are assumed to contain valid UTF-16 or UTF-32, and will be +decoded to Python ``str``. + +.. code-block:: c++ + + #define UNICODE + #include + + m.def("set_window_text", + [](HWND hwnd, std::wstring s) { + // Call SetWindowText with null-terminated UTF-16 string + ::SetWindowText(hwnd, s.c_str()); + } + ); + m.def("get_window_text", + [](HWND hwnd) { + const int buffer_size = ::GetWindowTextLength(hwnd) + 1; + auto buffer = std::make_unique< wchar_t[] >(buffer_size); + + ::GetWindowText(hwnd, buffer.data(), buffer_size); + + std::wstring text(buffer.get()); + + // wstring will be converted to Python str + return text; + } + ); + +.. warning:: + + Wide character strings may not work as described on Python 2.7 or Python + 3.3 compiled with ``--enable-unicode=ucs2``. + +Strings in multibyte encodings such as Shift-JIS must transcoded to a +UTF-8/16/32 before being returned to Python. + + +Character literals +================== + +C++ functions that accept character literals as input will receive the first +character of a Python ``str`` as their input. If the string is longer than one +Unicode character, trailing characters will be ignored. + +When a character literal is returned from C++ (such as a ``char`` or a +``wchar_t``), it will be converted to a ``str`` that represents the single +character. + +.. code-block:: c++ + + m.def("pass_char", [](char c) { return c; }); + m.def("pass_wchar", [](wchar_t w) { return w; }); + +.. code-block:: python + + >>> example.pass_char('A') + 'A' + +While C++ will cast integers to character types (``char c = 0x65;``), pybind11 +does not convert Python integers to characters implicitly. The Python function +``chr()`` can be used to convert integers to characters. + +.. code-block:: python + + >>> example.pass_char(0x65) + TypeError + + >>> example.pass_char(chr(0x65)) + 'A' + +If the desire is to work with an 8-bit integer, use ``int8_t`` or ``uint8_t`` +as the argument type. + +Grapheme clusters +----------------- + +A single grapheme may be represented by two or more Unicode characters. For +example 'é' is usually represented as U+00E9 but can also be expressed as the +combining character sequence U+0065 U+0301 (that is, the letter 'e' followed by +a combining acute accent). The combining character will be lost if the +two-character sequence is passed as an argument, even though it renders as a +single grapheme. + +.. code-block:: python + + >>> example.pass_wchar('é') + 'é' + + >>> combining_e_acute = 'e' + '\u0301' + + >>> combining_e_acute + 'é' + + >>> combining_e_acute == 'é' + False + + >>> example.pass_wchar(combining_e_acute) + 'e' + +Normalizing combining characters before passing the character literal to C++ +may resolve *some* of these issues: + +.. code-block:: python + + >>> example.pass_wchar(unicodedata.normalize('NFC', combining_e_acute)) + 'é' + +In some languages (Thai for example), there are `graphemes that cannot be +expressed as a single Unicode code point +`_, so there is +no way to capture them in a C++ character type. + + +C++17 string views +================== + +C++17 string views are automatically supported when compiling in C++17 mode. +They follow the same rules for encoding and decoding as the corresponding STL +string type (for example, a ``std::u16string_view`` argument will be passed +UTF-16-encoded data, and a returned ``std::string_view`` will be decoded as +UTF-8). + +References +========== + +* `The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) `_ +* `C++ - Using STL Strings at Win32 API Boundaries `_ diff --git a/external/pybind11/docs/advanced/classes.rst b/external/pybind11/docs/advanced/classes.rst new file mode 100644 index 0000000..93deeec --- /dev/null +++ b/external/pybind11/docs/advanced/classes.rst @@ -0,0 +1,1001 @@ +Classes +####### + +This section presents advanced binding code for classes and it is assumed +that you are already familiar with the basics from :doc:`/classes`. + +.. _overriding_virtuals: + +Overriding virtual functions in Python +====================================== + +Suppose that a C++ class or interface has a virtual function that we'd like to +to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is +given as a specific example of how one would do this with traditional C++ +code). + +.. code-block:: cpp + + class Animal { + public: + virtual ~Animal() { } + virtual std::string go(int n_times) = 0; + }; + + class Dog : public Animal { + public: + std::string go(int n_times) override { + std::string result; + for (int i=0; igo(3); + } + +Normally, the binding code for these classes would look as follows: + +.. code-block:: cpp + + PYBIND11_MODULE(example, m) { + py::class_ animal(m, "Animal"); + animal + .def("go", &Animal::go); + + py::class_(m, "Dog", animal) + .def(py::init<>()); + + m.def("call_go", &call_go); + } + +However, these bindings are impossible to extend: ``Animal`` is not +constructible, and we clearly require some kind of "trampoline" that +redirects virtual calls back to Python. + +Defining a new type of ``Animal`` from within Python is possible but requires a +helper class that is defined as follows: + +.. code-block:: cpp + + class PyAnimal : public Animal { + public: + /* Inherit the constructors */ + using Animal::Animal; + + /* Trampoline (need one for each virtual function) */ + std::string go(int n_times) override { + PYBIND11_OVERLOAD_PURE( + std::string, /* Return type */ + Animal, /* Parent class */ + go, /* Name of function in C++ (must match Python name) */ + n_times /* Argument(s) */ + ); + } + }; + +The macro :func:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual +functions, and :func:`PYBIND11_OVERLOAD` should be used for functions which have +a default implementation. There are also two alternate macros +:func:`PYBIND11_OVERLOAD_PURE_NAME` and :func:`PYBIND11_OVERLOAD_NAME` which +take a string-valued name argument between the *Parent class* and *Name of the +function* slots, which defines the name of function in Python. This is required +when the C++ and Python versions of the +function have different names, e.g. ``operator()`` vs ``__call__``. + +The binding code also needs a few minor adaptations (highlighted): + +.. code-block:: cpp + :emphasize-lines: 2,4,5 + + PYBIND11_MODULE(example, m) { + py::class_ animal(m, "Animal"); + animal + .def(py::init<>()) + .def("go", &Animal::go); + + py::class_(m, "Dog", animal) + .def(py::init<>()); + + m.def("call_go", &call_go); + } + +Importantly, pybind11 is made aware of the trampoline helper class by +specifying it as an extra template argument to :class:`class_`. (This can also +be combined with other template arguments such as a custom holder type; the +order of template types does not matter). Following this, we are able to +define a constructor as usual. + +Bindings should be made against the actual class, not the trampoline helper class. + +.. code-block:: cpp + + py::class_ animal(m, "Animal"); + animal + .def(py::init<>()) + .def("go", &PyAnimal::go); /* <--- THIS IS WRONG, use &Animal::go */ + +Note, however, that the above is sufficient for allowing python classes to +extend ``Animal``, but not ``Dog``: see :ref:`virtual_and_inheritance` for the +necessary steps required to providing proper overload support for inherited +classes. + +The Python session below shows how to override ``Animal::go`` and invoke it via +a virtual method call. + +.. code-block:: pycon + + >>> from example import * + >>> d = Dog() + >>> call_go(d) + u'woof! woof! woof! ' + >>> class Cat(Animal): + ... def go(self, n_times): + ... return "meow! " * n_times + ... + >>> c = Cat() + >>> call_go(c) + u'meow! meow! meow! ' + +If you are defining a custom constructor in a derived Python class, you *must* +ensure that you explicitly call the bound C++ constructor using ``__init__``, +*regardless* of whether it is a default constructor or not. Otherwise, the +memory for the C++ portion of the instance will be left uninitialized, which +will generally leave the C++ instance in an invalid state and cause undefined +behavior if the C++ instance is subsequently used. + +Here is an example: + +.. code-block:: python + + class Dachschund(Dog): + def __init__(self, name): + Dog.__init__(self) # Without this, undefind behavior may occur if the C++ portions are referenced. + self.name = name + def bark(self): + return "yap!" + +Note that a direct ``__init__`` constructor *should be called*, and ``super()`` +should not be used. For simple cases of linear inheritance, ``super()`` +may work, but once you begin mixing Python and C++ multiple inheritance, +things will fall apart due to differences between Python's MRO and C++'s +mechanisms. + +Please take a look at the :ref:`macro_notes` before using this feature. + +.. note:: + + When the overridden type returns a reference or pointer to a type that + pybind11 converts from Python (for example, numeric values, std::string, + and other built-in value-converting types), there are some limitations to + be aware of: + + - because in these cases there is no C++ variable to reference (the value + is stored in the referenced Python variable), pybind11 provides one in + the PYBIND11_OVERLOAD macros (when needed) with static storage duration. + Note that this means that invoking the overloaded method on *any* + instance will change the referenced value stored in *all* instances of + that type. + + - Attempts to modify a non-const reference will not have the desired + effect: it will change only the static cache variable, but this change + will not propagate to underlying Python instance, and the change will be + replaced the next time the overload is invoked. + +.. seealso:: + + The file :file:`tests/test_virtual_functions.cpp` contains a complete + example that demonstrates how to override virtual functions using pybind11 + in more detail. + +.. _virtual_and_inheritance: + +Combining virtual functions and inheritance +=========================================== + +When combining virtual methods with inheritance, you need to be sure to provide +an override for each method for which you want to allow overrides from derived +python classes. For example, suppose we extend the above ``Animal``/``Dog`` +example as follows: + +.. code-block:: cpp + + class Animal { + public: + virtual std::string go(int n_times) = 0; + virtual std::string name() { return "unknown"; } + }; + class Dog : public Animal { + public: + std::string go(int n_times) override { + std::string result; + for (int i=0; i class PyAnimal : public AnimalBase { + public: + using AnimalBase::AnimalBase; // Inherit constructors + std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, AnimalBase, go, n_times); } + std::string name() override { PYBIND11_OVERLOAD(std::string, AnimalBase, name, ); } + }; + template class PyDog : public PyAnimal { + public: + using PyAnimal::PyAnimal; // Inherit constructors + // Override PyAnimal's pure virtual go() with a non-pure one: + std::string go(int n_times) override { PYBIND11_OVERLOAD(std::string, DogBase, go, n_times); } + std::string bark() override { PYBIND11_OVERLOAD(std::string, DogBase, bark, ); } + }; + +This technique has the advantage of requiring just one trampoline method to be +declared per virtual method and pure virtual method override. It does, +however, require the compiler to generate at least as many methods (and +possibly more, if both pure virtual and overridden pure virtual methods are +exposed, as above). + +The classes are then registered with pybind11 using: + +.. code-block:: cpp + + py::class_> animal(m, "Animal"); + py::class_> dog(m, "Dog"); + py::class_> husky(m, "Husky"); + // ... add animal, dog, husky definitions + +Note that ``Husky`` did not require a dedicated trampoline template class at +all, since it neither declares any new virtual methods nor provides any pure +virtual method implementations. + +With either the repeated-virtuals or templated trampoline methods in place, you +can now create a python class that inherits from ``Dog``: + +.. code-block:: python + + class ShihTzu(Dog): + def bark(self): + return "yip!" + +.. seealso:: + + See the file :file:`tests/test_virtual_functions.cpp` for complete examples + using both the duplication and templated trampoline approaches. + +.. _extended_aliases: + +Extended trampoline class functionality +======================================= + +The trampoline classes described in the previous sections are, by default, only +initialized when needed. More specifically, they are initialized when a python +class actually inherits from a registered type (instead of merely creating an +instance of the registered type), or when a registered constructor is only +valid for the trampoline class but not the registered class. This is primarily +for performance reasons: when the trampoline class is not needed for anything +except virtual method dispatching, not initializing the trampoline class +improves performance by avoiding needing to do a run-time check to see if the +inheriting python instance has an overloaded method. + +Sometimes, however, it is useful to always initialize a trampoline class as an +intermediate class that does more than just handle virtual method dispatching. +For example, such a class might perform extra class initialization, extra +destruction operations, and might define new members and methods to enable a +more python-like interface to a class. + +In order to tell pybind11 that it should *always* initialize the trampoline +class when creating new instances of a type, the class constructors should be +declared using ``py::init_alias()`` instead of the usual +``py::init()``. This forces construction via the trampoline class, +ensuring member initialization and (eventual) destruction. + +.. seealso:: + + See the file :file:`tests/test_virtual_functions.cpp` for complete examples + showing both normal and forced trampoline instantiation. + +.. _custom_constructors: + +Custom constructors +=================== + +The syntax for binding constructors was previously introduced, but it only +works when a constructor of the appropriate arguments actually exists on the +C++ side. To extend this to more general cases, pybind11 makes it possible +to bind factory functions as constructors. For example, suppose you have a +class like this: + +.. code-block:: cpp + + class Example { + private: + Example(int); // private constructor + public: + // Factory function: + static Example create(int a) { return Example(a); } + }; + + py::class_(m, "Example") + .def(py::init(&Example::create)); + +While it is possible to create a straightforward binding of the static +``create`` method, it may sometimes be preferable to expose it as a constructor +on the Python side. This can be accomplished by calling ``.def(py::init(...))`` +with the function reference returning the new instance passed as an argument. +It is also possible to use this approach to bind a function returning a new +instance by raw pointer or by the holder (e.g. ``std::unique_ptr``). + +The following example shows the different approaches: + +.. code-block:: cpp + + class Example { + private: + Example(int); // private constructor + public: + // Factory function - returned by value: + static Example create(int a) { return Example(a); } + + // These constructors are publicly callable: + Example(double); + Example(int, int); + Example(std::string); + }; + + py::class_(m, "Example") + // Bind the factory function as a constructor: + .def(py::init(&Example::create)) + // Bind a lambda function returning a pointer wrapped in a holder: + .def(py::init([](std::string arg) { + return std::unique_ptr(new Example(arg)); + })) + // Return a raw pointer: + .def(py::init([](int a, int b) { return new Example(a, b); })) + // You can mix the above with regular C++ constructor bindings as well: + .def(py::init()) + ; + +When the constructor is invoked from Python, pybind11 will call the factory +function and store the resulting C++ instance in the Python instance. + +When combining factory functions constructors with :ref:`virtual function +trampolines ` there are two approaches. The first is to +add a constructor to the alias class that takes a base value by +rvalue-reference. If such a constructor is available, it will be used to +construct an alias instance from the value returned by the factory function. +The second option is to provide two factory functions to ``py::init()``: the +first will be invoked when no alias class is required (i.e. when the class is +being used but not inherited from in Python), and the second will be invoked +when an alias is required. + +You can also specify a single factory function that always returns an alias +instance: this will result in behaviour similar to ``py::init_alias<...>()``, +as described in the :ref:`extended trampoline class documentation +`. + +The following example shows the different factory approaches for a class with +an alias: + +.. code-block:: cpp + + #include + class Example { + public: + // ... + virtual ~Example() = default; + }; + class PyExample : public Example { + public: + using Example::Example; + PyExample(Example &&base) : Example(std::move(base)) {} + }; + py::class_(m, "Example") + // Returns an Example pointer. If a PyExample is needed, the Example + // instance will be moved via the extra constructor in PyExample, above. + .def(py::init([]() { return new Example(); })) + // Two callbacks: + .def(py::init([]() { return new Example(); } /* no alias needed */, + []() { return new PyExample(); } /* alias needed */)) + // *Always* returns an alias instance (like py::init_alias<>()) + .def(py::init([]() { return new PyExample(); })) + ; + +Brace initialization +-------------------- + +``pybind11::init<>`` internally uses C++11 brace initialization to call the +constructor of the target class. This means that it can be used to bind +*implicit* constructors as well: + +.. code-block:: cpp + + struct Aggregate { + int a; + std::string b; + }; + + py::class_(m, "Aggregate") + .def(py::init()); + +.. note:: + + Note that brace initialization preferentially invokes constructor overloads + taking a ``std::initializer_list``. In the rare event that this causes an + issue, you can work around it by using ``py::init(...)`` with a lambda + function that constructs the new object as desired. + +.. _classes_with_non_public_destructors: + +Non-public destructors +====================== + +If a class has a private or protected destructor (as might e.g. be the case in +a singleton pattern), a compile error will occur when creating bindings via +pybind11. The underlying issue is that the ``std::unique_ptr`` holder type that +is responsible for managing the lifetime of instances will reference the +destructor even if no deallocations ever take place. In order to expose classes +with private or protected destructors, it is possible to override the holder +type via a holder type argument to ``class_``. Pybind11 provides a helper class +``py::nodelete`` that disables any destructor invocations. In this case, it is +crucial that instances are deallocated on the C++ side to avoid memory leaks. + +.. code-block:: cpp + + /* ... definition ... */ + + class MyClass { + private: + ~MyClass() { } + }; + + /* ... binding code ... */ + + py::class_>(m, "MyClass") + .def(py::init<>()) + +.. _implicit_conversions: + +Implicit conversions +==================== + +Suppose that instances of two types ``A`` and ``B`` are used in a project, and +that an ``A`` can easily be converted into an instance of type ``B`` (examples of this +could be a fixed and an arbitrary precision number type). + +.. code-block:: cpp + + py::class_(m, "A") + /// ... members ... + + py::class_(m, "B") + .def(py::init()) + /// ... members ... + + m.def("func", + [](const B &) { /* .... */ } + ); + +To invoke the function ``func`` using a variable ``a`` containing an ``A`` +instance, we'd have to write ``func(B(a))`` in Python. On the other hand, C++ +will automatically apply an implicit type conversion, which makes it possible +to directly write ``func(a)``. + +In this situation (i.e. where ``B`` has a constructor that converts from +``A``), the following statement enables similar implicit conversions on the +Python side: + +.. code-block:: cpp + + py::implicitly_convertible(); + +.. note:: + + Implicit conversions from ``A`` to ``B`` only work when ``B`` is a custom + data type that is exposed to Python via pybind11. + + To prevent runaway recursion, implicit conversions are non-reentrant: an + implicit conversion invoked as part of another implicit conversion of the + same type (i.e. from ``A`` to ``B``) will fail. + +.. _static_properties: + +Static properties +================= + +The section on :ref:`properties` discussed the creation of instance properties +that are implemented in terms of C++ getters and setters. + +Static properties can also be created in a similar way to expose getters and +setters of static class attributes. Note that the implicit ``self`` argument +also exists in this case and is used to pass the Python ``type`` subclass +instance. This parameter will often not be needed by the C++ side, and the +following example illustrates how to instantiate a lambda getter function +that ignores it: + +.. code-block:: cpp + + py::class_(m, "Foo") + .def_property_readonly_static("foo", [](py::object /* self */) { return Foo(); }); + +Operator overloading +==================== + +Suppose that we're given the following ``Vector2`` class with a vector addition +and scalar multiplication operation, all implemented using overloaded operators +in C++. + +.. code-block:: cpp + + class Vector2 { + public: + Vector2(float x, float y) : x(x), y(y) { } + + Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); } + Vector2 operator*(float value) const { return Vector2(x * value, y * value); } + Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; } + Vector2& operator*=(float v) { x *= v; y *= v; return *this; } + + friend Vector2 operator*(float f, const Vector2 &v) { + return Vector2(f * v.x, f * v.y); + } + + std::string toString() const { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; + } + private: + float x, y; + }; + +The following snippet shows how the above operators can be conveniently exposed +to Python. + +.. code-block:: cpp + + #include + + PYBIND11_MODULE(example, m) { + py::class_(m, "Vector2") + .def(py::init()) + .def(py::self + py::self) + .def(py::self += py::self) + .def(py::self *= float()) + .def(float() * py::self) + .def(py::self * float()) + .def("__repr__", &Vector2::toString); + } + +Note that a line like + +.. code-block:: cpp + + .def(py::self * float()) + +is really just short hand notation for + +.. code-block:: cpp + + .def("__mul__", [](const Vector2 &a, float b) { + return a * b; + }, py::is_operator()) + +This can be useful for exposing additional operators that don't exist on the +C++ side, or to perform other types of customization. The ``py::is_operator`` +flag marker is needed to inform pybind11 that this is an operator, which +returns ``NotImplemented`` when invoked with incompatible arguments rather than +throwing a type error. + +.. note:: + + To use the more convenient ``py::self`` notation, the additional + header file :file:`pybind11/operators.h` must be included. + +.. seealso:: + + The file :file:`tests/test_operator_overloading.cpp` contains a + complete example that demonstrates how to work with overloaded operators in + more detail. + +.. _pickling: + +Pickling support +================ + +Python's ``pickle`` module provides a powerful facility to serialize and +de-serialize a Python object graph into a binary data stream. To pickle and +unpickle C++ classes using pybind11, a ``py::pickle()`` definition must be +provided. Suppose the class in question has the following signature: + +.. code-block:: cpp + + class Pickleable { + public: + Pickleable(const std::string &value) : m_value(value) { } + const std::string &value() const { return m_value; } + + void setExtra(int extra) { m_extra = extra; } + int extra() const { return m_extra; } + private: + std::string m_value; + int m_extra = 0; + }; + +Pickling support in Python is enabled by defining the ``__setstate__`` and +``__getstate__`` methods [#f3]_. For pybind11 classes, use ``py::pickle()`` +to bind these two functions: + +.. code-block:: cpp + + py::class_(m, "Pickleable") + .def(py::init()) + .def("value", &Pickleable::value) + .def("extra", &Pickleable::extra) + .def("setExtra", &Pickleable::setExtra) + .def(py::pickle( + [](const Pickleable &p) { // __getstate__ + /* Return a tuple that fully encodes the state of the object */ + return py::make_tuple(p.value(), p.extra()); + }, + [](py::tuple t) { // __setstate__ + if (t.size() != 2) + throw std::runtime_error("Invalid state!"); + + /* Create a new C++ instance */ + Pickleable p(t[0].cast()); + + /* Assign any additional state */ + p.setExtra(t[1].cast()); + + return p; + } + )); + +The ``__setstate__`` part of the ``py::picke()`` definition follows the same +rules as the single-argument version of ``py::init()``. The return type can be +a value, pointer or holder type. See :ref:`custom_constructors` for details. + +An instance can now be pickled as follows: + +.. code-block:: python + + try: + import cPickle as pickle # Use cPickle on Python 2.7 + except ImportError: + import pickle + + p = Pickleable("test_value") + p.setExtra(15) + data = pickle.dumps(p, 2) + +Note that only the cPickle module is supported on Python 2.7. The second +argument to ``dumps`` is also crucial: it selects the pickle protocol version +2, since the older version 1 is not supported. Newer versions are also fine—for +instance, specify ``-1`` to always use the latest available version. Beware: +failure to follow these instructions will cause important pybind11 memory +allocation routines to be skipped during unpickling, which will likely lead to +memory corruption and/or segmentation faults. + +.. seealso:: + + The file :file:`tests/test_pickling.cpp` contains a complete example + that demonstrates how to pickle and unpickle types using pybind11 in more + detail. + +.. [#f3] http://docs.python.org/3/library/pickle.html#pickling-class-instances + +Multiple Inheritance +==================== + +pybind11 can create bindings for types that derive from multiple base types +(aka. *multiple inheritance*). To do so, specify all bases in the template +arguments of the ``class_`` declaration: + +.. code-block:: cpp + + py::class_(m, "MyType") + ... + +The base types can be specified in arbitrary order, and they can even be +interspersed with alias types and holder types (discussed earlier in this +document)---pybind11 will automatically find out which is which. The only +requirement is that the first template argument is the type to be declared. + +It is also permitted to inherit multiply from exported C++ classes in Python, +as well as inheriting from multiple Python and/or pybind-exported classes. + +There is one caveat regarding the implementation of this feature: + +When only one base type is specified for a C++ type that actually has multiple +bases, pybind11 will assume that it does not participate in multiple +inheritance, which can lead to undefined behavior. In such cases, add the tag +``multiple_inheritance`` to the class constructor: + +.. code-block:: cpp + + py::class_(m, "MyType", py::multiple_inheritance()); + +The tag is redundant and does not need to be specified when multiple base types +are listed. + +.. _module_local: + +Module-local class bindings +=========================== + +When creating a binding for a class, pybind by default makes that binding +"global" across modules. What this means is that a type defined in one module +can be returned from any module resulting in the same Python type. For +example, this allows the following: + +.. code-block:: cpp + + // In the module1.cpp binding code for module1: + py::class_(m, "Pet") + .def(py::init()) + .def_readonly("name", &Pet::name); + +.. code-block:: cpp + + // In the module2.cpp binding code for module2: + m.def("create_pet", [](std::string name) { return new Pet(name); }); + +.. code-block:: pycon + + >>> from module1 import Pet + >>> from module2 import create_pet + >>> pet1 = Pet("Kitty") + >>> pet2 = create_pet("Doggy") + >>> pet2.name() + 'Doggy' + +When writing binding code for a library, this is usually desirable: this +allows, for example, splitting up a complex library into multiple Python +modules. + +In some cases, however, this can cause conflicts. For example, suppose two +unrelated modules make use of an external C++ library and each provide custom +bindings for one of that library's classes. This will result in an error when +a Python program attempts to import both modules (directly or indirectly) +because of conflicting definitions on the external type: + +.. code-block:: cpp + + // dogs.cpp + + // Binding for external library class: + py::class(m, "Pet") + .def("name", &pets::Pet::name); + + // Binding for local extension class: + py::class(m, "Dog") + .def(py::init()); + +.. code-block:: cpp + + // cats.cpp, in a completely separate project from the above dogs.cpp. + + // Binding for external library class: + py::class(m, "Pet") + .def("get_name", &pets::Pet::name); + + // Binding for local extending class: + py::class(m, "Cat") + .def(py::init()); + +.. code-block:: pycon + + >>> import cats + >>> import dogs + Traceback (most recent call last): + File "", line 1, in + ImportError: generic_type: type "Pet" is already registered! + +To get around this, you can tell pybind11 to keep the external class binding +localized to the module by passing the ``py::module_local()`` attribute into +the ``py::class_`` constructor: + +.. code-block:: cpp + + // Pet binding in dogs.cpp: + py::class(m, "Pet", py::module_local()) + .def("name", &pets::Pet::name); + +.. code-block:: cpp + + // Pet binding in cats.cpp: + py::class(m, "Pet", py::module_local()) + .def("get_name", &pets::Pet::name); + +This makes the Python-side ``dogs.Pet`` and ``cats.Pet`` into distinct classes, +avoiding the conflict and allowing both modules to be loaded. C++ code in the +``dogs`` module that casts or returns a ``Pet`` instance will result in a +``dogs.Pet`` Python instance, while C++ code in the ``cats`` module will result +in a ``cats.Pet`` Python instance. + +This does come with two caveats, however: First, external modules cannot return +or cast a ``Pet`` instance to Python (unless they also provide their own local +bindings). Second, from the Python point of view they are two distinct classes. + +Note that the locality only applies in the C++ -> Python direction. When +passing such a ``py::module_local`` type into a C++ function, the module-local +classes are still considered. This means that if the following function is +added to any module (including but not limited to the ``cats`` and ``dogs`` +modules above) it will be callable with either a ``dogs.Pet`` or ``cats.Pet`` +argument: + +.. code-block:: cpp + + m.def("pet_name", [](const pets::Pet &pet) { return pet.name(); }); + +For example, suppose the above function is added to each of ``cats.cpp``, +``dogs.cpp`` and ``frogs.cpp`` (where ``frogs.cpp`` is some other module that +does *not* bind ``Pets`` at all). + +.. code-block:: pycon + + >>> import cats, dogs, frogs # No error because of the added py::module_local() + >>> mycat, mydog = cats.Cat("Fluffy"), dogs.Dog("Rover") + >>> (cats.pet_name(mycat), dogs.pet_name(mydog)) + ('Fluffy', 'Rover') + >>> (cats.pet_name(mydog), dogs.pet_name(mycat), frogs.pet_name(mycat)) + ('Rover', 'Fluffy', 'Fluffy') + +It is possible to use ``py::module_local()`` registrations in one module even +if another module registers the same type globally: within the module with the +module-local definition, all C++ instances will be cast to the associated bound +Python type. In other modules any such values are converted to the global +Python type created elsewhere. + +.. note:: + + STL bindings (as provided via the optional :file:`pybind11/stl_bind.h` + header) apply ``py::module_local`` by default when the bound type might + conflict with other modules; see :ref:`stl_bind` for details. + +.. note:: + + The localization of the bound types is actually tied to the shared object + or binary generated by the compiler/linker. For typical modules created + with ``PYBIND11_MODULE()``, this distinction is not significant. It is + possible, however, when :ref:`embedding` to embed multiple modules in the + same binary (see :ref:`embedding_modules`). In such a case, the + localization will apply across all embedded modules within the same binary. + +.. seealso:: + + The file :file:`tests/test_local_bindings.cpp` contains additional examples + that demonstrate how ``py::module_local()`` works. + +Binding protected member functions +================================== + +It's normally not possible to expose ``protected`` member functions to Python: + +.. code-block:: cpp + + class A { + protected: + int foo() const { return 42; } + }; + + py::class_(m, "A") + .def("foo", &A::foo); // error: 'foo' is a protected member of 'A' + +On one hand, this is good because non-``public`` members aren't meant to be +accessed from the outside. But we may want to make use of ``protected`` +functions in derived Python classes. + +The following pattern makes this possible: + +.. code-block:: cpp + + class A { + protected: + int foo() const { return 42; } + }; + + class Publicist : public A { // helper type for exposing protected functions + public: + using A::foo; // inherited with different access modifier + }; + + py::class_(m, "A") // bind the primary class + .def("foo", &Publicist::foo); // expose protected methods via the publicist + +This works because ``&Publicist::foo`` is exactly the same function as +``&A::foo`` (same signature and address), just with a different access +modifier. The only purpose of the ``Publicist`` helper class is to make +the function name ``public``. + +If the intent is to expose ``protected`` ``virtual`` functions which can be +overridden in Python, the publicist pattern can be combined with the previously +described trampoline: + +.. code-block:: cpp + + class A { + public: + virtual ~A() = default; + + protected: + virtual int foo() const { return 42; } + }; + + class Trampoline : public A { + public: + int foo() const override { PYBIND11_OVERLOAD(int, A, foo, ); } + }; + + class Publicist : public A { + public: + using A::foo; + }; + + py::class_(m, "A") // <-- `Trampoline` here + .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! + +.. note:: + + MSVC 2015 has a compiler bug (fixed in version 2017) which + requires a more explicit function binding in the form of + ``.def("foo", static_cast(&Publicist::foo));`` + where ``int (A::*)() const`` is the type of ``A::foo``. diff --git a/external/pybind11/docs/advanced/embedding.rst b/external/pybind11/docs/advanced/embedding.rst new file mode 100644 index 0000000..3930316 --- /dev/null +++ b/external/pybind11/docs/advanced/embedding.rst @@ -0,0 +1,261 @@ +.. _embedding: + +Embedding the interpreter +######################### + +While pybind11 is mainly focused on extending Python using C++, it's also +possible to do the reverse: embed the Python interpreter into a C++ program. +All of the other documentation pages still apply here, so refer to them for +general pybind11 usage. This section will cover a few extra things required +for embedding. + +Getting started +=============== + +A basic executable with an embedded interpreter can be created with just a few +lines of CMake and the ``pybind11::embed`` target, as shown below. For more +information, see :doc:`/compiling`. + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.0) + project(example) + + find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)` + + add_executable(example main.cpp) + target_link_libraries(example PRIVATE pybind11::embed) + +The essential structure of the ``main.cpp`` file looks like this: + +.. code-block:: cpp + + #include // everything needed for embedding + namespace py = pybind11; + + int main() { + py::scoped_interpreter guard{}; // start the interpreter and keep it alive + + py::print("Hello, World!"); // use the Python API + } + +The interpreter must be initialized before using any Python API, which includes +all the functions and classes in pybind11. The RAII guard class `scoped_interpreter` +takes care of the interpreter lifetime. After the guard is destroyed, the interpreter +shuts down and clears its memory. No Python functions can be called after this. + +Executing Python code +===================== + +There are a few different ways to run Python code. One option is to use `eval`, +`exec` or `eval_file`, as explained in :ref:`eval`. Here is a quick example in +the context of an executable with an embedded interpreter: + +.. code-block:: cpp + + #include + namespace py = pybind11; + + int main() { + py::scoped_interpreter guard{}; + + py::exec(R"( + kwargs = dict(name="World", number=42) + message = "Hello, {name}! The answer is {number}".format(**kwargs) + print(message) + )"); + } + +Alternatively, similar results can be achieved using pybind11's API (see +:doc:`/advanced/pycpp/index` for more details). + +.. code-block:: cpp + + #include + namespace py = pybind11; + using namespace py::literals; + + int main() { + py::scoped_interpreter guard{}; + + auto kwargs = py::dict("name"_a="World", "number"_a=42); + auto message = "Hello, {name}! The answer is {number}"_s.format(**kwargs); + py::print(message); + } + +The two approaches can also be combined: + +.. code-block:: cpp + + #include + #include + + namespace py = pybind11; + using namespace py::literals; + + int main() { + py::scoped_interpreter guard{}; + + auto locals = py::dict("name"_a="World", "number"_a=42); + py::exec(R"( + message = "Hello, {name}! The answer is {number}".format(**locals()) + )", py::globals(), locals); + + auto message = locals["message"].cast(); + std::cout << message; + } + +Importing modules +================= + +Python modules can be imported using `module::import()`: + +.. code-block:: cpp + + py::module sys = py::module::import("sys"); + py::print(sys.attr("path")); + +For convenience, the current working directory is included in ``sys.path`` when +embedding the interpreter. This makes it easy to import local Python files: + +.. code-block:: python + + """calc.py located in the working directory""" + + def add(i, j): + return i + j + + +.. code-block:: cpp + + py::module calc = py::module::import("calc"); + py::object result = calc.attr("add")(1, 2); + int n = result.cast(); + assert(n == 3); + +Modules can be reloaded using `module::reload()` if the source is modified e.g. +by an external process. This can be useful in scenarios where the application +imports a user defined data processing script which needs to be updated after +changes by the user. Note that this function does not reload modules recursively. + +.. _embedding_modules: + +Adding embedded modules +======================= + +Embedded binary modules can be added using the `PYBIND11_EMBEDDED_MODULE` macro. +Note that the definition must be placed at global scope. They can be imported +like any other module. + +.. code-block:: cpp + + #include + namespace py = pybind11; + + PYBIND11_EMBEDDED_MODULE(fast_calc, m) { + // `m` is a `py::module` which is used to bind functions and classes + m.def("add", [](int i, int j) { + return i + j; + }); + } + + int main() { + py::scoped_interpreter guard{}; + + auto fast_calc = py::module::import("fast_calc"); + auto result = fast_calc.attr("add")(1, 2).cast(); + assert(result == 3); + } + +Unlike extension modules where only a single binary module can be created, on +the embedded side an unlimited number of modules can be added using multiple +`PYBIND11_EMBEDDED_MODULE` definitions (as long as they have unique names). + +These modules are added to Python's list of builtins, so they can also be +imported in pure Python files loaded by the interpreter. Everything interacts +naturally: + +.. code-block:: python + + """py_module.py located in the working directory""" + import cpp_module + + a = cpp_module.a + b = a + 1 + + +.. code-block:: cpp + + #include + namespace py = pybind11; + + PYBIND11_EMBEDDED_MODULE(cpp_module, m) { + m.attr("a") = 1; + } + + int main() { + py::scoped_interpreter guard{}; + + auto py_module = py::module::import("py_module"); + + auto locals = py::dict("fmt"_a="{} + {} = {}", **py_module.attr("__dict__")); + assert(locals["a"].cast() == 1); + assert(locals["b"].cast() == 2); + + py::exec(R"( + c = a + b + message = fmt.format(a, b, c) + )", py::globals(), locals); + + assert(locals["c"].cast() == 3); + assert(locals["message"].cast() == "1 + 2 = 3"); + } + + +Interpreter lifetime +==================== + +The Python interpreter shuts down when `scoped_interpreter` is destroyed. After +this, creating a new instance will restart the interpreter. Alternatively, the +`initialize_interpreter` / `finalize_interpreter` pair of functions can be used +to directly set the state at any time. + +Modules created with pybind11 can be safely re-initialized after the interpreter +has been restarted. However, this may not apply to third-party extension modules. +The issue is that Python itself cannot completely unload extension modules and +there are several caveats with regard to interpreter restarting. In short, not +all memory may be freed, either due to Python reference cycles or user-created +global data. All the details can be found in the CPython documentation. + +.. warning:: + + Creating two concurrent `scoped_interpreter` guards is a fatal error. So is + calling `initialize_interpreter` for a second time after the interpreter + has already been initialized. + + Do not use the raw CPython API functions ``Py_Initialize`` and + ``Py_Finalize`` as these do not properly handle the lifetime of + pybind11's internal data. + + +Sub-interpreter support +======================= + +Creating multiple copies of `scoped_interpreter` is not possible because it +represents the main Python interpreter. Sub-interpreters are something different +and they do permit the existence of multiple interpreters. This is an advanced +feature of the CPython API and should be handled with care. pybind11 does not +currently offer a C++ interface for sub-interpreters, so refer to the CPython +documentation for all the details regarding this feature. + +We'll just mention a couple of caveats the sub-interpreters support in pybind11: + + 1. Sub-interpreters will not receive independent copies of embedded modules. + Instead, these are shared and modifications in one interpreter may be + reflected in another. + + 2. Managing multiple threads, multiple interpreters and the GIL can be + challenging and there are several caveats here, even within the pure + CPython API (please refer to the Python docs for details). As for + pybind11, keep in mind that `gil_scoped_release` and `gil_scoped_acquire` + do not take sub-interpreters into account. diff --git a/external/pybind11/docs/advanced/exceptions.rst b/external/pybind11/docs/advanced/exceptions.rst new file mode 100644 index 0000000..3483379 --- /dev/null +++ b/external/pybind11/docs/advanced/exceptions.rst @@ -0,0 +1,142 @@ +Exceptions +########## + +Built-in exception translation +============================== + +When C++ code invoked from Python throws an ``std::exception``, it is +automatically converted into a Python ``Exception``. pybind11 defines multiple +special exception classes that will map to different types of Python +exceptions: + +.. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}| + ++--------------------------------------+------------------------------+ +| C++ exception type | Python exception type | ++======================================+==============================+ +| :class:`std::exception` | ``RuntimeError`` | ++--------------------------------------+------------------------------+ +| :class:`std::bad_alloc` | ``MemoryError`` | ++--------------------------------------+------------------------------+ +| :class:`std::domain_error` | ``ValueError`` | ++--------------------------------------+------------------------------+ +| :class:`std::invalid_argument` | ``ValueError`` | ++--------------------------------------+------------------------------+ +| :class:`std::length_error` | ``ValueError`` | ++--------------------------------------+------------------------------+ +| :class:`std::out_of_range` | ``ValueError`` | ++--------------------------------------+------------------------------+ +| :class:`std::range_error` | ``ValueError`` | ++--------------------------------------+------------------------------+ +| :class:`pybind11::stop_iteration` | ``StopIteration`` (used to | +| | implement custom iterators) | ++--------------------------------------+------------------------------+ +| :class:`pybind11::index_error` | ``IndexError`` (used to | +| | indicate out of bounds | +| | accesses in ``__getitem__``, | +| | ``__setitem__``, etc.) | ++--------------------------------------+------------------------------+ +| :class:`pybind11::value_error` | ``ValueError`` (used to | +| | indicate wrong value passed | +| | in ``container.remove(...)`` | ++--------------------------------------+------------------------------+ +| :class:`pybind11::key_error` | ``KeyError`` (used to | +| | indicate out of bounds | +| | accesses in ``__getitem__``, | +| | ``__setitem__`` in dict-like | +| | objects, etc.) | ++--------------------------------------+------------------------------+ +| :class:`pybind11::error_already_set` | Indicates that the Python | +| | exception flag has already | +| | been initialized | ++--------------------------------------+------------------------------+ + +When a Python function invoked from C++ throws an exception, it is converted +into a C++ exception of type :class:`error_already_set` whose string payload +contains a textual summary. + +There is also a special exception :class:`cast_error` that is thrown by +:func:`handle::call` when the input arguments cannot be converted to Python +objects. + +Registering custom translators +============================== + +If the default exception conversion policy described above is insufficient, +pybind11 also provides support for registering custom exception translators. +To register a simple exception conversion that translates a C++ exception into +a new Python exception using the C++ exception's ``what()`` method, a helper +function is available: + +.. code-block:: cpp + + py::register_exception(module, "PyExp"); + +This call creates a Python exception class with the name ``PyExp`` in the given +module and automatically converts any encountered exceptions of type ``CppExp`` +into Python exceptions of type ``PyExp``. + +When more advanced exception translation is needed, the function +``py::register_exception_translator(translator)`` can be used to register +functions that can translate arbitrary exception types (and which may include +additional logic to do so). The function takes a stateless callable (e.g. a +function pointer or a lambda function without captured variables) with the call +signature ``void(std::exception_ptr)``. + +When a C++ exception is thrown, the registered exception translators are tried +in reverse order of registration (i.e. the last registered translator gets the +first shot at handling the exception). + +Inside the translator, ``std::rethrow_exception`` should be used within +a try block to re-throw the exception. One or more catch clauses to catch +the appropriate exceptions should then be used with each clause using +``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set +the python exception to a custom exception type (see below). + +To declare a custom Python exception type, declare a ``py::exception`` variable +and use this in the associated exception translator (note: it is often useful +to make this a static declaration when using it inside a lambda expression +without requiring capturing). + + +The following example demonstrates this for a hypothetical exception classes +``MyCustomException`` and ``OtherException``: the first is translated to a +custom python exception ``MyCustomError``, while the second is translated to a +standard python RuntimeError: + +.. code-block:: cpp + + static py::exception exc(m, "MyCustomError"); + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const MyCustomException &e) { + exc(e.what()); + } catch (const OtherException &e) { + PyErr_SetString(PyExc_RuntimeError, e.what()); + } + }); + +Multiple exceptions can be handled by a single translator, as shown in the +example above. If the exception is not caught by the current translator, the +previously registered one gets a chance. + +If none of the registered exception translators is able to handle the +exception, it is handled by the default converter as described in the previous +section. + +.. seealso:: + + The file :file:`tests/test_exceptions.cpp` contains examples + of various custom exception translators and custom exception types. + +.. note:: + + You must call either ``PyErr_SetString`` or a custom exception's call + operator (``exc(string)``) for every exception caught in a custom exception + translator. Failure to do so will cause Python to crash with ``SystemError: + error return without exception set``. + + Exceptions that you do not plan to handle should simply not be caught, or + may be explicity (re-)thrown to delegate it to the other, + previously-declared existing exception translators. diff --git a/external/pybind11/docs/advanced/functions.rst b/external/pybind11/docs/advanced/functions.rst new file mode 100644 index 0000000..c7892b5 --- /dev/null +++ b/external/pybind11/docs/advanced/functions.rst @@ -0,0 +1,498 @@ +Functions +######### + +Before proceeding with this section, make sure that you are already familiar +with the basics of binding functions and classes, as explained in :doc:`/basics` +and :doc:`/classes`. The following guide is applicable to both free and member +functions, i.e. *methods* in Python. + +.. _return_value_policies: + +Return value policies +===================== + +Python and C++ use fundamentally different ways of managing the memory and +lifetime of objects managed by them. This can lead to issues when creating +bindings for functions that return a non-trivial type. Just by looking at the +type information, it is not clear whether Python should take charge of the +returned value and eventually free its resources, or if this is handled on the +C++ side. For this reason, pybind11 provides a several *return value policy* +annotations that can be passed to the :func:`module::def` and +:func:`class_::def` functions. The default policy is +:enum:`return_value_policy::automatic`. + +Return value policies are tricky, and it's very important to get them right. +Just to illustrate what can go wrong, consider the following simple example: + +.. code-block:: cpp + + /* Function declaration */ + Data *get_data() { return _data; /* (pointer to a static data structure) */ } + ... + + /* Binding code */ + m.def("get_data", &get_data); // <-- KABOOM, will cause crash when called from Python + +What's going on here? When ``get_data()`` is called from Python, the return +value (a native C++ type) must be wrapped to turn it into a usable Python type. +In this case, the default return value policy (:enum:`return_value_policy::automatic`) +causes pybind11 to assume ownership of the static ``_data`` instance. + +When Python's garbage collector eventually deletes the Python +wrapper, pybind11 will also attempt to delete the C++ instance (via ``operator +delete()``) due to the implied ownership. At this point, the entire application +will come crashing down, though errors could also be more subtle and involve +silent data corruption. + +In the above example, the policy :enum:`return_value_policy::reference` should have +been specified so that the global data instance is only *referenced* without any +implied transfer of ownership, i.e.: + +.. code-block:: cpp + + m.def("get_data", &get_data, return_value_policy::reference); + +On the other hand, this is not the right policy for many other situations, +where ignoring ownership could lead to resource leaks. +As a developer using pybind11, it's important to be familiar with the different +return value policies, including which situation calls for which one of them. +The following table provides an overview of available policies: + +.. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}| + ++--------------------------------------------------+----------------------------------------------------------------------------+ +| Return value policy | Description | ++==================================================+============================================================================+ +| :enum:`return_value_policy::take_ownership` | Reference an existing object (i.e. do not create a new copy) and take | +| | ownership. Python will call the destructor and delete operator when the | +| | object's reference count reaches zero. Undefined behavior ensues when the | +| | C++ side does the same, or when the data was not dynamically allocated. | ++--------------------------------------------------+----------------------------------------------------------------------------+ +| :enum:`return_value_policy::copy` | Create a new copy of the returned object, which will be owned by Python. | +| | This policy is comparably safe because the lifetimes of the two instances | +| | are decoupled. | ++--------------------------------------------------+----------------------------------------------------------------------------+ +| :enum:`return_value_policy::move` | Use ``std::move`` to move the return value contents into a new instance | +| | that will be owned by Python. This policy is comparably safe because the | +| | lifetimes of the two instances (move source and destination) are decoupled.| ++--------------------------------------------------+----------------------------------------------------------------------------+ +| :enum:`return_value_policy::reference` | Reference an existing object, but do not take ownership. The C++ side is | +| | responsible for managing the object's lifetime and deallocating it when | +| | it is no longer used. Warning: undefined behavior will ensue when the C++ | +| | side deletes an object that is still referenced and used by Python. | ++--------------------------------------------------+----------------------------------------------------------------------------+ +| :enum:`return_value_policy::reference_internal` | Indicates that the lifetime of the return value is tied to the lifetime | +| | of a parent object, namely the implicit ``this``, or ``self`` argument of | +| | the called method or property. Internally, this policy works just like | +| | :enum:`return_value_policy::reference` but additionally applies a | +| | ``keep_alive<0, 1>`` *call policy* (described in the next section) that | +| | prevents the parent object from being garbage collected as long as the | +| | return value is referenced by Python. This is the default policy for | +| | property getters created via ``def_property``, ``def_readwrite``, etc. | ++--------------------------------------------------+----------------------------------------------------------------------------+ +| :enum:`return_value_policy::automatic` | **Default policy.** This policy falls back to the policy | +| | :enum:`return_value_policy::take_ownership` when the return value is a | +| | pointer. Otherwise, it uses :enum:`return_value_policy::move` or | +| | :enum:`return_value_policy::copy` for rvalue and lvalue references, | +| | respectively. See above for a description of what all of these different | +| | policies do. | ++--------------------------------------------------+----------------------------------------------------------------------------+ +| :enum:`return_value_policy::automatic_reference` | As above, but use policy :enum:`return_value_policy::reference` when the | +| | return value is a pointer. This is the default conversion policy for | +| | function arguments when calling Python functions manually from C++ code | +| | (i.e. via handle::operator()). You probably won't need to use this. | ++--------------------------------------------------+----------------------------------------------------------------------------+ + +Return value policies can also be applied to properties: + +.. code-block:: cpp + + class_(m, "MyClass") + .def_property("data", &MyClass::getData, &MyClass::setData, + py::return_value_policy::copy); + +Technically, the code above applies the policy to both the getter and the +setter function, however, the setter doesn't really care about *return* +value policies which makes this a convenient terse syntax. Alternatively, +targeted arguments can be passed through the :class:`cpp_function` constructor: + +.. code-block:: cpp + + class_(m, "MyClass") + .def_property("data" + py::cpp_function(&MyClass::getData, py::return_value_policy::copy), + py::cpp_function(&MyClass::setData) + ); + +.. warning:: + + Code with invalid return value policies might access unitialized memory or + free data structures multiple times, which can lead to hard-to-debug + non-determinism and segmentation faults, hence it is worth spending the + time to understand all the different options in the table above. + +.. note:: + + One important aspect of the above policies is that they only apply to + instances which pybind11 has *not* seen before, in which case the policy + clarifies essential questions about the return value's lifetime and + ownership. When pybind11 knows the instance already (as identified by its + type and address in memory), it will return the existing Python object + wrapper rather than creating a new copy. + +.. note:: + + The next section on :ref:`call_policies` discusses *call policies* that can be + specified *in addition* to a return value policy from the list above. Call + policies indicate reference relationships that can involve both return values + and parameters of functions. + +.. note:: + + As an alternative to elaborate call policies and lifetime management logic, + consider using smart pointers (see the section on :ref:`smart_pointers` for + details). Smart pointers can tell whether an object is still referenced from + C++ or Python, which generally eliminates the kinds of inconsistencies that + can lead to crashes or undefined behavior. For functions returning smart + pointers, it is not necessary to specify a return value policy. + +.. _call_policies: + +Additional call policies +======================== + +In addition to the above return value policies, further *call policies* can be +specified to indicate dependencies between parameters or ensure a certain state +for the function call. + +Keep alive +---------- + +In general, this policy is required when the C++ object is any kind of container +and another object is being added to the container. ``keep_alive`` +indicates that the argument with index ``Patient`` should be kept alive at least +until the argument with index ``Nurse`` is freed by the garbage collector. Argument +indices start at one, while zero refers to the return value. For methods, index +``1`` refers to the implicit ``this`` pointer, while regular arguments begin at +index ``2``. Arbitrarily many call policies can be specified. When a ``Nurse`` +with value ``None`` is detected at runtime, the call policy does nothing. + +When the nurse is not a pybind11-registered type, the implementation internally +relies on the ability to create a *weak reference* to the nurse object. When +the nurse object is not a pybind11-registered type and does not support weak +references, an exception will be thrown. + +Consider the following example: here, the binding code for a list append +operation ties the lifetime of the newly added element to the underlying +container: + +.. code-block:: cpp + + py::class_(m, "List") + .def("append", &List::append, py::keep_alive<1, 2>()); + +For consistency, the argument indexing is identical for constructors. Index +``1`` still refers to the implicit ``this`` pointer, i.e. the object which is +being constructed. Index ``0`` refers to the return type which is presumed to +be ``void`` when a constructor is viewed like a function. The following example +ties the lifetime of the constructor element to the constructed object: + +.. code-block:: cpp + + py::class_(m, "Nurse") + .def(py::init(), py::keep_alive<1, 2>()); + +.. note:: + + ``keep_alive`` is analogous to the ``with_custodian_and_ward`` (if Nurse, + Patient != 0) and ``with_custodian_and_ward_postcall`` (if Nurse/Patient == + 0) policies from Boost.Python. + +Call guard +---------- + +The ``call_guard`` policy allows any scope guard type ``T`` to be placed +around the function call. For example, this definition: + +.. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + +is equivalent to the following pseudocode: + +.. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + +The only requirement is that ``T`` is default-constructible, but otherwise any +scope guard will work. This is very useful in combination with `gil_scoped_release`. +See :ref:`gil`. + +Multiple guards can also be specified as ``py::call_guard``. The +constructor order is left to right and destruction happens in reverse. + +.. seealso:: + + The file :file:`tests/test_call_policies.cpp` contains a complete example + that demonstrates using `keep_alive` and `call_guard` in more detail. + +.. _python_objects_as_args: + +Python objects as arguments +=========================== + +pybind11 exposes all major Python types using thin C++ wrapper classes. These +wrapper classes can also be used as parameters of functions in bindings, which +makes it possible to directly work with native Python types on the C++ side. +For instance, the following statement iterates over a Python ``dict``: + +.. code-block:: cpp + + void print_dict(py::dict dict) { + /* Easily interact with Python types */ + for (auto item : dict) + std::cout << "key=" << std::string(py::str(item.first)) << ", " + << "value=" << std::string(py::str(item.second)) << std::endl; + } + +It can be exported: + +.. code-block:: cpp + + m.def("print_dict", &print_dict); + +And used in Python as usual: + +.. code-block:: pycon + + >>> print_dict({'foo': 123, 'bar': 'hello'}) + key=foo, value=123 + key=bar, value=hello + +For more information on using Python objects in C++, see :doc:`/advanced/pycpp/index`. + +Accepting \*args and \*\*kwargs +=============================== + +Python provides a useful mechanism to define functions that accept arbitrary +numbers of arguments and keyword arguments: + +.. code-block:: python + + def generic(*args, **kwargs): + ... # do something with args and kwargs + +Such functions can also be created using pybind11: + +.. code-block:: cpp + + void generic(py::args args, py::kwargs kwargs) { + /// .. do something with args + if (kwargs) + /// .. do something with kwargs + } + + /// Binding code + m.def("generic", &generic); + +The class ``py::args`` derives from ``py::tuple`` and ``py::kwargs`` derives +from ``py::dict``. + +You may also use just one or the other, and may combine these with other +arguments as long as the ``py::args`` and ``py::kwargs`` arguments are the last +arguments accepted by the function. + +Please refer to the other examples for details on how to iterate over these, +and on how to cast their entries into C++ objects. A demonstration is also +available in ``tests/test_kwargs_and_defaults.cpp``. + +.. note:: + + When combining \*args or \*\*kwargs with :ref:`keyword_args` you should + *not* include ``py::arg`` tags for the ``py::args`` and ``py::kwargs`` + arguments. + +Default arguments revisited +=========================== + +The section on :ref:`default_args` previously discussed basic usage of default +arguments using pybind11. One noteworthy aspect of their implementation is that +default arguments are converted to Python objects right at declaration time. +Consider the following example: + +.. code-block:: cpp + + py::class_("MyClass") + .def("myFunction", py::arg("arg") = SomeType(123)); + +In this case, pybind11 must already be set up to deal with values of the type +``SomeType`` (via a prior instantiation of ``py::class_``), or an +exception will be thrown. + +Another aspect worth highlighting is that the "preview" of the default argument +in the function signature is generated using the object's ``__repr__`` method. +If not available, the signature may not be very helpful, e.g.: + +.. code-block:: pycon + + FUNCTIONS + ... + | myFunction(...) + | Signature : (MyClass, arg : SomeType = ) -> NoneType + ... + +The first way of addressing this is by defining ``SomeType.__repr__``. +Alternatively, it is possible to specify the human-readable preview of the +default argument manually using the ``arg_v`` notation: + +.. code-block:: cpp + + py::class_("MyClass") + .def("myFunction", py::arg_v("arg", SomeType(123), "SomeType(123)")); + +Sometimes it may be necessary to pass a null pointer value as a default +argument. In this case, remember to cast it to the underlying type in question, +like so: + +.. code-block:: cpp + + py::class_("MyClass") + .def("myFunction", py::arg("arg") = (SomeType *) nullptr); + +.. _nonconverting_arguments: + +Non-converting arguments +======================== + +Certain argument types may support conversion from one type to another. Some +examples of conversions are: + +* :ref:`implicit_conversions` declared using ``py::implicitly_convertible()`` +* Calling a method accepting a double with an integer argument +* Calling a ``std::complex`` argument with a non-complex python type + (for example, with a float). (Requires the optional ``pybind11/complex.h`` + header). +* Calling a function taking an Eigen matrix reference with a numpy array of the + wrong type or of an incompatible data layout. (Requires the optional + ``pybind11/eigen.h`` header). + +This behaviour is sometimes undesirable: the binding code may prefer to raise +an error rather than convert the argument. This behaviour can be obtained +through ``py::arg`` by calling the ``.noconvert()`` method of the ``py::arg`` +object, such as: + +.. code-block:: cpp + + m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); + m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); + +Attempting the call the second function (the one without ``.noconvert()``) with +an integer will succeed, but attempting to call the ``.noconvert()`` version +will fail with a ``TypeError``: + +.. code-block:: pycon + + >>> floats_preferred(4) + 2.0 + >>> floats_only(4) + Traceback (most recent call last): + File "", line 1, in + TypeError: floats_only(): incompatible function arguments. The following argument types are supported: + 1. (f: float) -> float + + Invoked with: 4 + +You may, of course, combine this with the :var:`_a` shorthand notation (see +:ref:`keyword_args`) and/or :ref:`default_args`. It is also permitted to omit +the argument name by using the ``py::arg()`` constructor without an argument +name, i.e. by specifying ``py::arg().noconvert()``. + +.. note:: + + When specifying ``py::arg`` options it is necessary to provide the same + number of options as the bound function has arguments. Thus if you want to + enable no-convert behaviour for just one of several arguments, you will + need to specify a ``py::arg()`` annotation for each argument with the + no-convert argument modified to ``py::arg().noconvert()``. + +.. _none_arguments: + +Allow/Prohibiting None arguments +================================ + +When a C++ type registered with :class:`py::class_` is passed as an argument to +a function taking the instance as pointer or shared holder (e.g. ``shared_ptr`` +or a custom, copyable holder as described in :ref:`smart_pointers`), pybind +allows ``None`` to be passed from Python which results in calling the C++ +function with ``nullptr`` (or an empty holder) for the argument. + +To explicitly enable or disable this behaviour, using the +``.none`` method of the :class:`py::arg` object: + +.. code-block:: cpp + + py::class_(m, "Dog").def(py::init<>()); + py::class_(m, "Cat").def(py::init<>()); + m.def("bark", [](Dog *dog) -> std::string { + if (dog) return "woof!"; /* Called with a Dog instance */ + else return "(no dog)"; /* Called with None, d == nullptr */ + }, py::arg("dog").none(true)); + m.def("meow", [](Cat *cat) -> std::string { + // Can't be called with None argument + return "meow"; + }, py::arg("cat").none(false)); + +With the above, the Python call ``bark(None)`` will return the string ``"(no +dog)"``, while attempting to call ``meow(None)`` will raise a ``TypeError``: + +.. code-block:: pycon + + >>> from animals import Dog, Cat, bark, meow + >>> bark(Dog()) + 'woof!' + >>> meow(Cat()) + 'meow' + >>> bark(None) + '(no dog)' + >>> meow(None) + Traceback (most recent call last): + File "", line 1, in + TypeError: meow(): incompatible function arguments. The following argument types are supported: + 1. (cat: animals.Cat) -> str + + Invoked with: None + +The default behaviour when the tag is unspecified is to allow ``None``. + +Overload resolution order +========================= + +When a function or method with multiple overloads is called from Python, +pybind11 determines which overload to call in two passes. The first pass +attempts to call each overload without allowing argument conversion (as if +every argument had been specified as ``py::arg().noconvert()`` as decribed +above). + +If no overload succeeds in the no-conversion first pass, a second pass is +attempted in which argument conversion is allowed (except where prohibited via +an explicit ``py::arg().noconvert()`` attribute in the function definition). + +If the second pass also fails a ``TypeError`` is raised. + +Within each pass, overloads are tried in the order they were registered with +pybind11. + +What this means in practice is that pybind11 will prefer any overload that does +not require conversion of arguments to an overload that does, but otherwise prefers +earlier-defined overloads to later-defined ones. + +.. note:: + + pybind11 does *not* further prioritize based on the number/pattern of + overloaded arguments. That is, pybind11 does not prioritize a function + requiring one conversion over one requiring three, but only prioritizes + overloads requiring no conversion at all to overloads that require + conversion of at least one argument. diff --git a/external/pybind11/docs/advanced/misc.rst b/external/pybind11/docs/advanced/misc.rst new file mode 100644 index 0000000..87481ba --- /dev/null +++ b/external/pybind11/docs/advanced/misc.rst @@ -0,0 +1,272 @@ +Miscellaneous +############# + +.. _macro_notes: + +General notes regarding convenience macros +========================================== + +pybind11 provides a few convenience macros such as +:func:`PYBIND11_MAKE_OPAQUE` and :func:`PYBIND11_DECLARE_HOLDER_TYPE`, and +``PYBIND11_OVERLOAD_*``. Since these are "just" macros that are evaluated +in the preprocessor (which has no concept of types), they *will* get confused +by commas in a template argument such as ``PYBIND11_OVERLOAD(MyReturnValue, myFunc)``. In this case, the preprocessor assumes that the comma indicates +the beginning of the next parameter. Use a ``typedef`` to bind the template to +another name and use it in the macro to avoid this problem. + +.. _gil: + +Global Interpreter Lock (GIL) +============================= + +When calling a C++ function from Python, the GIL is always held. +The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be +used to acquire and release the global interpreter lock in the body of a C++ +function call. In this way, long-running C++ code can be parallelized using +multiple Python threads. Taking :ref:`overriding_virtuals` as an example, this +could be realized as follows (important changes highlighted): + +.. code-block:: cpp + :emphasize-lines: 8,9,31,32 + + class PyAnimal : public Animal { + public: + /* Inherit the constructors */ + using Animal::Animal; + + /* Trampoline (need one for each virtual function) */ + std::string go(int n_times) { + /* Acquire GIL before calling Python code */ + py::gil_scoped_acquire acquire; + + PYBIND11_OVERLOAD_PURE( + std::string, /* Return type */ + Animal, /* Parent class */ + go, /* Name of function */ + n_times /* Argument(s) */ + ); + } + }; + + PYBIND11_MODULE(example, m) { + py::class_ animal(m, "Animal"); + animal + .def(py::init<>()) + .def("go", &Animal::go); + + py::class_(m, "Dog", animal) + .def(py::init<>()); + + m.def("call_go", [](Animal *animal) -> std::string { + /* Release GIL before calling into (potentially long-running) C++ code */ + py::gil_scoped_release release; + return call_go(animal); + }); + } + +The ``call_go`` wrapper can also be simplified using the `call_guard` policy +(see :ref:`call_policies`) which yields the same result: + +.. code-block:: cpp + + m.def("call_go", &call_go, py::call_guard()); + + +Binding sequence data types, iterators, the slicing protocol, etc. +================================================================== + +Please refer to the supplemental example for details. + +.. seealso:: + + The file :file:`tests/test_sequences_and_iterators.cpp` contains a + complete example that shows how to bind a sequence data type, including + length queries (``__len__``), iterators (``__iter__``), the slicing + protocol and other kinds of useful operations. + + +Partitioning code over multiple extension modules +================================================= + +It's straightforward to split binding code over multiple extension modules, +while referencing types that are declared elsewhere. Everything "just" works +without any special precautions. One exception to this rule occurs when +extending a type declared in another extension module. Recall the basic example +from Section :ref:`inheritance`. + +.. code-block:: cpp + + py::class_ pet(m, "Pet"); + pet.def(py::init()) + .def_readwrite("name", &Pet::name); + + py::class_(m, "Dog", pet /* <- specify parent */) + .def(py::init()) + .def("bark", &Dog::bark); + +Suppose now that ``Pet`` bindings are defined in a module named ``basic``, +whereas the ``Dog`` bindings are defined somewhere else. The challenge is of +course that the variable ``pet`` is not available anymore though it is needed +to indicate the inheritance relationship to the constructor of ``class_``. +However, it can be acquired as follows: + +.. code-block:: cpp + + py::object pet = (py::object) py::module::import("basic").attr("Pet"); + + py::class_(m, "Dog", pet) + .def(py::init()) + .def("bark", &Dog::bark); + +Alternatively, you can specify the base class as a template parameter option to +``class_``, which performs an automated lookup of the corresponding Python +type. Like the above code, however, this also requires invoking the ``import`` +function once to ensure that the pybind11 binding code of the module ``basic`` +has been executed: + +.. code-block:: cpp + + py::module::import("basic"); + + py::class_(m, "Dog") + .def(py::init()) + .def("bark", &Dog::bark); + +Naturally, both methods will fail when there are cyclic dependencies. + +Note that pybind11 code compiled with hidden-by-default symbol visibility (e.g. +via the command line flag ``-fvisibility=hidden`` on GCC/Clang), which is +required proper pybind11 functionality, can interfere with the ability to +access types defined in another extension module. Working around this requires +manually exporting types that are accessed by multiple extension modules; +pybind11 provides a macro to do just this: + +.. code-block:: cpp + + class PYBIND11_EXPORT Dog : public Animal { + ... + }; + +Note also that it is possible (although would rarely be required) to share arbitrary +C++ objects between extension modules at runtime. Internal library data is shared +between modules using capsule machinery [#f6]_ which can be also utilized for +storing, modifying and accessing user-defined data. Note that an extension module +will "see" other extensions' data if and only if they were built with the same +pybind11 version. Consider the following example: + +.. code-block:: cpp + + auto data = (MyData *) py::get_shared_data("mydata"); + if (!data) + data = (MyData *) py::set_shared_data("mydata", new MyData(42)); + +If the above snippet was used in several separately compiled extension modules, +the first one to be imported would create a ``MyData`` instance and associate +a ``"mydata"`` key with a pointer to it. Extensions that are imported later +would be then able to access the data behind the same pointer. + +.. [#f6] https://docs.python.org/3/extending/extending.html#using-capsules + +Module Destructors +================== + +pybind11 does not provide an explicit mechanism to invoke cleanup code at +module destruction time. In rare cases where such functionality is required, it +is possible to emulate it using Python capsules or weak references with a +destruction callback. + +.. code-block:: cpp + + auto cleanup_callback = []() { + // perform cleanup here -- this function is called with the GIL held + }; + + m.add_object("_cleanup", py::capsule(cleanup_callback)); + +This approach has the potential downside that instances of classes exposed +within the module may still be alive when the cleanup callback is invoked +(whether this is acceptable will generally depend on the application). + +Alternatively, the capsule may also be stashed within a type object, which +ensures that it not called before all instances of that type have been +collected: + +.. code-block:: cpp + + auto cleanup_callback = []() { /* ... */ }; + m.attr("BaseClass").attr("_cleanup") = py::capsule(cleanup_callback); + +Both approaches also expose a potentially dangerous ``_cleanup`` attribute in +Python, which may be undesirable from an API standpoint (a premature explicit +call from Python might lead to undefined behavior). Yet another approach that +avoids this issue involves weak reference with a cleanup callback: + +.. code-block:: cpp + + // Register a callback function that is invoked when the BaseClass object is colelcted + py::cpp_function cleanup_callback( + [](py::handle weakref) { + // perform cleanup here -- this function is called with the GIL held + + weakref.dec_ref(); // release weak reference + } + ); + + // Create a weak reference with a cleanup callback and initially leak it + (void) py::weakref(m.attr("BaseClass"), cleanup_callback).release(); + + +Generating documentation using Sphinx +===================================== + +Sphinx [#f4]_ has the ability to inspect the signatures and documentation +strings in pybind11-based extension modules to automatically generate beautiful +documentation in a variety formats. The python_example repository [#f5]_ contains a +simple example repository which uses this approach. + +There are two potential gotchas when using this approach: first, make sure that +the resulting strings do not contain any :kbd:`TAB` characters, which break the +docstring parsing routines. You may want to use C++11 raw string literals, +which are convenient for multi-line comments. Conveniently, any excess +indentation will be automatically be removed by Sphinx. However, for this to +work, it is important that all lines are indented consistently, i.e.: + +.. code-block:: cpp + + // ok + m.def("foo", &foo, R"mydelimiter( + The foo function + + Parameters + ---------- + )mydelimiter"); + + // *not ok* + m.def("foo", &foo, R"mydelimiter(The foo function + + Parameters + ---------- + )mydelimiter"); + +By default, pybind11 automatically generates and prepends a signature to the docstring of a function +registered with ``module::def()`` and ``class_::def()``. Sometimes this +behavior is not desirable, because you want to provide your own signature or remove +the docstring completely to exclude the function from the Sphinx documentation. +The class ``options`` allows you to selectively suppress auto-generated signatures: + +.. code-block:: cpp + + PYBIND11_MODULE(example, m) { + py::options options; + options.disable_function_signatures(); + + m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers"); + } + +Note that changes to the settings affect only function bindings created during the +lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, +the default settings are restored to prevent unwanted side effects. + +.. [#f4] http://www.sphinx-doc.org +.. [#f5] http://github.com/pybind/python_example diff --git a/external/pybind11/docs/advanced/pycpp/index.rst b/external/pybind11/docs/advanced/pycpp/index.rst new file mode 100644 index 0000000..6885bdc --- /dev/null +++ b/external/pybind11/docs/advanced/pycpp/index.rst @@ -0,0 +1,13 @@ +Python C++ interface +#################### + +pybind11 exposes Python types and functions using thin C++ wrappers, which +makes it possible to conveniently call Python code from C++ without resorting +to Python's C API. + +.. toctree:: + :maxdepth: 2 + + object + numpy + utilities diff --git a/external/pybind11/docs/advanced/pycpp/numpy.rst b/external/pybind11/docs/advanced/pycpp/numpy.rst new file mode 100644 index 0000000..98b0c25 --- /dev/null +++ b/external/pybind11/docs/advanced/pycpp/numpy.rst @@ -0,0 +1,366 @@ +.. _numpy: + +NumPy +##### + +Buffer protocol +=============== + +Python supports an extremely general and convenient approach for exchanging +data between plugin libraries. Types can expose a buffer view [#f2]_, which +provides fast direct access to the raw internal data representation. Suppose we +want to bind the following simplistic Matrix class: + +.. code-block:: cpp + + class Matrix { + public: + Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) { + m_data = new float[rows*cols]; + } + float *data() { return m_data; } + size_t rows() const { return m_rows; } + size_t cols() const { return m_cols; } + private: + size_t m_rows, m_cols; + float *m_data; + }; + +The following binding code exposes the ``Matrix`` contents as a buffer object, +making it possible to cast Matrices into NumPy arrays. It is even possible to +completely avoid copy operations with Python expressions like +``np.array(matrix_instance, copy = False)``. + +.. code-block:: cpp + + py::class_(m, "Matrix", py::buffer_protocol()) + .def_buffer([](Matrix &m) -> py::buffer_info { + return py::buffer_info( + m.data(), /* Pointer to buffer */ + sizeof(float), /* Size of one scalar */ + py::format_descriptor::format(), /* Python struct-style format descriptor */ + 2, /* Number of dimensions */ + { m.rows(), m.cols() }, /* Buffer dimensions */ + { sizeof(float) * m.rows(), /* Strides (in bytes) for each index */ + sizeof(float) } + ); + }); + +Supporting the buffer protocol in a new type involves specifying the special +``py::buffer_protocol()`` tag in the ``py::class_`` constructor and calling the +``def_buffer()`` method with a lambda function that creates a +``py::buffer_info`` description record on demand describing a given matrix +instance. The contents of ``py::buffer_info`` mirror the Python buffer protocol +specification. + +.. code-block:: cpp + + struct buffer_info { + void *ptr; + ssize_t itemsize; + std::string format; + ssize_t ndim; + std::vector shape; + std::vector strides; + }; + +To create a C++ function that can take a Python buffer object as an argument, +simply use the type ``py::buffer`` as one of its arguments. Buffers can exist +in a great variety of configurations, hence some safety checks are usually +necessary in the function body. Below, you can see an basic example on how to +define a custom constructor for the Eigen double precision matrix +(``Eigen::MatrixXd``) type, which supports initialization from compatible +buffer objects (e.g. a NumPy matrix). + +.. code-block:: cpp + + /* Bind MatrixXd (or some other Eigen type) to Python */ + typedef Eigen::MatrixXd Matrix; + + typedef Matrix::Scalar Scalar; + constexpr bool rowMajor = Matrix::Flags & Eigen::RowMajorBit; + + py::class_(m, "Matrix", py::buffer_protocol()) + .def("__init__", [](Matrix &m, py::buffer b) { + typedef Eigen::Stride Strides; + + /* Request a buffer descriptor from Python */ + py::buffer_info info = b.request(); + + /* Some sanity checks ... */ + if (info.format != py::format_descriptor::format()) + throw std::runtime_error("Incompatible format: expected a double array!"); + + if (info.ndim != 2) + throw std::runtime_error("Incompatible buffer dimension!"); + + auto strides = Strides( + info.strides[rowMajor ? 0 : 1] / (py::ssize_t)sizeof(Scalar), + info.strides[rowMajor ? 1 : 0] / (py::ssize_t)sizeof(Scalar)); + + auto map = Eigen::Map( + static_cast(info.ptr), info.shape[0], info.shape[1], strides); + + new (&m) Matrix(map); + }); + +For reference, the ``def_buffer()`` call for this Eigen data type should look +as follows: + +.. code-block:: cpp + + .def_buffer([](Matrix &m) -> py::buffer_info { + return py::buffer_info( + m.data(), /* Pointer to buffer */ + sizeof(Scalar), /* Size of one scalar */ + py::format_descriptor::format(), /* Python struct-style format descriptor */ + 2, /* Number of dimensions */ + { m.rows(), m.cols() }, /* Buffer dimensions */ + { sizeof(Scalar) * (rowMajor ? m.cols() : 1), + sizeof(Scalar) * (rowMajor ? 1 : m.rows()) } + /* Strides (in bytes) for each index */ + ); + }) + +For a much easier approach of binding Eigen types (although with some +limitations), refer to the section on :doc:`/advanced/cast/eigen`. + +.. seealso:: + + The file :file:`tests/test_buffers.cpp` contains a complete example + that demonstrates using the buffer protocol with pybind11 in more detail. + +.. [#f2] http://docs.python.org/3/c-api/buffer.html + +Arrays +====== + +By exchanging ``py::buffer`` with ``py::array`` in the above snippet, we can +restrict the function so that it only accepts NumPy arrays (rather than any +type of Python object satisfying the buffer protocol). + +In many situations, we want to define a function which only accepts a NumPy +array of a certain data type. This is possible via the ``py::array_t`` +template. For instance, the following function requires the argument to be a +NumPy array containing double precision values. + +.. code-block:: cpp + + void f(py::array_t array); + +When it is invoked with a different type (e.g. an integer or a list of +integers), the binding code will attempt to cast the input into a NumPy array +of the requested type. Note that this feature requires the +:file:`pybind11/numpy.h` header to be included. + +Data in NumPy arrays is not guaranteed to packed in a dense manner; +furthermore, entries can be separated by arbitrary column and row strides. +Sometimes, it can be useful to require a function to only accept dense arrays +using either the C (row-major) or Fortran (column-major) ordering. This can be +accomplished via a second template argument with values ``py::array::c_style`` +or ``py::array::f_style``. + +.. code-block:: cpp + + void f(py::array_t array); + +The ``py::array::forcecast`` argument is the default value of the second +template parameter, and it ensures that non-conforming arguments are converted +into an array satisfying the specified requirements instead of trying the next +function overload. + +Structured types +================ + +In order for ``py::array_t`` to work with structured (record) types, we first +need to register the memory layout of the type. This can be done via +``PYBIND11_NUMPY_DTYPE`` macro, called in the plugin definition code, which +expects the type followed by field names: + +.. code-block:: cpp + + struct A { + int x; + double y; + }; + + struct B { + int z; + A a; + }; + + // ... + PYBIND11_MODULE(test, m) { + // ... + + PYBIND11_NUMPY_DTYPE(A, x, y); + PYBIND11_NUMPY_DTYPE(B, z, a); + /* now both A and B can be used as template arguments to py::array_t */ + } + +The structure should consist of fundamental arithmetic types, ``std::complex``, +previously registered substructures, and arrays of any of the above. Both C++ +arrays and ``std::array`` are supported. While there is a static assertion to +prevent many types of unsupported structures, it is still the user's +responsibility to use only "plain" structures that can be safely manipulated as +raw memory without violating invariants. + +Vectorizing functions +===================== + +Suppose we want to bind a function with the following signature to Python so +that it can process arbitrary NumPy array arguments (vectors, matrices, general +N-D arrays) in addition to its normal arguments: + +.. code-block:: cpp + + double my_func(int x, float y, double z); + +After including the ``pybind11/numpy.h`` header, this is extremely simple: + +.. code-block:: cpp + + m.def("vectorized_func", py::vectorize(my_func)); + +Invoking the function like below causes 4 calls to be made to ``my_func`` with +each of the array elements. The significant advantage of this compared to +solutions like ``numpy.vectorize()`` is that the loop over the elements runs +entirely on the C++ side and can be crunched down into a tight, optimized loop +by the compiler. The result is returned as a NumPy array of type +``numpy.dtype.float64``. + +.. code-block:: pycon + + >>> x = np.array([[1, 3],[5, 7]]) + >>> y = np.array([[2, 4],[6, 8]]) + >>> z = 3 + >>> result = vectorized_func(x, y, z) + +The scalar argument ``z`` is transparently replicated 4 times. The input +arrays ``x`` and ``y`` are automatically converted into the right types (they +are of type ``numpy.dtype.int64`` but need to be ``numpy.dtype.int32`` and +``numpy.dtype.float32``, respectively). + +.. note:: + + Only arithmetic, complex, and POD types passed by value or by ``const &`` + reference are vectorized; all other arguments are passed through as-is. + Functions taking rvalue reference arguments cannot be vectorized. + +In cases where the computation is too complicated to be reduced to +``vectorize``, it will be necessary to create and access the buffer contents +manually. The following snippet contains a complete example that shows how this +works (the code is somewhat contrived, since it could have been done more +simply using ``vectorize``). + +.. code-block:: cpp + + #include + #include + + namespace py = pybind11; + + py::array_t add_arrays(py::array_t input1, py::array_t input2) { + auto buf1 = input1.request(), buf2 = input2.request(); + + if (buf1.ndim != 1 || buf2.ndim != 1) + throw std::runtime_error("Number of dimensions must be one"); + + if (buf1.size != buf2.size) + throw std::runtime_error("Input shapes must match"); + + /* No pointer is passed, so NumPy will allocate the buffer */ + auto result = py::array_t(buf1.size); + + auto buf3 = result.request(); + + double *ptr1 = (double *) buf1.ptr, + *ptr2 = (double *) buf2.ptr, + *ptr3 = (double *) buf3.ptr; + + for (size_t idx = 0; idx < buf1.shape[0]; idx++) + ptr3[idx] = ptr1[idx] + ptr2[idx]; + + return result; + } + + PYBIND11_MODULE(test, m) { + m.def("add_arrays", &add_arrays, "Add two NumPy arrays"); + } + +.. seealso:: + + The file :file:`tests/test_numpy_vectorize.cpp` contains a complete + example that demonstrates using :func:`vectorize` in more detail. + +Direct access +============= + +For performance reasons, particularly when dealing with very large arrays, it +is often desirable to directly access array elements without internal checking +of dimensions and bounds on every access when indices are known to be already +valid. To avoid such checks, the ``array`` class and ``array_t`` template +class offer an unchecked proxy object that can be used for this unchecked +access through the ``unchecked`` and ``mutable_unchecked`` methods, +where ``N`` gives the required dimensionality of the array: + +.. code-block:: cpp + + m.def("sum_3d", [](py::array_t x) { + auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable + double sum = 0; + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t k = 0; k < r.shape(2); k++) + sum += r(i, j, k); + return sum; + }); + m.def("increment_3d", [](py::array_t x) { + auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t k = 0; k < r.shape(2); k++) + r(i, j, k) += 1.0; + }, py::arg().noconvert()); + +To obtain the proxy from an ``array`` object, you must specify both the data +type and number of dimensions as template arguments, such as ``auto r = +myarray.mutable_unchecked()``. + +If the number of dimensions is not known at compile time, you can omit the +dimensions template parameter (i.e. calling ``arr_t.unchecked()`` or +``arr.unchecked()``. This will give you a proxy object that works in the +same way, but results in less optimizable code and thus a small efficiency +loss in tight loops. + +Note that the returned proxy object directly references the array's data, and +only reads its shape, strides, and writeable flag when constructed. You must +take care to ensure that the referenced array is not destroyed or reshaped for +the duration of the returned object, typically by limiting the scope of the +returned instance. + +The returned proxy object supports some of the same methods as ``py::array`` so +that it can be used as a drop-in replacement for some existing, index-checked +uses of ``py::array``: + +- ``r.ndim()`` returns the number of dimensions + +- ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to + the ``const T`` or ``T`` data, respectively, at the given indices. The + latter is only available to proxies obtained via ``a.mutable_unchecked()``. + +- ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. + +- ``ndim()`` returns the number of dimensions. + +- ``shape(n)`` returns the size of dimension ``n`` + +- ``size()`` returns the total number of elements (i.e. the product of the shapes). + +- ``nbytes()`` returns the number of bytes used by the referenced elements + (i.e. ``itemsize()`` times ``size()``). + +.. seealso:: + + The file :file:`tests/test_numpy_array.cpp` contains additional examples + demonstrating the use of this feature. diff --git a/external/pybind11/docs/advanced/pycpp/object.rst b/external/pybind11/docs/advanced/pycpp/object.rst new file mode 100644 index 0000000..117131e --- /dev/null +++ b/external/pybind11/docs/advanced/pycpp/object.rst @@ -0,0 +1,170 @@ +Python types +############ + +Available wrappers +================== + +All major Python types are available as thin C++ wrapper classes. These +can also be used as function parameters -- see :ref:`python_objects_as_args`. + +Available types include :class:`handle`, :class:`object`, :class:`bool_`, +:class:`int_`, :class:`float_`, :class:`str`, :class:`bytes`, :class:`tuple`, +:class:`list`, :class:`dict`, :class:`slice`, :class:`none`, :class:`capsule`, +:class:`iterable`, :class:`iterator`, :class:`function`, :class:`buffer`, +:class:`array`, and :class:`array_t`. + +Casting back and forth +====================== + +In this kind of mixed code, it is often necessary to convert arbitrary C++ +types to Python, which can be done using :func:`py::cast`: + +.. code-block:: cpp + + MyClass *cls = ..; + py::object obj = py::cast(cls); + +The reverse direction uses the following syntax: + +.. code-block:: cpp + + py::object obj = ...; + MyClass *cls = obj.cast(); + +When conversion fails, both directions throw the exception :class:`cast_error`. + +.. _python_libs: + +Accessing Python libraries from C++ +=================================== + +It is also possible to import objects defined in the Python standard +library or available in the current Python environment (``sys.path``) and work +with these in C++. + +This example obtains a reference to the Python ``Decimal`` class. + +.. code-block:: cpp + + // Equivalent to "from decimal import Decimal" + py::object Decimal = py::module::import("decimal").attr("Decimal"); + +.. code-block:: cpp + + // Try to import scipy + py::object scipy = py::module::import("scipy"); + return scipy.attr("__version__"); + +.. _calling_python_functions: + +Calling Python functions +======================== + +It is also possible to call Python classes, functions and methods +via ``operator()``. + +.. code-block:: cpp + + // Construct a Python object of class Decimal + py::object pi = Decimal("3.14159"); + +.. code-block:: cpp + + // Use Python to make our directories + py::object os = py::module::import("os"); + py::object makedirs = os.attr("makedirs"); + makedirs("/tmp/path/to/somewhere"); + +One can convert the result obtained from Python to a pure C++ version +if a ``py::class_`` or type conversion is defined. + +.. code-block:: cpp + + py::function f = <...>; + py::object result_py = f(1234, "hello", some_instance); + MyClass &result = result_py.cast(); + +.. _calling_python_methods: + +Calling Python methods +======================== + +To call an object's method, one can again use ``.attr`` to obtain access to the +Python method. + +.. code-block:: cpp + + // Calculate e^π in decimal + py::object exp_pi = pi.attr("exp")(); + py::print(py::str(exp_pi)); + +In the example above ``pi.attr("exp")`` is a *bound method*: it will always call +the method for that same instance of the class. Alternately one can create an +*unbound method* via the Python class (instead of instance) and pass the ``self`` +object explicitly, followed by other arguments. + +.. code-block:: cpp + + py::object decimal_exp = Decimal.attr("exp"); + + // Compute the e^n for n=0..4 + for (int n = 0; n < 5; n++) { + py::print(decimal_exp(Decimal(n)); + } + +Keyword arguments +================= + +Keyword arguments are also supported. In Python, there is the usual call syntax: + +.. code-block:: python + + def f(number, say, to): + ... # function code + + f(1234, say="hello", to=some_instance) # keyword call in Python + +In C++, the same call can be made using: + +.. code-block:: cpp + + using namespace pybind11::literals; // to bring in the `_a` literal + f(1234, "say"_a="hello", "to"_a=some_instance); // keyword call in C++ + +Unpacking arguments +=================== + +Unpacking of ``*args`` and ``**kwargs`` is also possible and can be mixed with +other arguments: + +.. code-block:: cpp + + // * unpacking + py::tuple args = py::make_tuple(1234, "hello", some_instance); + f(*args); + + // ** unpacking + py::dict kwargs = py::dict("number"_a=1234, "say"_a="hello", "to"_a=some_instance); + f(**kwargs); + + // mixed keywords, * and ** unpacking + py::tuple args = py::make_tuple(1234); + py::dict kwargs = py::dict("to"_a=some_instance); + f(*args, "say"_a="hello", **kwargs); + +Generalized unpacking according to PEP448_ is also supported: + +.. code-block:: cpp + + py::dict kwargs1 = py::dict("number"_a=1234); + py::dict kwargs2 = py::dict("to"_a=some_instance); + f(**kwargs1, "say"_a="hello", **kwargs2); + +.. seealso:: + + The file :file:`tests/test_pytypes.cpp` contains a complete + example that demonstrates passing native Python types in more detail. The + file :file:`tests/test_callbacks.cpp` presents a few examples of calling + Python functions from C++, including keywords arguments and unpacking. + +.. _PEP448: https://www.python.org/dev/peps/pep-0448/ diff --git a/external/pybind11/docs/advanced/pycpp/utilities.rst b/external/pybind11/docs/advanced/pycpp/utilities.rst new file mode 100644 index 0000000..369e7c9 --- /dev/null +++ b/external/pybind11/docs/advanced/pycpp/utilities.rst @@ -0,0 +1,144 @@ +Utilities +######### + +Using Python's print function in C++ +==================================== + +The usual way to write output in C++ is using ``std::cout`` while in Python one +would use ``print``. Since these methods use different buffers, mixing them can +lead to output order issues. To resolve this, pybind11 modules can use the +:func:`py::print` function which writes to Python's ``sys.stdout`` for consistency. + +Python's ``print`` function is replicated in the C++ API including optional +keyword arguments ``sep``, ``end``, ``file``, ``flush``. Everything works as +expected in Python: + +.. code-block:: cpp + + py::print(1, 2.0, "three"); // 1 2.0 three + py::print(1, 2.0, "three", "sep"_a="-"); // 1-2.0-three + + auto args = py::make_tuple("unpacked", true); + py::print("->", *args, "end"_a="<-"); // -> unpacked True <- + +.. _ostream_redirect: + +Capturing standard output from ostream +====================================== + +Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print, +but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr`` +redirection. Replacing a library's printing with `py::print ` may not +be feasible. This can be fixed using a guard around the library function that +redirects output to the corresponding Python streams: + +.. code-block:: cpp + + #include + + ... + + // Add a scoped redirect for your noisy code + m.def("noisy_func", []() { + py::scoped_ostream_redirect stream( + std::cout, // std::ostream& + py::module::import("sys").attr("stdout") // Python output + ); + call_noisy_func(); + }); + +This method respects flushes on the output streams and will flush if needed +when the scoped guard is destroyed. This allows the output to be redirected in +real time, such as to a Jupyter notebook. The two arguments, the C++ stream and +the Python output, are optional, and default to standard output if not given. An +extra type, `py::scoped_estream_redirect `, is identical +except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with +`py::call_guard`, which allows multiple items, but uses the default constructor: + +.. code-block:: py + + // Alternative: Call single function using call guard + m.def("noisy_func", &call_noisy_function, + py::call_guard()); + +The redirection can also be done in Python with the addition of a context +manager, using the `py::add_ostream_redirect() ` function: + +.. code-block:: cpp + + py::add_ostream_redirect(m, "ostream_redirect"); + +The name in Python defaults to ``ostream_redirect`` if no name is passed. This +creates the following context manager in Python: + +.. code-block:: python + + with ostream_redirect(stdout=True, stderr=True): + noisy_function() + +It defaults to redirecting both streams, though you can use the keyword +arguments to disable one of the streams if needed. + +.. note:: + + The above methods will not redirect C-level output to file descriptors, such + as ``fprintf``. For those cases, you'll need to redirect the file + descriptors either directly in C or with Python's ``os.dup2`` function + in an operating-system dependent way. + +.. _eval: + +Evaluating Python expressions from strings and files +==================================================== + +pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate +Python expressions and statements. The following example illustrates how they +can be used. + +.. code-block:: cpp + + // At beginning of file + #include + + ... + + // Evaluate in scope of main module + py::object scope = py::module::import("__main__").attr("__dict__"); + + // Evaluate an isolated expression + int result = py::eval("my_variable + 10", scope).cast(); + + // Evaluate a sequence of statements + py::exec( + "print('Hello')\n" + "print('world!');", + scope); + + // Evaluate the statements in an separate Python file on disk + py::eval_file("script.py", scope); + +C++11 raw string literals are also supported and quite handy for this purpose. +The only requirement is that the first statement must be on a new line following +the raw string delimiter ``R"(``, ensuring all lines have common leading indent: + +.. code-block:: cpp + + py::exec(R"( + x = get_answer() + if x == 42: + print('Hello World!') + else: + print('Bye!') + )", scope + ); + +.. note:: + + `eval` and `eval_file` accept a template parameter that describes how the + string/file should be interpreted. Possible choices include ``eval_expr`` + (isolated expression), ``eval_single_statement`` (a single statement, return + value is always ``none``), and ``eval_statements`` (sequence of statements, + return value is always ``none``). `eval` defaults to ``eval_expr``, + `eval_file` defaults to ``eval_statements`` and `exec` is just a shortcut + for ``eval``. diff --git a/external/pybind11/docs/advanced/smart_ptrs.rst b/external/pybind11/docs/advanced/smart_ptrs.rst new file mode 100644 index 0000000..da57748 --- /dev/null +++ b/external/pybind11/docs/advanced/smart_ptrs.rst @@ -0,0 +1,173 @@ +Smart pointers +############## + +std::unique_ptr +=============== + +Given a class ``Example`` with Python bindings, it's possible to return +instances wrapped in C++11 unique pointers, like so + +.. code-block:: cpp + + std::unique_ptr create_example() { return std::unique_ptr(new Example()); } + +.. code-block:: cpp + + m.def("create_example", &create_example); + +In other words, there is nothing special that needs to be done. While returning +unique pointers in this way is allowed, it is *illegal* to use them as function +arguments. For instance, the following function signature cannot be processed +by pybind11. + +.. code-block:: cpp + + void do_something_with_example(std::unique_ptr ex) { ... } + +The above signature would imply that Python needs to give up ownership of an +object that is passed to this function, which is generally not possible (for +instance, the object might be referenced elsewhere). + +std::shared_ptr +=============== + +The binding generator for classes, :class:`class_`, can be passed a template +type that denotes a special *holder* type that is used to manage references to +the object. If no such holder type template argument is given, the default for +a type named ``Type`` is ``std::unique_ptr``, which means that the object +is deallocated when Python's reference count goes to zero. + +It is possible to switch to other types of reference counting wrappers or smart +pointers, which is useful in codebases that rely on them. For instance, the +following snippet causes ``std::shared_ptr`` to be used instead. + +.. code-block:: cpp + + py::class_ /* <- holder type */> obj(m, "Example"); + +Note that any particular class can only be associated with a single holder type. + +One potential stumbling block when using holder types is that they need to be +applied consistently. Can you guess what's broken about the following binding +code? + +.. code-block:: cpp + + class Child { }; + + class Parent { + public: + Parent() : child(std::make_shared()) { } + Child *get_child() { return child.get(); } /* Hint: ** DON'T DO THIS ** */ + private: + std::shared_ptr child; + }; + + PYBIND11_MODULE(example, m) { + py::class_>(m, "Child"); + + py::class_>(m, "Parent") + .def(py::init<>()) + .def("get_child", &Parent::get_child); + } + +The following Python code will cause undefined behavior (and likely a +segmentation fault). + +.. code-block:: python + + from example import Parent + print(Parent().get_child()) + +The problem is that ``Parent::get_child()`` returns a pointer to an instance of +``Child``, but the fact that this instance is already managed by +``std::shared_ptr<...>`` is lost when passing raw pointers. In this case, +pybind11 will create a second independent ``std::shared_ptr<...>`` that also +claims ownership of the pointer. In the end, the object will be freed **twice** +since these shared pointers have no way of knowing about each other. + +There are two ways to resolve this issue: + +1. For types that are managed by a smart pointer class, never use raw pointers + in function arguments or return values. In other words: always consistently + wrap pointers into their designated holder types (such as + ``std::shared_ptr<...>``). In this case, the signature of ``get_child()`` + should be modified as follows: + +.. code-block:: cpp + + std::shared_ptr get_child() { return child; } + +2. Adjust the definition of ``Child`` by specifying + ``std::enable_shared_from_this`` (see cppreference_ for details) as a + base class. This adds a small bit of information to ``Child`` that allows + pybind11 to realize that there is already an existing + ``std::shared_ptr<...>`` and communicate with it. In this case, the + declaration of ``Child`` should look as follows: + +.. _cppreference: http://en.cppreference.com/w/cpp/memory/enable_shared_from_this + +.. code-block:: cpp + + class Child : public std::enable_shared_from_this { }; + +.. _smart_pointers: + +Custom smart pointers +===================== + +pybind11 supports ``std::unique_ptr`` and ``std::shared_ptr`` right out of the +box. For any other custom smart pointer, transparent conversions can be enabled +using a macro invocation similar to the following. It must be declared at the +top namespace level before any binding code: + +.. code-block:: cpp + + PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr); + +The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a +placeholder name that is used as a template parameter of the second argument. +Thus, feel free to use any identifier, but use it consistently on both sides; +also, don't use the name of a type that already exists in your codebase. + +The macro also accepts a third optional boolean parameter that is set to false +by default. Specify + +.. code-block:: cpp + + PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr, true); + +if ``SmartPtr`` can always be initialized from a ``T*`` pointer without the +risk of inconsistencies (such as multiple independent ``SmartPtr`` instances +believing that they are the sole owner of the ``T*`` pointer). A common +situation where ``true`` should be passed is when the ``T`` instances use +*intrusive* reference counting. + +Please take a look at the :ref:`macro_notes` before using this feature. + +By default, pybind11 assumes that your custom smart pointer has a standard +interface, i.e. provides a ``.get()`` member function to access the underlying +raw pointer. If this is not the case, pybind11's ``holder_helper`` must be +specialized: + +.. code-block:: cpp + + // Always needed for custom holder types + PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr); + + // Only needed if the type's `.get()` goes by another name + namespace pybind11 { namespace detail { + template + struct holder_helper> { // <-- specialization + static const T *get(const SmartPtr &p) { return p.getPointer(); } + }; + }} + +The above specialization informs pybind11 that the custom ``SmartPtr`` class +provides ``.get()`` functionality via ``.getPointer()``. + +.. seealso:: + + The file :file:`tests/test_smart_ptr.cpp` contains a complete example + that demonstrates how to work with custom reference-counting holder types + in more detail. diff --git a/external/pybind11/docs/basics.rst b/external/pybind11/docs/basics.rst new file mode 100644 index 0000000..447250e --- /dev/null +++ b/external/pybind11/docs/basics.rst @@ -0,0 +1,293 @@ +.. _basics: + +First steps +########### + +This sections demonstrates the basic features of pybind11. Before getting +started, make sure that development environment is set up to compile the +included set of test cases. + + +Compiling the test cases +======================== + +Linux/MacOS +----------- + +On Linux you'll need to install the **python-dev** or **python3-dev** packages as +well as **cmake**. On Mac OS, the included python version works out of the box, +but **cmake** must still be installed. + +After installing the prerequisites, run + +.. code-block:: bash + + mkdir build + cd build + cmake .. + make check -j 4 + +The last line will both compile and run the tests. + +Windows +------- + +On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies +on various C++11 language features that break older versions of Visual Studio. + +To compile and run the tests: + +.. code-block:: batch + + mkdir build + cd build + cmake .. + cmake --build . --config Release --target check + +This will create a Visual Studio project, compile and run the target, all from the +command line. + +.. Note:: + + If all tests fail, make sure that the Python binary and the testcases are compiled + for the same processor type and bitness (i.e. either **i386** or **x86_64**). You + can specify **x86_64** as the target architecture for the generated Visual Studio + project using ``cmake -A x64 ..``. + +.. seealso:: + + Advanced users who are already familiar with Boost.Python may want to skip + the tutorial and look at the test cases in the :file:`tests` directory, + which exercise all features of pybind11. + +Header and namespace conventions +================================ + +For brevity, all code examples assume that the following two lines are present: + +.. code-block:: cpp + + #include + + namespace py = pybind11; + +Some features may require additional headers, but those will be specified as needed. + +.. _simple_example: + +Creating bindings for a simple function +======================================= + +Let's start by creating Python bindings for an extremely simple function, which +adds two numbers and returns their result: + +.. code-block:: cpp + + int add(int i, int j) { + return i + j; + } + +For simplicity [#f1]_, we'll put both this function and the binding code into +a file named :file:`example.cpp` with the following contents: + +.. code-block:: cpp + + #include + + int add(int i, int j) { + return i + j; + } + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example plugin"; // optional module docstring + + m.def("add", &add, "A function which adds two numbers"); + } + +.. [#f1] In practice, implementation and binding code will generally be located + in separate files. + +The :func:`PYBIND11_MODULE` macro creates a function that will be called when an +``import`` statement is issued from within Python. The module name (``example``) +is given as the first macro argument (it should not be in quotes). The second +argument (``m``) defines a variable of type :class:`py::module ` which +is the main interface for creating bindings. The method :func:`module::def` +generates binding code that exposes the ``add()`` function to Python. + +.. note:: + + Notice how little code was needed to expose our function to Python: all + details regarding the function's parameters and return value were + automatically inferred using template metaprogramming. This overall + approach and the used syntax are borrowed from Boost.Python, though the + underlying implementation is very different. + +pybind11 is a header-only library, hence it is not necessary to link against +any special libraries and there are no intermediate (magic) translation steps. +On Linux, the above example can be compiled using the following command: + +.. code-block:: bash + + $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + +For more details on the required compiler flags on Linux and MacOS, see +:ref:`building_manually`. For complete cross-platform compilation instructions, +refer to the :ref:`compiling` page. + +The `python_example`_ and `cmake_example`_ repositories are also a good place +to start. They are both complete project examples with cross-platform build +systems. The only difference between the two is that `python_example`_ uses +Python's ``setuptools`` to build the module, while `cmake_example`_ uses CMake +(which may be preferable for existing C++ projects). + +.. _python_example: https://github.com/pybind/python_example +.. _cmake_example: https://github.com/pybind/cmake_example + +Building the above C++ code will produce a binary module file that can be +imported to Python. Assuming that the compiled module is located in the +current directory, the following interactive Python session shows how to +load and execute the example: + +.. code-block:: pycon + + $ python + Python 2.7.10 (default, Aug 22 2015, 20:33:39) + [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin + Type "help", "copyright", "credits" or "license" for more information. + >>> import example + >>> example.add(1, 2) + 3L + >>> + +.. _keyword_args: + +Keyword arguments +================= + +With a simple modification code, it is possible to inform Python about the +names of the arguments ("i" and "j" in this case). + +.. code-block:: cpp + + m.def("add", &add, "A function which adds two numbers", + py::arg("i"), py::arg("j")); + +:class:`arg` is one of several special tag classes which can be used to pass +metadata into :func:`module::def`. With this modified binding code, we can now +call the function using keyword arguments, which is a more readable alternative +particularly for functions taking many parameters: + +.. code-block:: pycon + + >>> import example + >>> example.add(i=1, j=2) + 3L + +The keyword names also appear in the function signatures within the documentation. + +.. code-block:: pycon + + >>> help(example) + + .... + + FUNCTIONS + add(...) + Signature : (i: int, j: int) -> int + + A function which adds two numbers + +A shorter notation for named arguments is also available: + +.. code-block:: cpp + + // regular notation + m.def("add1", &add, py::arg("i"), py::arg("j")); + // shorthand + using namespace pybind11::literals; + m.def("add2", &add, "i"_a, "j"_a); + +The :var:`_a` suffix forms a C++11 literal which is equivalent to :class:`arg`. +Note that the literal operator must first be made visible with the directive +``using namespace pybind11::literals``. This does not bring in anything else +from the ``pybind11`` namespace except for literals. + +.. _default_args: + +Default arguments +================= + +Suppose now that the function to be bound has default arguments, e.g.: + +.. code-block:: cpp + + int add(int i = 1, int j = 2) { + return i + j; + } + +Unfortunately, pybind11 cannot automatically extract these parameters, since they +are not part of the function's type information. However, they are simple to specify +using an extension of :class:`arg`: + +.. code-block:: cpp + + m.def("add", &add, "A function which adds two numbers", + py::arg("i") = 1, py::arg("j") = 2); + +The default values also appear within the documentation. + +.. code-block:: pycon + + >>> help(example) + + .... + + FUNCTIONS + add(...) + Signature : (i: int = 1, j: int = 2) -> int + + A function which adds two numbers + +The shorthand notation is also available for default arguments: + +.. code-block:: cpp + + // regular notation + m.def("add1", &add, py::arg("i") = 1, py::arg("j") = 2); + // shorthand + m.def("add2", &add, "i"_a=1, "j"_a=2); + +Exporting variables +=================== + +To expose a value from C++, use the ``attr`` function to register it in a +module as shown below. Built-in types and general objects (more on that later) +are automatically converted when assigned as attributes, and can be explicitly +converted using the function ``py::cast``. + +.. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.attr("the_answer") = 42; + py::object world = py::cast("World"); + m.attr("what") = world; + } + +These are then accessible from Python: + +.. code-block:: pycon + + >>> import example + >>> example.the_answer + 42 + >>> example.what + 'World' + +.. _supported_types: + +Supported data types +==================== + +A large number of data types are supported out of the box and can be used +seamlessly as functions arguments, return values or with ``py::cast`` in general. +For a full overview, see the :doc:`advanced/cast/index` section. diff --git a/external/pybind11/docs/benchmark.py b/external/pybind11/docs/benchmark.py new file mode 100644 index 0000000..6dc0604 --- /dev/null +++ b/external/pybind11/docs/benchmark.py @@ -0,0 +1,88 @@ +import random +import os +import time +import datetime as dt + +nfns = 4 # Functions per class +nargs = 4 # Arguments per function + + +def generate_dummy_code_pybind11(nclasses=10): + decl = "" + bindings = "" + + for cl in range(nclasses): + decl += "class cl%03i;\n" % cl + decl += '\n' + + for cl in range(nclasses): + decl += "class cl%03i {\n" % cl + decl += "public:\n" + bindings += ' py::class_(m, "cl%03i")\n' % (cl, cl) + for fn in range(nfns): + ret = random.randint(0, nclasses - 1) + params = [random.randint(0, nclasses - 1) for i in range(nargs)] + decl += " cl%03i *fn_%03i(" % (ret, fn) + decl += ", ".join("cl%03i *" % p for p in params) + decl += ");\n" + bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % \ + (fn, cl, fn) + decl += "};\n\n" + bindings += ' ;\n' + + result = "#include \n\n" + result += "namespace py = pybind11;\n\n" + result += decl + '\n' + result += "PYBIND11_MODULE(example, m) {\n" + result += bindings + result += "}" + return result + + +def generate_dummy_code_boost(nclasses=10): + decl = "" + bindings = "" + + for cl in range(nclasses): + decl += "class cl%03i;\n" % cl + decl += '\n' + + for cl in range(nclasses): + decl += "class cl%03i {\n" % cl + decl += "public:\n" + bindings += ' py::class_("cl%03i")\n' % (cl, cl) + for fn in range(nfns): + ret = random.randint(0, nclasses - 1) + params = [random.randint(0, nclasses - 1) for i in range(nargs)] + decl += " cl%03i *fn_%03i(" % (ret, fn) + decl += ", ".join("cl%03i *" % p for p in params) + decl += ");\n" + bindings += ' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy())\n' % \ + (fn, cl, fn) + decl += "};\n\n" + bindings += ' ;\n' + + result = "#include \n\n" + result += "namespace py = boost::python;\n\n" + result += decl + '\n' + result += "BOOST_PYTHON_MODULE(example) {\n" + result += bindings + result += "}" + return result + + +for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: + print ("{") + for i in range(0, 10): + nclasses = 2 ** i + with open("test.cpp", "w") as f: + f.write(codegen(nclasses)) + n1 = dt.datetime.now() + os.system("g++ -Os -shared -rdynamic -undefined dynamic_lookup " + "-fvisibility=hidden -std=c++14 test.cpp -I include " + "-I /System/Library/Frameworks/Python.framework/Headers -o test.so") + n2 = dt.datetime.now() + elapsed = (n2 - n1).total_seconds() + size = os.stat('test.so').st_size + print(" {%i, %f, %i}," % (nclasses * nfns, elapsed, size)) + print ("}") diff --git a/external/pybind11/docs/benchmark.rst b/external/pybind11/docs/benchmark.rst new file mode 100644 index 0000000..59d533d --- /dev/null +++ b/external/pybind11/docs/benchmark.rst @@ -0,0 +1,97 @@ +Benchmark +========= + +The following is the result of a synthetic benchmark comparing both compilation +time and module size of pybind11 against Boost.Python. A detailed report about a +Boost.Python to pybind11 conversion of a real project is available here: [#f1]_. + +.. [#f1] http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf + +Setup +----- + +A python script (see the ``docs/benchmark.py`` file) was used to generate a set +of files with dummy classes whose count increases for each successive benchmark +(between 1 and 2048 classes in powers of two). Each class has four methods with +a randomly generated signature with a return value and four arguments. (There +was no particular reason for this setup other than the desire to generate many +unique function signatures whose count could be controlled in a simple way.) + +Here is an example of the binding code for one class: + +.. code-block:: cpp + + ... + class cl034 { + public: + cl279 *fn_000(cl084 *, cl057 *, cl065 *, cl042 *); + cl025 *fn_001(cl098 *, cl262 *, cl414 *, cl121 *); + cl085 *fn_002(cl445 *, cl297 *, cl145 *, cl421 *); + cl470 *fn_003(cl200 *, cl323 *, cl332 *, cl492 *); + }; + ... + + PYBIND11_MODULE(example, m) { + ... + py::class_(m, "cl034") + .def("fn_000", &cl034::fn_000) + .def("fn_001", &cl034::fn_001) + .def("fn_002", &cl034::fn_002) + .def("fn_003", &cl034::fn_003) + ... + } + +The Boost.Python version looks almost identical except that a return value +policy had to be specified as an argument to ``def()``. For both libraries, +compilation was done with + +.. code-block:: bash + + Apple LLVM version 7.0.2 (clang-700.1.81) + +and the following compilation flags + +.. code-block:: bash + + g++ -Os -shared -rdynamic -undefined dynamic_lookup -fvisibility=hidden -std=c++14 + +Compilation time +---------------- + +The following log-log plot shows how the compilation time grows for an +increasing number of class and function declarations. pybind11 includes many +fewer headers, which initially leads to shorter compilation times, but the +performance is ultimately fairly similar (pybind11 is 19.8 seconds faster for +the largest largest file with 2048 classes and a total of 8192 methods -- a +modest **1.2x** speedup relative to Boost.Python, which required 116.35 +seconds). + +.. only:: not latex + + .. image:: pybind11_vs_boost_python1.svg + +.. only:: latex + + .. image:: pybind11_vs_boost_python1.png + +Module size +----------- + +Differences between the two libraries become much more pronounced when +considering the file size of the generated Python plugin: for the largest file, +the binary generated by Boost.Python required 16.8 MiB, which was **2.17 +times** / **9.1 megabytes** larger than the output generated by pybind11. For +very small inputs, Boost.Python has an edge in the plot below -- however, note +that it stores many definitions in an external library, whose size was not +included here, hence the comparison is slightly shifted in Boost.Python's +favor. + +.. only:: not latex + + .. image:: pybind11_vs_boost_python2.svg + +.. only:: latex + + .. image:: pybind11_vs_boost_python2.png + + diff --git a/external/pybind11/docs/changelog.rst b/external/pybind11/docs/changelog.rst new file mode 100644 index 0000000..1ca501d --- /dev/null +++ b/external/pybind11/docs/changelog.rst @@ -0,0 +1,927 @@ +.. _changelog: + +Changelog +######### + +Starting with version 1.8.0, pybind11 releases use a `semantic versioning +`_ policy. + +v2.3.0 (Not yet released) +----------------------------------------------------- + +* TBD + +v2.2.1 (September 14, 2017) +----------------------------------------------------- + +* Added ``py::module::reload()`` member function for reloading a module. + `#1040 `_. + +* Fixed a reference leak in the number converter. + `#1078 `_. + +* Fixed compilation with Clang on host GCC < 5 (old libstdc++ which isn't fully + C++11 compliant). `#1062 `_. + +* Fixed a regression where the automatic ``std::vector`` caster would + fail to compile. The same fix also applies to any container which returns + element proxies instead of references. + `#1053 `_. + +* Fixed a regression where the ``py::keep_alive`` policy could not be applied + to constructors. `#1065 `_. + +* Fixed a nullptr dereference when loading a ``py::module_local`` type + that's only registered in an external module. + `#1058 `_. + +* Fixed implicit conversion of accessors to types derived from ``py::object``. + `#1076 `_. + +* The ``name`` in ``PYBIND11_MODULE(name, variable)`` can now be a macro. + `#1082 `_. + +* Relaxed overly strict ``py::pickle()`` check for matching get and set types. + `#1064 `_. + +* Conversion errors now try to be more informative when it's likely that + a missing header is the cause (e.g. forgetting ````). + `#1077 `_. + +v2.2.0 (August 31, 2017) +----------------------------------------------------- + +* Support for embedding the Python interpreter. See the + :doc:`documentation page ` for a + full overview of the new features. + `#774 `_, + `#889 `_, + `#892 `_, + `#920 `_. + + .. code-block:: cpp + + #include + namespace py = pybind11; + + int main() { + py::scoped_interpreter guard{}; // start the interpreter and keep it alive + + py::print("Hello, World!"); // use the Python API + } + +* Support for inheriting from multiple C++ bases in Python. + `#693 `_. + + .. code-block:: python + + from cpp_module import CppBase1, CppBase2 + + class PyDerived(CppBase1, CppBase2): + def __init__(self): + CppBase1.__init__(self) # C++ bases must be initialized explicitly + CppBase2.__init__(self) + +* ``PYBIND11_MODULE`` is now the preferred way to create module entry points. + ``PYBIND11_PLUGIN`` is deprecated. See :ref:`macros` for details. + `#879 `_. + + .. code-block:: cpp + + // new + PYBIND11_MODULE(example, m) { + m.def("add", [](int a, int b) { return a + b; }); + } + + // old + PYBIND11_PLUGIN(example) { + py::module m("example"); + m.def("add", [](int a, int b) { return a + b; }); + return m.ptr(); + } + +* pybind11's headers and build system now more strictly enforce hidden symbol + visibility for extension modules. This should be seamless for most users, + but see the :doc:`upgrade` if you use a custom build system. + `#995 `_. + +* Support for ``py::module_local`` types which allow multiple modules to + export the same C++ types without conflicts. This is useful for opaque + types like ``std::vector``. ``py::bind_vector`` and ``py::bind_map`` + now default to ``py::module_local`` if their elements are builtins or + local types. See :ref:`module_local` for details. + `#949 `_, + `#981 `_, + `#995 `_, + `#997 `_. + +* Custom constructors can now be added very easily using lambdas or factory + functions which return a class instance by value, pointer or holder. This + supersedes the old placement-new ``__init__`` technique. + See :ref:`custom_constructors` for details. + `#805 `_, + `#1014 `_. + + .. code-block:: cpp + + struct Example { + Example(std::string); + }; + + py::class_(m, "Example") + .def(py::init()) // existing constructor + .def(py::init([](int n) { // custom constructor + return std::make_unique(std::to_string(n)); + })); + +* Similarly to custom constructors, pickling support functions are now bound + using the ``py::pickle()`` adaptor which improves type safety. See the + :doc:`upgrade` and :ref:`pickling` for details. + `#1038 `_. + +* Builtin support for converting C++17 standard library types and general + conversion improvements: + + 1. C++17 ``std::variant`` is supported right out of the box. C++11/14 + equivalents (e.g. ``boost::variant``) can also be added with a simple + user-defined specialization. See :ref:`cpp17_container_casters` for details. + `#811 `_, + `#845 `_, + `#989 `_. + + 2. Out-of-the-box support for C++17 ``std::string_view``. + `#906 `_. + + 3. Improved compatibility of the builtin ``optional`` converter. + `#874 `_. + + 4. The ``bool`` converter now accepts ``numpy.bool_`` and types which + define ``__bool__`` (Python 3.x) or ``__nonzero__`` (Python 2.7). + `#925 `_. + + 5. C++-to-Python casters are now more efficient and move elements out + of rvalue containers whenever possible. + `#851 `_, + `#936 `_, + `#938 `_. + + 6. Fixed ``bytes`` to ``std::string/char*`` conversion on Python 3. + `#817 `_. + + 7. Fixed lifetime of temporary C++ objects created in Python-to-C++ conversions. + `#924 `_. + +* Scope guard call policy for RAII types, e.g. ``py::call_guard()``, + ``py::call_guard()``. See :ref:`call_policies` for details. + `#740 `_. + +* Utility for redirecting C++ streams to Python (e.g. ``std::cout`` -> + ``sys.stdout``). Scope guard ``py::scoped_ostream_redirect`` in C++ and + a context manager in Python. See :ref:`ostream_redirect`. + `#1009 `_. + +* Improved handling of types and exceptions across module boundaries. + `#915 `_, + `#951 `_, + `#995 `_. + +* Fixed destruction order of ``py::keep_alive`` nurse/patient objects + in reference cycles. + `#856 `_. + +* Numpy and buffer protocol related improvements: + + 1. Support for negative strides in Python buffer objects/numpy arrays. This + required changing integers from unsigned to signed for the related C++ APIs. + Note: If you have compiler warnings enabled, you may notice some new conversion + warnings after upgrading. These can be resolved with ``static_cast``. + `#782 `_. + + 2. Support ``std::complex`` and arrays inside ``PYBIND11_NUMPY_DTYPE``. + `#831 `_, + `#832 `_. + + 3. Support for constructing ``py::buffer_info`` and ``py::arrays`` using + arbitrary containers or iterators instead of requiring a ``std::vector``. + `#788 `_, + `#822 `_, + `#860 `_. + + 4. Explicitly check numpy version and require >= 1.7.0. + `#819 `_. + +* Support for allowing/prohibiting ``None`` for specific arguments and improved + ``None`` overload resolution order. See :ref:`none_arguments` for details. + `#843 `_. + `#859 `_. + +* Added ``py::exec()`` as a shortcut for ``py::eval()`` + and support for C++11 raw string literals as input. See :ref:`eval`. + `#766 `_, + `#827 `_. + +* ``py::vectorize()`` ignores non-vectorizable arguments and supports + member functions. + `#762 `_. + +* Support for bound methods as callbacks (``pybind11/functional.h``). + `#815 `_. + +* Allow aliasing pybind11 methods: ``cls.attr("foo") = cls.attr("bar")``. + `#802 `_. + +* Don't allow mixed static/non-static overloads. + `#804 `_. + +* Fixed overriding static properties in derived classes. + `#784 `_. + +* Improved deduction of member functions of a derived class when its bases + aren't registered with pybind11. + `#855 `_. + + .. code-block:: cpp + + struct Base { + int foo() { return 42; } + } + + struct Derived : Base {} + + // Now works, but previously required also binding `Base` + py::class_(m, "Derived") + .def("foo", &Derived::foo); // function is actually from `Base` + +* The implementation of ``py::init<>`` now uses C++11 brace initialization + syntax to construct instances, which permits binding implicit constructors of + aggregate types. `#1015 `_. + + .. code-block:: cpp + + struct Aggregate { + int a; + std::string b; + }; + + py::class_(m, "Aggregate") + .def(py::init()); + +* Fixed issues with multiple inheritance with offset base/derived pointers. + `#812 `_, + `#866 `_, + `#960 `_. + +* Fixed reference leak of type objects. + `#1030 `_. + +* Improved support for the ``/std:c++14`` and ``/std:c++latest`` modes + on MSVC 2017. + `#841 `_, + `#999 `_. + +* Fixed detection of private operator new on MSVC. + `#893 `_, + `#918 `_. + +* Intel C++ compiler compatibility fixes. + `#937 `_. + +* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7. + `#821 `_. + +* Added ``py::hash`` to fetch the hash value of Python objects, and + ``.def(hash(py::self))`` to provide the C++ ``std::hash`` as the Python + ``__hash__`` method. + `#1034 `_. + +* Fixed ``__truediv__`` on Python 2 and ``__itruediv__`` on Python 3. + `#867 `_. + +* ``py::capsule`` objects now support the ``name`` attribute. This is useful + for interfacing with ``scipy.LowLevelCallable``. + `#902 `_. + +* Fixed ``py::make_iterator``'s ``__next__()`` for past-the-end calls. + `#897 `_. + +* Added ``error_already_set::matches()`` for checking Python exceptions. + `#772 `_. + +* Deprecated ``py::error_already_set::clear()``. It's no longer needed + following a simplification of the ``py::error_already_set`` class. + `#954 `_. + +* Deprecated ``py::handle::operator==()`` in favor of ``py::handle::is()`` + `#825 `_. + +* Deprecated ``py::object::borrowed``/``py::object::stolen``. + Use ``py::object::borrowed_t{}``/``py::object::stolen_t{}`` instead. + `#771 `_. + +* Changed internal data structure versioning to avoid conflicts between + modules compiled with different revisions of pybind11. + `#1012 `_. + +* Additional compile-time and run-time error checking and more informative messages. + `#786 `_, + `#794 `_, + `#803 `_. + +* Various minor improvements and fixes. + `#764 `_, + `#791 `_, + `#795 `_, + `#840 `_, + `#844 `_, + `#846 `_, + `#849 `_, + `#858 `_, + `#862 `_, + `#871 `_, + `#872 `_, + `#881 `_, + `#888 `_, + `#899 `_, + `#928 `_, + `#931 `_, + `#944 `_, + `#950 `_, + `#952 `_, + `#962 `_, + `#965 `_, + `#970 `_, + `#978 `_, + `#979 `_, + `#986 `_, + `#1020 `_, + `#1027 `_, + `#1037 `_. + +* Testing improvements. + `#798 `_, + `#882 `_, + `#898 `_, + `#900 `_, + `#921 `_, + `#923 `_, + `#963 `_. + +v2.1.1 (April 7, 2017) +----------------------------------------------------- + +* Fixed minimum version requirement for MSVC 2015u3 + `#773 `_. + +v2.1.0 (March 22, 2017) +----------------------------------------------------- + +* pybind11 now performs function overload resolution in two phases. The first + phase only considers exact type matches, while the second allows for implicit + conversions to take place. A special ``noconvert()`` syntax can be used to + completely disable implicit conversions for specific arguments. + `#643 `_, + `#634 `_, + `#650 `_. + +* Fixed a regression where static properties no longer worked with classes + using multiple inheritance. The ``py::metaclass`` attribute is no longer + necessary (and deprecated as of this release) when binding classes with + static properties. + `#679 `_, + +* Classes bound using ``pybind11`` can now use custom metaclasses. + `#679 `_, + +* ``py::args`` and ``py::kwargs`` can now be mixed with other positional + arguments when binding functions using pybind11. + `#611 `_. + +* Improved support for C++11 unicode string and character types; added + extensive documentation regarding pybind11's string conversion behavior. + `#624 `_, + `#636 `_, + `#715 `_. + +* pybind11 can now avoid expensive copies when converting Eigen arrays to NumPy + arrays (and vice versa). `#610 `_. + +* The "fast path" in ``py::vectorize`` now works for any full-size group of C or + F-contiguous arrays. The non-fast path is also faster since it no longer performs + copies of the input arguments (except when type conversions are necessary). + `#610 `_. + +* Added fast, unchecked access to NumPy arrays via a proxy object. + `#746 `_. + +* Transparent support for class-specific ``operator new`` and + ``operator delete`` implementations. + `#755 `_. + +* Slimmer and more efficient STL-compatible iterator interface for sequence types. + `#662 `_. + +* Improved custom holder type support. + `#607 `_. + +* ``nullptr`` to ``None`` conversion fixed in various builtin type casters. + `#732 `_. + +* ``enum_`` now exposes its members via a special ``__members__`` attribute. + `#666 `_. + +* ``std::vector`` bindings created using ``stl_bind.h`` can now optionally + implement the buffer protocol. `#488 `_. + +* Automated C++ reference documentation using doxygen and breathe. + `#598 `_. + +* Added minimum compiler version assertions. + `#727 `_. + +* Improved compatibility with C++1z. + `#677 `_. + +* Improved ``py::capsule`` API. Can be used to implement cleanup + callbacks that are involved at module destruction time. + `#752 `_. + +* Various minor improvements and fixes. + `#595 `_, + `#588 `_, + `#589 `_, + `#603 `_, + `#619 `_, + `#648 `_, + `#695 `_, + `#720 `_, + `#723 `_, + `#729 `_, + `#724 `_, + `#742 `_, + `#753 `_. + +v2.0.1 (Jan 4, 2017) +----------------------------------------------------- + +* Fix pointer to reference error in type_caster on MSVC + `#583 `_. + +* Fixed a segmentation in the test suite due to a typo + `cd7eac `_. + +v2.0.0 (Jan 1, 2017) +----------------------------------------------------- + +* Fixed a reference counting regression affecting types with custom metaclasses + (introduced in v2.0.0-rc1). + `#571 `_. + +* Quenched a CMake policy warning. + `#570 `_. + +v2.0.0-rc1 (Dec 23, 2016) +----------------------------------------------------- + +The pybind11 developers are excited to issue a release candidate of pybind11 +with a subsequent v2.0.0 release planned in early January next year. + +An incredible amount of effort by went into pybind11 over the last ~5 months, +leading to a release that is jam-packed with exciting new features and numerous +usability improvements. The following list links PRs or individual commits +whenever applicable. + +Happy Christmas! + +* Support for binding C++ class hierarchies that make use of multiple + inheritance. `#410 `_. + +* PyPy support: pybind11 now supports nightly builds of PyPy and will + interoperate with the future 5.7 release. No code changes are necessary, + everything "just" works as usual. Note that we only target the Python 2.7 + branch for now; support for 3.x will be added once its ``cpyext`` extension + support catches up. A few minor features remain unsupported for the time + being (notably dynamic attributes in custom types). + `#527 `_. + +* Significant work on the documentation -- in particular, the monolitic + ``advanced.rst`` file was restructured into a easier to read hierarchical + organization. `#448 `_. + +* Many NumPy-related improvements: + + 1. Object-oriented API to access and modify NumPy ``ndarray`` instances, + replicating much of the corresponding NumPy C API functionality. + `#402 `_. + + 2. NumPy array ``dtype`` array descriptors are now first-class citizens and + are exposed via a new class ``py::dtype``. + + 3. Structured dtypes can be registered using the ``PYBIND11_NUMPY_DTYPE()`` + macro. Special ``array`` constructors accepting dtype objects were also + added. + + One potential caveat involving this change: format descriptor strings + should now be accessed via ``format_descriptor::format()`` (however, for + compatibility purposes, the old syntax ``format_descriptor::value`` will + still work for non-structured data types). `#308 + `_. + + 4. Further improvements to support structured dtypes throughout the system. + `#472 `_, + `#474 `_, + `#459 `_, + `#453 `_, + `#452 `_, and + `#505 `_. + + 5. Fast access operators. `#497 `_. + + 6. Constructors for arrays whose storage is owned by another object. + `#440 `_. + + 7. Added constructors for ``array`` and ``array_t`` explicitly accepting shape + and strides; if strides are not provided, they are deduced assuming + C-contiguity. Also added simplified constructors for 1-dimensional case. + + 8. Added buffer/NumPy support for ``char[N]`` and ``std::array`` types. + + 9. Added ``memoryview`` wrapper type which is constructible from ``buffer_info``. + +* Eigen: many additional conversions and support for non-contiguous + arrays/slices. + `#427 `_, + `#315 `_, + `#316 `_, + `#312 `_, and + `#267 `_ + +* Incompatible changes in ``class_<...>::class_()``: + + 1. Declarations of types that provide access via the buffer protocol must + now include the ``py::buffer_protocol()`` annotation as an argument to + the ``class_`` constructor. + + 2. Declarations of types that require a custom metaclass (i.e. all classes + which include static properties via commands such as + ``def_readwrite_static()``) must now include the ``py::metaclass()`` + annotation as an argument to the ``class_`` constructor. + + These two changes were necessary to make type definitions in pybind11 + future-proof, and to support PyPy via its cpyext mechanism. `#527 + `_. + + + 3. This version of pybind11 uses a redesigned mechnism for instantiating + trempoline classes that are used to override virtual methods from within + Python. This led to the following user-visible syntax change: instead of + + .. code-block:: cpp + + py::class_("MyClass") + .alias() + .... + + write + + .. code-block:: cpp + + py::class_("MyClass") + .... + + Importantly, both the original and the trampoline class are now + specified as an arguments (in arbitrary order) to the ``py::class_`` + template, and the ``alias<..>()`` call is gone. The new scheme has zero + overhead in cases when Python doesn't override any functions of the + underlying C++ class. `rev. 86d825 + `_. + +* Added ``eval`` and ``eval_file`` functions for evaluating expressions and + statements from a string or file. `rev. 0d3fc3 + `_. + +* pybind11 can now create types with a modifiable dictionary. + `#437 `_ and + `#444 `_. + +* Support for translation of arbitrary C++ exceptions to Python counterparts. + `#296 `_ and + `#273 `_. + +* Report full backtraces through mixed C++/Python code, better reporting for + import errors, fixed GIL management in exception processing. + `#537 `_, + `#494 `_, + `rev. e72d95 `_, and + `rev. 099d6e `_. + +* Support for bit-level operations, comparisons, and serialization of C++ + enumerations. `#503 `_, + `#508 `_, + `#380 `_, + `#309 `_. + `#311 `_. + +* The ``class_`` constructor now accepts its template arguments in any order. + `#385 `_. + +* Attribute and item accessors now have a more complete interface which makes + it possible to chain attributes as in + ``obj.attr("a")[key].attr("b").attr("method")(1, 2, 3)``. `#425 + `_. + +* Major redesign of the default and conversion constructors in ``pytypes.h``. + `#464 `_. + +* Added built-in support for ``std::shared_ptr`` holder type. It is no longer + necessary to to include a declaration of the form + ``PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr)`` (though continuing to + do so won't cause an error). + `#454 `_. + +* New ``py::overload_cast`` casting operator to select among multiple possible + overloads of a function. An example: + + .. code-block:: cpp + + py::class_(m, "Pet") + .def("set", py::overload_cast(&Pet::set), "Set the pet's age") + .def("set", py::overload_cast(&Pet::set), "Set the pet's name"); + + This feature only works on C++14-capable compilers. + `#541 `_. + +* C++ types are automatically cast to Python types, e.g. when assigning + them as an attribute. For instance, the following is now legal: + + .. code-block:: cpp + + py::module m = /* ... */ + m.attr("constant") = 123; + + (Previously, a ``py::cast`` call was necessary to avoid a compilation error.) + `#551 `_. + +* Redesigned ``pytest``-based test suite. `#321 `_. + +* Instance tracking to detect reference leaks in test suite. `#324 `_ + +* pybind11 can now distinguish between multiple different instances that are + located at the same memory address, but which have different types. + `#329 `_. + +* Improved logic in ``move`` return value policy. + `#510 `_, + `#297 `_. + +* Generalized unpacking API to permit calling Python functions from C++ using + notation such as ``foo(a1, a2, *args, "ka"_a=1, "kb"_a=2, **kwargs)``. `#372 `_. + +* ``py::print()`` function whose behavior matches that of the native Python + ``print()`` function. `#372 `_. + +* Added ``py::dict`` keyword constructor:``auto d = dict("number"_a=42, + "name"_a="World");``. `#372 `_. + +* Added ``py::str::format()`` method and ``_s`` literal: ``py::str s = "1 + 2 + = {}"_s.format(3);``. `#372 `_. + +* Added ``py::repr()`` function which is equivalent to Python's builtin + ``repr()``. `#333 `_. + +* Improved construction and destruction logic for holder types. It is now + possible to reference instances with smart pointer holder types without + constructing the holder if desired. The ``PYBIND11_DECLARE_HOLDER_TYPE`` + macro now accepts an optional second parameter to indicate whether the holder + type uses intrusive reference counting. + `#533 `_ and + `#561 `_. + +* Mapping a stateless C++ function to Python and back is now "for free" (i.e. + no extra indirections or argument conversion overheads). `rev. 954b79 + `_. + +* Bindings for ``std::valarray``. + `#545 `_. + +* Improved support for C++17 capable compilers. + `#562 `_. + +* Bindings for ``std::optional``. + `#475 `_, + `#476 `_, + `#479 `_, + `#499 `_, and + `#501 `_. + +* ``stl_bind.h``: general improvements and support for ``std::map`` and + ``std::unordered_map``. + `#490 `_, + `#282 `_, + `#235 `_. + +* The ``std::tuple``, ``std::pair``, ``std::list``, and ``std::vector`` type + casters now accept any Python sequence type as input. `rev. 107285 + `_. + +* Improved CMake Python detection on multi-architecture Linux. + `#532 `_. + +* Infrastructure to selectively disable or enable parts of the automatically + generated docstrings. `#486 `_. + +* ``reference`` and ``reference_internal`` are now the default return value + properties for static and non-static properties, respectively. `#473 + `_. (the previous defaults + were ``automatic``). `#473 `_. + +* Support for ``std::unique_ptr`` with non-default deleters or no deleter at + all (``py::nodelete``). `#384 `_. + +* Deprecated ``handle::call()`` method. The new syntax to call Python + functions is simply ``handle()``. It can also be invoked explicitly via + ``handle::operator()``, where ``X`` is an optional return value policy. + +* Print more informative error messages when ``make_tuple()`` or ``cast()`` + fail. `#262 `_. + +* Creation of holder types for classes deriving from + ``std::enable_shared_from_this<>`` now also works for ``const`` values. + `#260 `_. + +* ``make_iterator()`` improvements for better compatibility with various + types (now uses prefix increment operator); it now also accepts iterators + with different begin/end types as long as they are equality comparable. + `#247 `_. + +* ``arg()`` now accepts a wider range of argument types for default values. + `#244 `_. + +* Support ``keep_alive`` where the nurse object may be ``None``. `#341 + `_. + +* Added constructors for ``str`` and ``bytes`` from zero-terminated char + pointers, and from char pointers and length. Added constructors for ``str`` + from ``bytes`` and for ``bytes`` from ``str``, which will perform UTF-8 + decoding/encoding as required. + +* Many other improvements of library internals without user-visible changes + + +1.8.1 (July 12, 2016) +---------------------- +* Fixed a rare but potentially very severe issue when the garbage collector ran + during pybind11 type creation. + +1.8.0 (June 14, 2016) +---------------------- +* Redesigned CMake build system which exports a convenient + ``pybind11_add_module`` function to parent projects. +* ``std::vector<>`` type bindings analogous to Boost.Python's ``indexing_suite`` +* Transparent conversion of sparse and dense Eigen matrices and vectors (``eigen.h``) +* Added an ``ExtraFlags`` template argument to the NumPy ``array_t<>`` wrapper + to disable an enforced cast that may lose precision, e.g. to create overloads + for different precisions and complex vs real-valued matrices. +* Prevent implicit conversion of floating point values to integral types in + function arguments +* Fixed incorrect default return value policy for functions returning a shared + pointer +* Don't allow registering a type via ``class_`` twice +* Don't allow casting a ``None`` value into a C++ lvalue reference +* Fixed a crash in ``enum_::operator==`` that was triggered by the ``help()`` command +* Improved detection of whether or not custom C++ types can be copy/move-constructed +* Extended ``str`` type to also work with ``bytes`` instances +* Added a ``"name"_a`` user defined string literal that is equivalent to ``py::arg("name")``. +* When specifying function arguments via ``py::arg``, the test that verifies + the number of arguments now runs at compile time. +* Added ``[[noreturn]]`` attribute to ``pybind11_fail()`` to quench some + compiler warnings +* List function arguments in exception text when the dispatch code cannot find + a matching overload +* Added ``PYBIND11_OVERLOAD_NAME`` and ``PYBIND11_OVERLOAD_PURE_NAME`` macros which + can be used to override virtual methods whose name differs in C++ and Python + (e.g. ``__call__`` and ``operator()``) +* Various minor ``iterator`` and ``make_iterator()`` improvements +* Transparently support ``__bool__`` on Python 2.x and Python 3.x +* Fixed issue with destructor of unpickled object not being called +* Minor CMake build system improvements on Windows +* New ``pybind11::args`` and ``pybind11::kwargs`` types to create functions which + take an arbitrary number of arguments and keyword arguments +* New syntax to call a Python function from C++ using ``*args`` and ``*kwargs`` +* The functions ``def_property_*`` now correctly process docstring arguments (these + formerly caused a segmentation fault) +* Many ``mkdoc.py`` improvements (enumerations, template arguments, ``DOC()`` + macro accepts more arguments) +* Cygwin support +* Documentation improvements (pickling support, ``keep_alive``, macro usage) + +1.7 (April 30, 2016) +---------------------- +* Added a new ``move`` return value policy that triggers C++11 move semantics. + The automatic return value policy falls back to this case whenever a rvalue + reference is encountered +* Significantly more general GIL state routines that are used instead of + Python's troublesome ``PyGILState_Ensure`` and ``PyGILState_Release`` API +* Redesign of opaque types that drastically simplifies their usage +* Extended ability to pass values of type ``[const] void *`` +* ``keep_alive`` fix: don't fail when there is no patient +* ``functional.h``: acquire the GIL before calling a Python function +* Added Python RAII type wrappers ``none`` and ``iterable`` +* Added ``*args`` and ``*kwargs`` pass-through parameters to + ``pybind11.get_include()`` function +* Iterator improvements and fixes +* Documentation on return value policies and opaque types improved + +1.6 (April 30, 2016) +---------------------- +* Skipped due to upload to PyPI gone wrong and inability to recover + (https://github.com/pypa/packaging-problems/issues/74) + +1.5 (April 21, 2016) +---------------------- +* For polymorphic types, use RTTI to try to return the closest type registered with pybind11 +* Pickling support for serializing and unserializing C++ instances to a byte stream in Python +* Added a convenience routine ``make_iterator()`` which turns a range indicated + by a pair of C++ iterators into a iterable Python object +* Added ``len()`` and a variadic ``make_tuple()`` function +* Addressed a rare issue that could confuse the current virtual function + dispatcher and another that could lead to crashes in multi-threaded + applications +* Added a ``get_include()`` function to the Python module that returns the path + of the directory containing the installed pybind11 header files +* Documentation improvements: import issues, symbol visibility, pickling, limitations +* Added casting support for ``std::reference_wrapper<>`` + +1.4 (April 7, 2016) +-------------------------- +* Transparent type conversion for ``std::wstring`` and ``wchar_t`` +* Allow passing ``nullptr``-valued strings +* Transparent passing of ``void *`` pointers using capsules +* Transparent support for returning values wrapped in ``std::unique_ptr<>`` +* Improved docstring generation for compatibility with Sphinx +* Nicer debug error message when default parameter construction fails +* Support for "opaque" types that bypass the transparent conversion layer for STL containers +* Redesigned type casting interface to avoid ambiguities that could occasionally cause compiler errors +* Redesigned property implementation; fixes crashes due to an unfortunate default return value policy +* Anaconda package generation support + +1.3 (March 8, 2016) +-------------------------- + +* Added support for the Intel C++ compiler (v15+) +* Added support for the STL unordered set/map data structures +* Added support for the STL linked list data structure +* NumPy-style broadcasting support in ``pybind11::vectorize`` +* pybind11 now displays more verbose error messages when ``arg::operator=()`` fails +* pybind11 internal data structures now live in a version-dependent namespace to avoid ABI issues +* Many, many bugfixes involving corner cases and advanced usage + +1.2 (February 7, 2016) +-------------------------- + +* Optional: efficient generation of function signatures at compile time using C++14 +* Switched to a simpler and more general way of dealing with function default + arguments. Unused keyword arguments in function calls are now detected and + cause errors as expected +* New ``keep_alive`` call policy analogous to Boost.Python's ``with_custodian_and_ward`` +* New ``pybind11::base<>`` attribute to indicate a subclass relationship +* Improved interface for RAII type wrappers in ``pytypes.h`` +* Use RAII type wrappers consistently within pybind11 itself. This + fixes various potential refcount leaks when exceptions occur +* Added new ``bytes`` RAII type wrapper (maps to ``string`` in Python 2.7) +* Made handle and related RAII classes const correct, using them more + consistently everywhere now +* Got rid of the ugly ``__pybind11__`` attributes on the Python side---they are + now stored in a C++ hash table that is not visible in Python +* Fixed refcount leaks involving NumPy arrays and bound functions +* Vastly improved handling of shared/smart pointers +* Removed an unnecessary copy operation in ``pybind11::vectorize`` +* Fixed naming clashes when both pybind11 and NumPy headers are included +* Added conversions for additional exception types +* Documentation improvements (using multiple extension modules, smart pointers, + other minor clarifications) +* unified infrastructure for parsing variadic arguments in ``class_`` and cpp_function +* Fixed license text (was: ZLIB, should have been: 3-clause BSD) +* Python 3.2 compatibility +* Fixed remaining issues when accessing types in another plugin module +* Added enum comparison and casting methods +* Improved SFINAE-based detection of whether types are copy-constructible +* Eliminated many warnings about unused variables and the use of ``offsetof()`` +* Support for ``std::array<>`` conversions + +1.1 (December 7, 2015) +-------------------------- + +* Documentation improvements (GIL, wrapping functions, casting, fixed many typos) +* Generalized conversion of integer types +* Improved support for casting function objects +* Improved support for ``std::shared_ptr<>`` conversions +* Initial support for ``std::set<>`` conversions +* Fixed type resolution issue for types defined in a separate plugin module +* Cmake build system improvements +* Factored out generic functionality to non-templated code (smaller code size) +* Added a code size / compile time benchmark vs Boost.Python +* Added an appveyor CI script + +1.0 (October 15, 2015) +------------------------ +* Initial release diff --git a/external/pybind11/docs/classes.rst b/external/pybind11/docs/classes.rst new file mode 100644 index 0000000..ca2477e --- /dev/null +++ b/external/pybind11/docs/classes.rst @@ -0,0 +1,500 @@ +.. _classes: + +Object-oriented code +#################### + +Creating bindings for a custom type +=================================== + +Let's now look at a more complex example where we'll create bindings for a +custom C++ data structure named ``Pet``. Its definition is given below: + +.. code-block:: cpp + + struct Pet { + Pet(const std::string &name) : name(name) { } + void setName(const std::string &name_) { name = name_; } + const std::string &getName() const { return name; } + + std::string name; + }; + +The binding code for ``Pet`` looks as follows: + +.. code-block:: cpp + + #include + + namespace py = pybind11; + + PYBIND11_MODULE(example, m) { + py::class_(m, "Pet") + .def(py::init()) + .def("setName", &Pet::setName) + .def("getName", &Pet::getName); + } + +:class:`class_` creates bindings for a C++ *class* or *struct*-style data +structure. :func:`init` is a convenience function that takes the types of a +constructor's parameters as template arguments and wraps the corresponding +constructor (see the :ref:`custom_constructors` section for details). An +interactive Python session demonstrating this example is shown below: + +.. code-block:: pycon + + % python + >>> import example + >>> p = example.Pet('Molly') + >>> print(p) + + >>> p.getName() + u'Molly' + >>> p.setName('Charly') + >>> p.getName() + u'Charly' + +.. seealso:: + + Static member functions can be bound in the same way using + :func:`class_::def_static`. + +Keyword and default arguments +============================= +It is possible to specify keyword and default arguments using the syntax +discussed in the previous chapter. Refer to the sections :ref:`keyword_args` +and :ref:`default_args` for details. + +Binding lambda functions +======================== + +Note how ``print(p)`` produced a rather useless summary of our data structure in the example above: + +.. code-block:: pycon + + >>> print(p) + + +To address this, we could bind an utility function that returns a human-readable +summary to the special method slot named ``__repr__``. Unfortunately, there is no +suitable functionality in the ``Pet`` data structure, and it would be nice if +we did not have to change it. This can easily be accomplished by binding a +Lambda function instead: + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init()) + .def("setName", &Pet::setName) + .def("getName", &Pet::getName) + .def("__repr__", + [](const Pet &a) { + return ""; + } + ); + +Both stateless [#f1]_ and stateful lambda closures are supported by pybind11. +With the above change, the same Python code now produces the following output: + +.. code-block:: pycon + + >>> print(p) + + +.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object. + +.. _properties: + +Instance and static fields +========================== + +We can also directly expose the ``name`` field using the +:func:`class_::def_readwrite` method. A similar :func:`class_::def_readonly` +method also exists for ``const`` fields. + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init()) + .def_readwrite("name", &Pet::name) + // ... remainder ... + +This makes it possible to write + +.. code-block:: pycon + + >>> p = example.Pet('Molly') + >>> p.name + u'Molly' + >>> p.name = 'Charly' + >>> p.name + u'Charly' + +Now suppose that ``Pet::name`` was a private internal variable +that can only be accessed via setters and getters. + +.. code-block:: cpp + + class Pet { + public: + Pet(const std::string &name) : name(name) { } + void setName(const std::string &name_) { name = name_; } + const std::string &getName() const { return name; } + private: + std::string name; + }; + +In this case, the method :func:`class_::def_property` +(:func:`class_::def_property_readonly` for read-only data) can be used to +provide a field-like interface within Python that will transparently call +the setter and getter functions: + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init()) + .def_property("name", &Pet::getName, &Pet::setName) + // ... remainder ... + +.. seealso:: + + Similar functions :func:`class_::def_readwrite_static`, + :func:`class_::def_readonly_static` :func:`class_::def_property_static`, + and :func:`class_::def_property_readonly_static` are provided for binding + static variables and properties. Please also see the section on + :ref:`static_properties` in the advanced part of the documentation. + +Dynamic attributes +================== + +Native Python classes can pick up new attributes dynamically: + +.. code-block:: pycon + + >>> class Pet: + ... name = 'Molly' + ... + >>> p = Pet() + >>> p.name = 'Charly' # overwrite existing + >>> p.age = 2 # dynamically add a new attribute + +By default, classes exported from C++ do not support this and the only writable +attributes are the ones explicitly defined using :func:`class_::def_readwrite` +or :func:`class_::def_property`. + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init<>()) + .def_readwrite("name", &Pet::name); + +Trying to set any other attribute results in an error: + +.. code-block:: pycon + + >>> p = example.Pet() + >>> p.name = 'Charly' # OK, attribute defined in C++ + >>> p.age = 2 # fail + AttributeError: 'Pet' object has no attribute 'age' + +To enable dynamic attributes for C++ classes, the :class:`py::dynamic_attr` tag +must be added to the :class:`py::class_` constructor: + +.. code-block:: cpp + + py::class_(m, "Pet", py::dynamic_attr()) + .def(py::init<>()) + .def_readwrite("name", &Pet::name); + +Now everything works as expected: + +.. code-block:: pycon + + >>> p = example.Pet() + >>> p.name = 'Charly' # OK, overwrite value in C++ + >>> p.age = 2 # OK, dynamically add a new attribute + >>> p.__dict__ # just like a native Python class + {'age': 2} + +Note that there is a small runtime cost for a class with dynamic attributes. +Not only because of the addition of a ``__dict__``, but also because of more +expensive garbage collection tracking which must be activated to resolve +possible circular references. Native Python classes incur this same cost by +default, so this is not anything to worry about. By default, pybind11 classes +are more efficient than native Python classes. Enabling dynamic attributes +just brings them on par. + +.. _inheritance: + +Inheritance and automatic upcasting +=================================== + +Suppose now that the example consists of two data structures with an +inheritance relationship: + +.. code-block:: cpp + + struct Pet { + Pet(const std::string &name) : name(name) { } + std::string name; + }; + + struct Dog : Pet { + Dog(const std::string &name) : Pet(name) { } + std::string bark() const { return "woof!"; } + }; + +There are two different ways of indicating a hierarchical relationship to +pybind11: the first specifies the C++ base class as an extra template +parameter of the :class:`class_`: + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init()) + .def_readwrite("name", &Pet::name); + + // Method 1: template parameter: + py::class_(m, "Dog") + .def(py::init()) + .def("bark", &Dog::bark); + +Alternatively, we can also assign a name to the previously bound ``Pet`` +:class:`class_` object and reference it when binding the ``Dog`` class: + +.. code-block:: cpp + + py::class_ pet(m, "Pet"); + pet.def(py::init()) + .def_readwrite("name", &Pet::name); + + // Method 2: pass parent class_ object: + py::class_(m, "Dog", pet /* <- specify Python parent type */) + .def(py::init()) + .def("bark", &Dog::bark); + +Functionality-wise, both approaches are equivalent. Afterwards, instances will +expose fields and methods of both types: + +.. code-block:: pycon + + >>> p = example.Dog('Molly') + >>> p.name + u'Molly' + >>> p.bark() + u'woof!' + +The C++ classes defined above are regular non-polymorphic types with an +inheritance relationship. This is reflected in Python: + +.. code-block:: cpp + + // Return a base pointer to a derived instance + m.def("pet_store", []() { return std::unique_ptr(new Dog("Molly")); }); + +.. code-block:: pycon + + >>> p = example.pet_store() + >>> type(p) # `Dog` instance behind `Pet` pointer + Pet # no pointer upcasting for regular non-polymorphic types + >>> p.bark() + AttributeError: 'Pet' object has no attribute 'bark' + +The function returned a ``Dog`` instance, but because it's a non-polymorphic +type behind a base pointer, Python only sees a ``Pet``. In C++, a type is only +considered polymorphic if it has at least one virtual function and pybind11 +will automatically recognize this: + +.. code-block:: cpp + + struct PolymorphicPet { + virtual ~PolymorphicPet() = default; + }; + + struct PolymorphicDog : PolymorphicPet { + std::string bark() const { return "woof!"; } + }; + + // Same binding code + py::class_(m, "PolymorphicPet"); + py::class_(m, "PolymorphicDog") + .def(py::init<>()) + .def("bark", &PolymorphicDog::bark); + + // Again, return a base pointer to a derived instance + m.def("pet_store2", []() { return std::unique_ptr(new PolymorphicDog); }); + +.. code-block:: pycon + + >>> p = example.pet_store2() + >>> type(p) + PolymorphicDog # automatically upcast + >>> p.bark() + u'woof!' + +Given a pointer to a polymorphic base, pybind11 performs automatic upcasting +to the actual derived type. Note that this goes beyond the usual situation in +C++: we don't just get access to the virtual functions of the base, we get the +concrete derived type including functions and attributes that the base type may +not even be aware of. + +.. seealso:: + + For more information about polymorphic behavior see :ref:`overriding_virtuals`. + + +Overloaded methods +================== + +Sometimes there are several overloaded C++ methods with the same name taking +different kinds of input arguments: + +.. code-block:: cpp + + struct Pet { + Pet(const std::string &name, int age) : name(name), age(age) { } + + void set(int age_) { age = age_; } + void set(const std::string &name_) { name = name_; } + + std::string name; + int age; + }; + +Attempting to bind ``Pet::set`` will cause an error since the compiler does not +know which method the user intended to select. We can disambiguate by casting +them to function pointers. Binding multiple functions to the same Python name +automatically creates a chain of function overloads that will be tried in +sequence. + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init()) + .def("set", (void (Pet::*)(int)) &Pet::set, "Set the pet's age") + .def("set", (void (Pet::*)(const std::string &)) &Pet::set, "Set the pet's name"); + +The overload signatures are also visible in the method's docstring: + +.. code-block:: pycon + + >>> help(example.Pet) + + class Pet(__builtin__.object) + | Methods defined here: + | + | __init__(...) + | Signature : (Pet, str, int) -> NoneType + | + | set(...) + | 1. Signature : (Pet, int) -> NoneType + | + | Set the pet's age + | + | 2. Signature : (Pet, str) -> NoneType + | + | Set the pet's name + +If you have a C++14 compatible compiler [#cpp14]_, you can use an alternative +syntax to cast the overloaded function: + +.. code-block:: cpp + + py::class_(m, "Pet") + .def("set", py::overload_cast(&Pet::set), "Set the pet's age") + .def("set", py::overload_cast(&Pet::set), "Set the pet's name"); + +Here, ``py::overload_cast`` only requires the parameter types to be specified. +The return type and class are deduced. This avoids the additional noise of +``void (Pet::*)()`` as seen in the raw cast. If a function is overloaded based +on constness, the ``py::const_`` tag should be used: + +.. code-block:: cpp + + struct Widget { + int foo(int x, float y); + int foo(int x, float y) const; + }; + + py::class_(m, "Widget") + .def("foo_mutable", py::overload_cast(&Widget::foo)) + .def("foo_const", py::overload_cast(&Widget::foo, py::const_)); + + +.. [#cpp14] A compiler which supports the ``-std=c++14`` flag + or Visual Studio 2015 Update 2 and newer. + +.. note:: + + To define multiple overloaded constructors, simply declare one after the + other using the ``.def(py::init<...>())`` syntax. The existing machinery + for specifying keyword and default arguments also works. + +Enumerations and internal types +=============================== + +Let's now suppose that the example class contains an internal enumeration type, +e.g.: + +.. code-block:: cpp + + struct Pet { + enum Kind { + Dog = 0, + Cat + }; + + Pet(const std::string &name, Kind type) : name(name), type(type) { } + + std::string name; + Kind type; + }; + +The binding code for this example looks as follows: + +.. code-block:: cpp + + py::class_ pet(m, "Pet"); + + pet.def(py::init()) + .def_readwrite("name", &Pet::name) + .def_readwrite("type", &Pet::type); + + py::enum_(pet, "Kind") + .value("Dog", Pet::Kind::Dog) + .value("Cat", Pet::Kind::Cat) + .export_values(); + +To ensure that the ``Kind`` type is created within the scope of ``Pet``, the +``pet`` :class:`class_` instance must be supplied to the :class:`enum_`. +constructor. The :func:`enum_::export_values` function exports the enum entries +into the parent scope, which should be skipped for newer C++11-style strongly +typed enums. + +.. code-block:: pycon + + >>> p = Pet('Lucy', Pet.Cat) + >>> p.type + Kind.Cat + >>> int(p.type) + 1L + +The entries defined by the enumeration type are exposed in the ``__members__`` property: + +.. code-block:: pycon + + >>> Pet.Kind.__members__ + {'Dog': Kind.Dog, 'Cat': Kind.Cat} + +.. note:: + + When the special tag ``py::arithmetic()`` is specified to the ``enum_`` + constructor, pybind11 creates an enumeration that also supports rudimentary + arithmetic and bit-level operations like comparisons, and, or, xor, negation, + etc. + + .. code-block:: cpp + + py::enum_(pet, "Kind", py::arithmetic()) + ... + + By default, these are omitted to conserve space. diff --git a/external/pybind11/docs/compiling.rst b/external/pybind11/docs/compiling.rst new file mode 100644 index 0000000..b5d6ce9 --- /dev/null +++ b/external/pybind11/docs/compiling.rst @@ -0,0 +1,273 @@ +.. _compiling: + +Build systems +############# + +Building with setuptools +======================== + +For projects on PyPI, building with setuptools is the way to go. Sylvain Corlay +has kindly provided an example project which shows how to set up everything, +including automatic generation of documentation using Sphinx. Please refer to +the [python_example]_ repository. + +.. [python_example] https://github.com/pybind/python_example + +Building with cppimport +======================== + +[cppimport]_ is a small Python import hook that determines whether there is a C++ +source file whose name matches the requested module. If there is, the file is +compiled as a Python extension using pybind11 and placed in the same folder as +the C++ source file. Python is then able to find the module and load it. + +.. [cppimport] https://github.com/tbenthompson/cppimport + +.. _cmake: + +Building with CMake +=================== + +For C++ codebases that have an existing CMake-based build system, a Python +extension module can be created with just a few lines of code: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 2.8.12) + project(example) + + add_subdirectory(pybind11) + pybind11_add_module(example example.cpp) + +This assumes that the pybind11 repository is located in a subdirectory named +:file:`pybind11` and that the code is located in a file named :file:`example.cpp`. +The CMake command ``add_subdirectory`` will import the pybind11 project which +provides the ``pybind11_add_module`` function. It will take care of all the +details needed to build a Python extension module on any platform. + +A working sample project, including a way to invoke CMake from :file:`setup.py` for +PyPI integration, can be found in the [cmake_example]_ repository. + +.. [cmake_example] https://github.com/pybind/cmake_example + +pybind11_add_module +------------------- + +To ease the creation of Python extension modules, pybind11 provides a CMake +function with the following signature: + +.. code-block:: cmake + + pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] + [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) + +This function behaves very much like CMake's builtin ``add_library`` (in fact, +it's a wrapper function around that command). It will add a library target +called ```` to be built from the listed source files. In addition, it +will take care of all the Python-specific compiler and linker flags as well +as the OS- and Python-version-specific file extension. The produced target +```` can be further manipulated with regular CMake commands. + +``MODULE`` or ``SHARED`` may be given to specify the type of library. If no +type is given, ``MODULE`` is used by default which ensures the creation of a +Python-exclusive module. Specifying ``SHARED`` will create a more traditional +dynamic library which can also be linked from elsewhere. ``EXCLUDE_FROM_ALL`` +removes this target from the default build (see CMake docs for details). + +Since pybind11 is a template library, ``pybind11_add_module`` adds compiler +flags to ensure high quality code generation without bloat arising from long +symbol names and duplication of code in different translation units. It +sets default visibility to *hidden*, which is required for some pybind11 +features and functionality when attempting to load multiple pybind11 modules +compiled under different pybind11 versions. It also adds additional flags +enabling LTO (Link Time Optimization) and strip unneeded symbols. See the +:ref:`FAQ entry ` for a more detailed explanation. These +latter optimizations are never applied in ``Debug`` mode. If ``NO_EXTRAS`` is +given, they will always be disabled, even in ``Release`` mode. However, this +will result in code bloat and is generally not recommended. + +As stated above, LTO is enabled by default. Some newer compilers also support +different flavors of LTO such as `ThinLTO`_. Setting ``THIN_LTO`` will cause +the function to prefer this flavor if available. The function falls back to +regular LTO if ``-flto=thin`` is not available. + +.. _ThinLTO: http://clang.llvm.org/docs/ThinLTO.html + +Configuration variables +----------------------- + +By default, pybind11 will compile modules with the C++14 standard, if available +on the target compiler, falling back to C++11 if C++14 support is not +available. Note, however, that this default is subject to change: future +pybind11 releases are expected to migrate to newer C++ standards as they become +available. To override this, the standard flag can be given explicitly in +``PYBIND11_CPP_STANDARD``: + +.. code-block:: cmake + + # Use just one of these: + # GCC/clang: + set(PYBIND11_CPP_STANDARD -std=c++11) + set(PYBIND11_CPP_STANDARD -std=c++14) + set(PYBIND11_CPP_STANDARD -std=c++1z) # Experimental C++17 support + # MSVC: + set(PYBIND11_CPP_STANDARD /std:c++14) + set(PYBIND11_CPP_STANDARD /std:c++latest) # Enables some MSVC C++17 features + + add_subdirectory(pybind11) # or find_package(pybind11) + +Note that this and all other configuration variables must be set **before** the +call to ``add_subdirectory`` or ``find_package``. The variables can also be set +when calling CMake from the command line using the ``-D=`` flag. + +The target Python version can be selected by setting ``PYBIND11_PYTHON_VERSION`` +or an exact Python installation can be specified with ``PYTHON_EXECUTABLE``. +For example: + +.. code-block:: bash + + cmake -DPYBIND11_PYTHON_VERSION=3.6 .. + # or + cmake -DPYTHON_EXECUTABLE=path/to/python .. + +find_package vs. add_subdirectory +--------------------------------- + +For CMake-based projects that don't include the pybind11 repository internally, +an external installation can be detected through ``find_package(pybind11)``. +See the `Config file`_ docstring for details of relevant CMake variables. + +.. code-block:: cmake + + cmake_minimum_required(VERSION 2.8.12) + project(example) + + find_package(pybind11 REQUIRED) + pybind11_add_module(example example.cpp) + +Once detected, the aforementioned ``pybind11_add_module`` can be employed as +before. The function usage and configuration variables are identical no matter +if pybind11 is added as a subdirectory or found as an installed package. You +can refer to the same [cmake_example]_ repository for a full sample project +-- just swap out ``add_subdirectory`` for ``find_package``. + +.. _Config file: https://github.com/pybind/pybind11/blob/master/tools/pybind11Config.cmake.in + +Advanced: interface library target +---------------------------------- + +When using a version of CMake greater than 3.0, pybind11 can additionally +be used as a special *interface library* . The target ``pybind11::module`` +is available with pybind11 headers, Python headers and libraries as needed, +and C++ compile definitions attached. This target is suitable for linking +to an independently constructed (through ``add_library``, not +``pybind11_add_module``) target in the consuming project. + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.0) + project(example) + + find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) + + add_library(example MODULE main.cpp) + target_link_libraries(example PRIVATE pybind11::module) + set_target_properties(example PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") + +.. warning:: + + Since pybind11 is a metatemplate library, it is crucial that certain + compiler flags are provided to ensure high quality code generation. In + contrast to the ``pybind11_add_module()`` command, the CMake interface + library only provides the *minimal* set of parameters to ensure that the + code using pybind11 compiles, but it does **not** pass these extra compiler + flags (i.e. this is up to you). + + These include Link Time Optimization (``-flto`` on GCC/Clang/ICPC, ``/GL`` + and ``/LTCG`` on Visual Studio) and .OBJ files with many sections on Visual + Studio (``/bigobj``). The :ref:`FAQ ` contains an + explanation on why these are needed. + +Embedding the Python interpreter +-------------------------------- + +In addition to extension modules, pybind11 also supports embedding Python into +a C++ executable or library. In CMake, simply link with the ``pybind11::embed`` +target. It provides everything needed to get the interpreter running. The Python +headers and libraries are attached to the target. Unlike ``pybind11::module``, +there is no need to manually set any additional properties here. For more +information about usage in C++, see :doc:`/advanced/embedding`. + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.0) + project(example) + + find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) + + add_executable(example main.cpp) + target_link_libraries(example PRIVATE pybind11::embed) + +.. _building_manually: + +Building manually +================= + +pybind11 is a header-only library, hence it is not necessary to link against +any special libraries and there are no intermediate (magic) translation steps. + +On Linux, you can compile an example such as the one given in +:ref:`simple_example` using the following command: + +.. code-block:: bash + + $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + +The flags given here assume that you're using Python 3. For Python 2, just +change the executable appropriately (to ``python`` or ``python2``). + +The ``python3 -m pybind11 --includes`` command fetches the include paths for +both pybind11 and Python headers. This assumes that pybind11 has been installed +using ``pip`` or ``conda``. If it hasn't, you can also manually specify +``-I /include`` together with the Python includes path +``python3-config --includes``. + +Note that Python 2.7 modules don't use a special suffix, so you should simply +use ``example.so`` instead of ``example`python3-config --extension-suffix```. +Besides, the ``--extension-suffix`` option may or may not be available, depending +on the distribution; in the latter case, the module extension can be manually +set to ``.so``. + +On Mac OS: the build command is almost the same but it also requires passing +the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when +building the module: + +.. code-block:: bash + + $ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + +In general, it is advisable to include several additional build parameters +that can considerably reduce the size of the created binary. Refer to section +:ref:`cmake` for a detailed example of a suitable cross-platform CMake-based +build system that works on all platforms including Windows. + +.. note:: + + On Linux and macOS, it's better to (intentionally) not link against + ``libpython``. The symbols will be resolved when the extension library + is loaded into a Python binary. This is preferable because you might + have several different installations of a given Python version (e.g. the + system-provided Python, and one that ships with a piece of commercial + software). In this way, the plugin will work with both versions, instead + of possibly importing a second Python library into a process that already + contains one (which will lead to a segfault). + +Generating binding code automatically +===================================== + +The ``Binder`` project is a tool for automatic generation of pybind11 binding +code by introspecting existing C++ codebases using LLVM/Clang. See the +[binder]_ documentation for details. + +.. [binder] http://cppbinder.readthedocs.io/en/latest/about.html diff --git a/external/pybind11/docs/conf.py b/external/pybind11/docs/conf.py new file mode 100644 index 0000000..cd0e17e --- /dev/null +++ b/external/pybind11/docs/conf.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# pybind11 documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 11 19:23:48 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex +import subprocess + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['breathe'] + +breathe_projects = {'pybind11': '.build/doxygenxml/'} +breathe_default_project = 'pybind11' +breathe_domain_by_extension = {'h': 'cpp'} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['.templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'pybind11' +copyright = '2017, Wenzel Jakob' +author = 'Wenzel Jakob' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '2.2' +# The full version, including alpha/beta/rc tags. +release = '2.2.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['.build', 'release.rst'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +default_role = 'any' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +#pygments_style = 'monokai' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + + html_context = { + 'css_files': [ + '_static/theme_overrides.css' + ] + } +else: + html_context = { + 'css_files': [ + '//media.readthedocs.org/css/sphinx_rtd_theme.css', + '//media.readthedocs.org/css/readthedocs-doc-embed.css', + '_static/theme_overrides.css' + ] + } + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pybind11doc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +'preamble': '\DeclareUnicodeCharacter{00A0}{}', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'pybind11.tex', 'pybind11 Documentation', + 'Wenzel Jakob', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = 'pybind11-logo.png' + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pybind11', 'pybind11 Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'pybind11', 'pybind11 Documentation', + author, 'pybind11', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + +primary_domain = 'cpp' +highlight_language = 'cpp' + + +def generate_doxygen_xml(app): + build_dir = os.path.join(app.confdir, '.build') + if not os.path.exists(build_dir): + os.mkdir(build_dir) + + try: + subprocess.call(['doxygen', '--version']) + retcode = subprocess.call(['doxygen'], cwd=app.confdir) + if retcode < 0: + sys.stderr.write("doxygen error code: {}\n".format(-retcode)) + except OSError as e: + sys.stderr.write("doxygen execution failed: {}\n".format(e)) + + +def setup(app): + """Add hook for building doxygen xml when needed""" + app.connect("builder-inited", generate_doxygen_xml) diff --git a/external/pybind11/docs/faq.rst b/external/pybind11/docs/faq.rst new file mode 100644 index 0000000..8f33eb0 --- /dev/null +++ b/external/pybind11/docs/faq.rst @@ -0,0 +1,273 @@ +Frequently asked questions +########################## + +"ImportError: dynamic module does not define init function" +=========================================================== + +You are likely using an incompatible version of Python (for instance, the +extension library was compiled against Python 2, while the interpreter is +running on top of some version of Python 3, or vice versa). + +"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" +======================================================================== + +See the first answer. + +"SystemError: dynamic module not initialized properly" +====================================================== + +See the first answer. + +The Python interpreter immediately crashes when importing my module +=================================================================== + +See the first answer. + +CMake doesn't detect the right Python version +============================================= + +The CMake-based build system will try to automatically detect the installed +version of Python and link against that. When this fails, or when there are +multiple versions of Python and it finds the wrong one, delete +``CMakeCache.txt`` and then invoke CMake as follows: + +.. code-block:: bash + + cmake -DPYTHON_EXECUTABLE:FILEPATH= . + +Limitations involving reference arguments +========================================= + +In C++, it's fairly common to pass arguments using mutable references or +mutable pointers, which allows both read and write access to the value +supplied by the caller. This is sometimes done for efficiency reasons, or to +realize functions that have multiple return values. Here are two very basic +examples: + +.. code-block:: cpp + + void increment(int &i) { i++; } + void increment_ptr(int *i) { (*i)++; } + +In Python, all arguments are passed by reference, so there is no general +issue in binding such code from Python. + +However, certain basic Python types (like ``str``, ``int``, ``bool``, +``float``, etc.) are **immutable**. This means that the following attempt +to port the function to Python doesn't have the same effect on the value +provided by the caller -- in fact, it does nothing at all. + +.. code-block:: python + + def increment(i): + i += 1 # nope.. + +pybind11 is also affected by such language-level conventions, which means that +binding ``increment`` or ``increment_ptr`` will also create Python functions +that don't modify their arguments. + +Although inconvenient, one workaround is to encapsulate the immutable types in +a custom type that does allow modifications. + +An other alternative involves binding a small wrapper lambda function that +returns a tuple with all output arguments (see the remainder of the +documentation for examples on binding lambda functions). An example: + +.. code-block:: cpp + + int foo(int &i) { i++; return 123; } + +and the binding code + +.. code-block:: cpp + + m.def("foo", [](int i) { int rv = foo(i); return std::make_tuple(rv, i); }); + + +How can I reduce the build time? +================================ + +It's good practice to split binding code over multiple files, as in the +following example: + +:file:`example.cpp`: + +.. code-block:: cpp + + void init_ex1(py::module &); + void init_ex2(py::module &); + /* ... */ + + PYBIND11_MODULE(example, m) { + init_ex1(m); + init_ex2(m); + /* ... */ + } + +:file:`ex1.cpp`: + +.. code-block:: cpp + + void init_ex1(py::module &m) { + m.def("add", [](int a, int b) { return a + b; }); + } + +:file:`ex2.cpp`: + +.. code-block:: cpp + + void init_ex1(py::module &m) { + m.def("sub", [](int a, int b) { return a - b; }); + } + +:command:`python`: + +.. code-block:: pycon + + >>> import example + >>> example.add(1, 2) + 3 + >>> example.sub(1, 1) + 0 + +As shown above, the various ``init_ex`` functions should be contained in +separate files that can be compiled independently from one another, and then +linked together into the same final shared object. Following this approach +will: + +1. reduce memory requirements per compilation unit. + +2. enable parallel builds (if desired). + +3. allow for faster incremental builds. For instance, when a single class + definition is changed, only a subset of the binding code will generally need + to be recompiled. + +"recursive template instantiation exceeded maximum depth of 256" +================================================================ + +If you receive an error about excessive recursive template evaluation, try +specifying a larger value, e.g. ``-ftemplate-depth=1024`` on GCC/Clang. The +culprit is generally the generation of function signatures at compile time +using C++14 template metaprogramming. + +.. _`faq:hidden_visibility`: + +"‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]" +============================================================================================================ + +This error typically indicates that you are compiling without the required +``-fvisibility`` flag. pybind11 code internally forces hidden visibility on +all internal code, but if non-hidden (and thus *exported*) code attempts to +include a pybind type (for example, ``py::object`` or ``py::list``) you can run +into this warning. + +To avoid it, make sure you are specifying ``-fvisibility=hidden`` when +compiling pybind code. + +As to why ``-fvisibility=hidden`` is necessary, because pybind modules could +have been compiled under different versions of pybind itself, it is also +important that the symbols defined in one module do not clash with the +potentially-incompatible symbols defined in another. While Python extension +modules are usually loaded with localized symbols (under POSIX systems +typically using ``dlopen`` with the ``RTLD_LOCAL`` flag), this Python default +can be changed, but even if it isn't it is not always enough to guarantee +complete independence of the symbols involved when not using +``-fvisibility=hidden``. + +Additionally, ``-fvisiblity=hidden`` can deliver considerably binary size +savings. (See the following section for more details). + + +.. _`faq:symhidden`: + +How can I create smaller binaries? +================================== + +To do its job, pybind11 extensively relies on a programming technique known as +*template metaprogramming*, which is a way of performing computation at compile +time using type information. Template metaprogamming usually instantiates code +involving significant numbers of deeply nested types that are either completely +removed or reduced to just a few instructions during the compiler's optimization +phase. However, due to the nested nature of these types, the resulting symbol +names in the compiled extension library can be extremely long. For instance, +the included test suite contains the following symbol: + +.. only:: html + + .. code-block:: none + + _​_​Z​N​8​p​y​b​i​n​d​1​1​1​2​c​p​p​_​f​u​n​c​t​i​o​n​C​1​I​v​8​E​x​a​m​p​l​e​2​J​R​N​S​t​3​_​_​1​6​v​e​c​t​o​r​I​N​S​3​_​1​2​b​a​s​i​c​_​s​t​r​i​n​g​I​w​N​S​3​_​1​1​c​h​a​r​_​t​r​a​i​t​s​I​w​E​E​N​S​3​_​9​a​l​l​o​c​a​t​o​r​I​w​E​E​E​E​N​S​8​_​I​S​A​_​E​E​E​E​E​J​N​S​_​4​n​a​m​e​E​N​S​_​7​s​i​b​l​i​n​g​E​N​S​_​9​i​s​_​m​e​t​h​o​d​E​A​2​8​_​c​E​E​E​M​T​0​_​F​T​_​D​p​T​1​_​E​D​p​R​K​T​2​_ + +.. only:: not html + + .. code-block:: cpp + + __ZN8pybind1112cpp_functionC1Iv8Example2JRNSt3__16vectorINS3_12basic_stringIwNS3_11char_traitsIwEENS3_9allocatorIwEEEENS8_ISA_EEEEEJNS_4nameENS_7siblingENS_9is_methodEA28_cEEEMT0_FT_DpT1_EDpRKT2_ + +which is the mangled form of the following function type: + +.. code-block:: cpp + + pybind11::cpp_function::cpp_function, std::__1::allocator >, std::__1::allocator, std::__1::allocator > > >&, pybind11::name, pybind11::sibling, pybind11::is_method, char [28]>(void (Example2::*)(std::__1::vector, std::__1::allocator >, std::__1::allocator, std::__1::allocator > > >&), pybind11::name const&, pybind11::sibling const&, pybind11::is_method const&, char const (&) [28]) + +The memory needed to store just the mangled name of this function (196 bytes) +is larger than the actual piece of code (111 bytes) it represents! On the other +hand, it's silly to even give this function a name -- after all, it's just a +tiny cog in a bigger piece of machinery that is not exposed to the outside +world. So we'll generally only want to export symbols for those functions which +are actually called from the outside. + +This can be achieved by specifying the parameter ``-fvisibility=hidden`` to GCC +and Clang, which sets the default symbol visibility to *hidden*, which has a +tremendous impact on the final binary size of the resulting extension library. +(On Visual Studio, symbols are already hidden by default, so nothing needs to +be done there.) + +In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids +potential serious issues when loading multiple modules and is required for +proper pybind operation. See the previous FAQ entry for more details. + +Another aspect that can require a fair bit of code are function signature +descriptions. pybind11 automatically generates human-readable function +signatures for docstrings, e.g.: + +.. code-block:: none + + | __init__(...) + | __init__(*args, **kwargs) + | Overloaded function. + | + | 1. __init__(example.Example1) -> NoneType + | + | Docstring for overload #1 goes here + | + | 2. __init__(example.Example1, int) -> NoneType + | + | Docstring for overload #2 goes here + | + | 3. __init__(example.Example1, example.Example1) -> NoneType + | + | Docstring for overload #3 goes here + + +In C++11 mode, these are generated at run time using string concatenation, +which can amount to 10-20% of the size of the resulting binary. If you can, +enable C++14 language features (using ``-std=c++14`` for GCC/Clang), in which +case signatures are efficiently pre-generated at compile time. Unfortunately, +Visual Studio's C++14 support (``constexpr``) is not good enough as of April +2016, so it always uses the more expensive run-time approach. + +Working with ancient Visual Studio 2009 builds on Windows +========================================================= + +The official Windows distributions of Python are compiled using truly +ancient versions of Visual Studio that lack good C++11 support. Some users +implicitly assume that it would be impossible to load a plugin built with +Visual Studio 2015 into a Python distribution that was compiled using Visual +Studio 2009. However, no such issue exists: it's perfectly legitimate to +interface DLLs that are built with different compilers and/or C libraries. +Common gotchas to watch out for involve not ``free()``-ing memory region +that that were ``malloc()``-ed in another shared library, using data +structures with incompatible ABIs, and so on. pybind11 is very careful not +to make these types of mistakes. diff --git a/external/pybind11/docs/index.rst b/external/pybind11/docs/index.rst new file mode 100644 index 0000000..d236611 --- /dev/null +++ b/external/pybind11/docs/index.rst @@ -0,0 +1,47 @@ +.. only: not latex + + .. image:: pybind11-logo.png + +pybind11 --- Seamless operability between C++11 and Python +========================================================== + +.. only: not latex + + Contents: + +.. toctree:: + :maxdepth: 1 + + intro + changelog + upgrade + +.. toctree:: + :caption: The Basics + :maxdepth: 2 + + basics + classes + compiling + +.. toctree:: + :caption: Advanced Topics + :maxdepth: 2 + + advanced/functions + advanced/classes + advanced/exceptions + advanced/smart_ptrs + advanced/cast/index + advanced/pycpp/index + advanced/embedding + advanced/misc + +.. toctree:: + :caption: Extra Information + :maxdepth: 1 + + faq + benchmark + limitations + reference diff --git a/external/pybind11/docs/intro.rst b/external/pybind11/docs/intro.rst new file mode 100644 index 0000000..2149c18 --- /dev/null +++ b/external/pybind11/docs/intro.rst @@ -0,0 +1,95 @@ +.. image:: pybind11-logo.png + +About this project +================== +**pybind11** is a lightweight header-only library that exposes C++ types in Python +and vice versa, mainly to create Python bindings of existing C++ code. Its +goals and syntax are similar to the excellent `Boost.Python`_ library by David +Abrahams: to minimize boilerplate code in traditional extension modules by +inferring type information using compile-time introspection. + +.. _Boost.Python: http://www.boost.org/doc/libs/release/libs/python/doc/index.html + +The main issue with Boost.Python—and the reason for creating such a similar +project—is Boost. Boost is an enormously large and complex suite of utility +libraries that works with almost every C++ compiler in existence. This +compatibility has its cost: arcane template tricks and workarounds are +necessary to support the oldest and buggiest of compiler specimens. Now that +C++11-compatible compilers are widely available, this heavy machinery has +become an excessively large and unnecessary dependency. +Think of this library as a tiny self-contained version of Boost.Python with +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~4K lines of code and depend on +Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This +compact implementation was possible thanks to some of the new C++11 language +features (specifically: tuples, lambda functions and variadic templates). Since +its creation, this library has grown beyond Boost.Python in many ways, leading +to dramatically simpler binding code in many common situations. + +Core features +************* +The following core C++ features can be mapped to Python + +- Functions accepting and returning custom data structures per value, reference, or pointer +- Instance methods and static methods +- Overloaded functions +- Instance attributes and static attributes +- Arbitrary exception types +- Enumerations +- Callbacks +- Iterators and ranges +- Custom operators +- Single and multiple inheritance +- STL data structures +- Iterators and ranges +- Smart pointers with reference counting like ``std::shared_ptr`` +- Internal references with correct reference counting +- C++ classes with virtual (and pure virtual) methods can be extended in Python + +Goodies +******* +In addition to the core functionality, pybind11 provides some extra goodies: + +- Python 2.7, 3.x, and PyPy (PyPy2.7 >= 5.7) are supported with an + implementation-agnostic interface. + +- It is possible to bind C++11 lambda functions with captured variables. The + lambda capture data is stored inside the resulting Python function object. + +- pybind11 uses C++11 move constructors and move assignment operators whenever + possible to efficiently transfer custom data types. + +- It's easy to expose the internal storage of custom data types through + Pythons' buffer protocols. This is handy e.g. for fast conversion between + C++ matrix classes like Eigen and NumPy without expensive copy operations. + +- pybind11 can automatically vectorize functions so that they are transparently + applied to all entries of one or more NumPy array arguments. + +- Python's slice-based access and assignment operations can be supported with + just a few lines of code. + +- Everything is contained in just a few header files; there is no need to link + against any additional libraries. + +- Binaries are generally smaller by a factor of at least 2 compared to + equivalent bindings generated by Boost.Python. A recent pybind11 conversion + of `PyRosetta`_, an enormous Boost.Python binding project, reported a binary + size reduction of **5.4x** and compile time reduction by **5.8x**. + +- When supported by the compiler, two new C++14 features (relaxed constexpr and + return value deduction) are used to precompute function signatures at compile + time, leading to smaller binaries. + +- With little extra effort, C++ types can be pickled and unpickled similar to + regular Python objects. + +.. _PyRosetta: http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf + +Supported compilers +******************* + +1. Clang/LLVM (any non-ancient version with C++11 support) +2. GCC 4.8 or newer +3. Microsoft Visual Studio 2015 or newer +4. Intel C++ compiler v15 or newer diff --git a/external/pybind11/docs/limitations.rst b/external/pybind11/docs/limitations.rst new file mode 100644 index 0000000..a1a4f1a --- /dev/null +++ b/external/pybind11/docs/limitations.rst @@ -0,0 +1,20 @@ +Limitations +########### + +pybind11 strives to be a general solution to binding generation, but it also has +certain limitations: + +- pybind11 casts away ``const``-ness in function arguments and return values. + This is in line with the Python language, which has no concept of ``const`` + values. This means that some additional care is needed to avoid bugs that + would be caught by the type checker in a traditional C++ program. + +- The NumPy interface ``pybind11::array`` greatly simplifies accessing + numerical data from C++ (and vice versa), but it's not a full-blown array + class like ``Eigen::Array`` or ``boost.multi_array``. + +These features could be implemented but would lead to a significant increase in +complexity. I've decided to draw the line here to keep this project simple and +compact. Users who absolutely require these features are encouraged to fork +pybind11. + diff --git a/external/pybind11/docs/pybind11-logo.png b/external/pybind11/docs/pybind11-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4cbad54f797d3ced04d4048f282df5e4336d4af4 GIT binary patch literal 58510 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/pybind11/docs/pybind11_vs_boost_python2.png b/external/pybind11/docs/pybind11_vs_boost_python2.png new file mode 100644 index 0000000000000000000000000000000000000000..9f17272c50663957d6ae6d8e23fdd5a15757e71f GIT binary patch literal 41121 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 BtT6xp literal 0 HcmV?d00001 diff --git a/external/pybind11/docs/pybind11_vs_boost_python2.svg b/external/pybind11/docs/pybind11_vs_boost_python2.svg new file mode 100644 index 0000000..5ed6530 --- /dev/null +++ b/external/pybind11/docs/pybind11_vs_boost_python2.svg @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/pybind11/docs/reference.rst b/external/pybind11/docs/reference.rst new file mode 100644 index 0000000..e41141b --- /dev/null +++ b/external/pybind11/docs/reference.rst @@ -0,0 +1,102 @@ +.. _reference: + +.. warning:: + + Please be advised that the reference documentation discussing pybind11 + internals is currently incomplete. Please refer to the previous sections + and the pybind11 header files for the nitty gritty details. + +Reference +######### + +.. _macros: + +Macros +====== + +.. doxygendefine:: PYBIND11_MODULE + +.. _core_types: + +Convenience classes for arbitrary Python types +============================================== + +Common member functions +----------------------- + +.. doxygenclass:: object_api + :members: + +Without reference counting +-------------------------- + +.. doxygenclass:: handle + :members: + +With reference counting +----------------------- + +.. doxygenclass:: object + :members: + +.. doxygenfunction:: reinterpret_borrow + +.. doxygenfunction:: reinterpret_steal + +Convenience classes for specific Python types +============================================= + +.. doxygenclass:: module + :members: + +.. doxygengroup:: pytypes + :members: + +.. _extras: + +Passing extra arguments to ``def`` or ``class_`` +================================================ + +.. doxygengroup:: annotations + :members: + +Embedding the interpreter +========================= + +.. doxygendefine:: PYBIND11_EMBEDDED_MODULE + +.. doxygenfunction:: initialize_interpreter + +.. doxygenfunction:: finalize_interpreter + +.. doxygenclass:: scoped_interpreter + +Redirecting C++ streams +======================= + +.. doxygenclass:: scoped_ostream_redirect + +.. doxygenclass:: scoped_estream_redirect + +.. doxygenfunction:: add_ostream_redirect + +Python build-in functions +========================= + +.. doxygengroup:: python_builtins + :members: + +Exceptions +========== + +.. doxygenclass:: error_already_set + :members: + +.. doxygenclass:: builtin_exception + :members: + + +Literals +======== + +.. doxygennamespace:: literals diff --git a/external/pybind11/docs/release.rst b/external/pybind11/docs/release.rst new file mode 100644 index 0000000..b31bbe9 --- /dev/null +++ b/external/pybind11/docs/release.rst @@ -0,0 +1,25 @@ +To release a new version of pybind11: + +- Update the version number and push to pypi + - Update ``pybind11/_version.py`` (set release version, remove 'dev'). + - Update ``PYBIND11_VERSION_MAJOR`` etc. in ``include/pybind11/detail/common.h``. + - Ensure that all the information in ``setup.py`` is up-to-date. + - Update version in ``docs/conf.py``. + - Tag release date in ``docs/changelog.rst``. + - ``git add`` and ``git commit``. + - if new minor version: ``git checkout -b vX.Y``, ``git push -u origin vX.Y`` + - ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``. + - ``git push`` + - ``git push --tags``. + - ``python setup.py sdist upload``. + - ``python setup.py bdist_wheel upload``. +- Update conda-forge (https://github.com/conda-forge/pybind11-feedstock) via PR + - download release package from Github: ``wget https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz`` + - compute checksum: ``shasum -a 256 vX.Y.Z.tar.gz`` + - change version number and checksum in ``recipe/meta.yml`` +- Get back to work + - Update ``_version.py`` (add 'dev' and increment minor). + - Update version in ``docs/conf.py`` + - Update version macros in ``include/pybind11/common.h`` + - ``git add`` and ``git commit``. + ``git push`` diff --git a/external/pybind11/docs/requirements.txt b/external/pybind11/docs/requirements.txt new file mode 100644 index 0000000..3818fe8 --- /dev/null +++ b/external/pybind11/docs/requirements.txt @@ -0,0 +1 @@ +breathe == 4.5.0 diff --git a/external/pybind11/docs/upgrade.rst b/external/pybind11/docs/upgrade.rst new file mode 100644 index 0000000..3f56973 --- /dev/null +++ b/external/pybind11/docs/upgrade.rst @@ -0,0 +1,404 @@ +Upgrade guide +############# + +This is a companion guide to the :doc:`changelog`. While the changelog briefly +lists all of the new features, improvements and bug fixes, this upgrade guide +focuses only the subset which directly impacts your experience when upgrading +to a new version. But it goes into more detail. This includes things like +deprecated APIs and their replacements, build system changes, general code +modernization and other useful information. + + +v2.2 +==== + +Deprecation of the ``PYBIND11_PLUGIN`` macro +-------------------------------------------- + +``PYBIND11_MODULE`` is now the preferred way to create module entry points. +The old macro emits a compile-time deprecation warning. + +.. code-block:: cpp + + // old + PYBIND11_PLUGIN(example) { + py::module m("example", "documentation string"); + + m.def("add", [](int a, int b) { return a + b; }); + + return m.ptr(); + } + + // new + PYBIND11_MODULE(example, m) { + m.doc() = "documentation string"; // optional + + m.def("add", [](int a, int b) { return a + b; }); + } + + +New API for defining custom constructors and pickling functions +--------------------------------------------------------------- + +The old placement-new custom constructors have been deprecated. The new approach +uses ``py::init()`` and factory functions to greatly improve type safety. + +Placement-new can be called accidentally with an incompatible type (without any +compiler errors or warnings), or it can initialize the same object multiple times +if not careful with the Python-side ``__init__`` calls. The new-style custom +constructors prevent such mistakes. See :ref:`custom_constructors` for details. + +.. code-block:: cpp + + // old -- deprecated (runtime warning shown only in debug mode) + py::class(m, "Foo") + .def("__init__", [](Foo &self, ...) { + new (&self) Foo(...); // uses placement-new + }); + + // new + py::class(m, "Foo") + .def(py::init([](...) { // Note: no `self` argument + return new Foo(...); // return by raw pointer + // or: return std::make_unique(...); // return by holder + // or: return Foo(...); // return by value (move constructor) + })); + +Mirroring the custom constructor changes, ``py::pickle()`` is now the preferred +way to get and set object state. See :ref:`pickling` for details. + +.. code-block:: cpp + + // old -- deprecated (runtime warning shown only in debug mode) + py::class(m, "Foo") + ... + .def("__getstate__", [](const Foo &self) { + return py::make_tuple(self.value1(), self.value2(), ...); + }) + .def("__setstate__", [](Foo &self, py::tuple t) { + new (&self) Foo(t[0].cast(), ...); + }); + + // new + py::class(m, "Foo") + ... + .def(py::pickle( + [](const Foo &self) { // __getstate__ + return py::make_tuple(f.value1(), f.value2(), ...); // unchanged + }, + [](py::tuple t) { // __setstate__, note: no `self` argument + return new Foo(t[0].cast(), ...); + // or: return std::make_unique(...); // return by holder + // or: return Foo(...); // return by value (move constructor) + } + )); + +For both the constructors and pickling, warnings are shown at module +initialization time (on import, not when the functions are called). +They're only visible when compiled in debug mode. Sample warning: + +.. code-block:: none + + pybind11-bound class 'mymodule.Foo' is using an old-style placement-new '__init__' + which has been deprecated. See the upgrade guide in pybind11's docs. + + +Stricter enforcement of hidden symbol visibility for pybind11 modules +--------------------------------------------------------------------- + +pybind11 now tries to actively enforce hidden symbol visibility for modules. +If you're using either one of pybind11's :doc:`CMake or Python build systems +` (the two example repositories) and you haven't been exporting any +symbols, there's nothing to be concerned about. All the changes have been done +transparently in the background. If you were building manually or relied on +specific default visibility, read on. + +Setting default symbol visibility to *hidden* has always been recommended for +pybind11 (see :ref:`faq:symhidden`). On Linux and macOS, hidden symbol +visibility (in conjunction with the ``strip`` utility) yields much smaller +module binaries. `CPython's extension docs`_ also recommend hiding symbols +by default, with the goal of avoiding symbol name clashes between modules. +Starting with v2.2, pybind11 enforces this more strictly: (1) by declaring +all symbols inside the ``pybind11`` namespace as hidden and (2) by including +the ``-fvisibility=hidden`` flag on Linux and macOS (only for extension +modules, not for embedding the interpreter). + +.. _CPython's extension docs: https://docs.python.org/3/extending/extending.html#providing-a-c-api-for-an-extension-module + +The namespace-scope hidden visibility is done automatically in pybind11's +headers and it's generally transparent to users. It ensures that: + +* Modules compiled with different pybind11 versions don't clash with each other. + +* Some new features, like ``py::module_local`` bindings, can work as intended. + +The ``-fvisibility=hidden`` flag applies the same visibility to user bindings +outside of the ``pybind11`` namespace. It's now set automatic by pybind11's +CMake and Python build systems, but this needs to be done manually by users +of other build systems. Adding this flag: + +* Minimizes the chances of symbol conflicts between modules. E.g. if two + unrelated modules were statically linked to different (ABI-incompatible) + versions of the same third-party library, a symbol clash would be likely + (and would end with unpredictable results). + +* Produces smaller binaries on Linux and macOS, as pointed out previously. + +Within pybind11's CMake build system, ``pybind11_add_module`` has always been +setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's +being applied unconditionally, even in debug mode and it can no longer be opted +out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also +adds this flag to it's interface. The ``pybind11::embed`` target is unchanged. + +The most significant change here is for the ``pybind11::module`` target. If you +were previously relying on default visibility, i.e. if your Python module was +doubling as a shared library with dependents, you'll need to either export +symbols manually (recommended for cross-platform libraries) or factor out the +shared library (and have the Python module link to it like the other +dependents). As a temporary workaround, you can also restore default visibility +using the CMake code below, but this is not recommended in the long run: + +.. code-block:: cmake + + target_link_libraries(mymodule PRIVATE pybind11::module) + + add_library(restore_default_visibility INTERFACE) + target_compile_options(restore_default_visibility INTERFACE -fvisibility=default) + target_link_libraries(mymodule PRIVATE restore_default_visibility) + + +Local STL container bindings +---------------------------- + +Previous pybind11 versions could only bind types globally -- all pybind11 +modules, even unrelated ones, would have access to the same exported types. +However, this would also result in a conflict if two modules exported the +same C++ type, which is especially problematic for very common types, e.g. +``std::vector``. :ref:`module_local` were added to resolve this (see +that section for a complete usage guide). + +``py::class_`` still defaults to global bindings (because these types are +usually unique across modules), however in order to avoid clashes of opaque +types, ``py::bind_vector`` and ``py::bind_map`` will now bind STL containers +as ``py::module_local`` if their elements are: builtins (``int``, ``float``, +etc.), not bound using ``py::class_``, or bound as ``py::module_local``. For +example, this change allows multiple modules to bind ``std::vector`` +without causing conflicts. See :ref:`stl_bind` for more details. + +When upgrading to this version, if you have multiple modules which depend on +a single global binding of an STL container, note that all modules can still +accept foreign ``py::module_local`` types in the direction of Python-to-C++. +The locality only affects the C++-to-Python direction. If this is needed in +multiple modules, you'll need to either: + +* Add a copy of the same STL binding to all of the modules which need it. + +* Restore the global status of that single binding by marking it + ``py::module_local(false)``. + +The latter is an easy workaround, but in the long run it would be best to +localize all common type bindings in order to avoid conflicts with +third-party modules. + + +Negative strides for Python buffer objects and numpy arrays +----------------------------------------------------------- + +Support for negative strides required changing the integer type from unsigned +to signed in the interfaces of ``py::buffer_info`` and ``py::array``. If you +have compiler warnings enabled, you may notice some new conversion warnings +after upgrading. These can be resolved using ``static_cast``. + + +Deprecation of some ``py::object`` APIs +--------------------------------------- + +To compare ``py::object`` instances by pointer, you should now use +``obj1.is(obj2)`` which is equivalent to ``obj1 is obj2`` in Python. +Previously, pybind11 used ``operator==`` for this (``obj1 == obj2``), but +that could be confusing and is now deprecated (so that it can eventually +be replaced with proper rich object comparison in a future release). + +For classes which inherit from ``py::object``, ``borrowed`` and ``stolen`` +were previously available as protected constructor tags. Now the types +should be used directly instead: ``borrowed_t{}`` and ``stolen_t{}`` +(`#771 `_). + + +Stricter compile-time error checking +------------------------------------ + +Some error checks have been moved from run time to compile time. Notably, +automatic conversion of ``std::shared_ptr`` is not possible when ``T`` is +not directly registered with ``py::class_`` (e.g. ``std::shared_ptr`` +or ``std::shared_ptr>`` are not automatically convertible). +Attempting to bind a function with such arguments now results in a compile-time +error instead of waiting to fail at run time. + +``py::init<...>()`` constructor definitions are also stricter and now prevent +bindings which could cause unexpected behavior: + +.. code-block:: cpp + + struct Example { + Example(int &); + }; + + py::class_(m, "Example") + .def(py::init()); // OK, exact match + // .def(py::init()); // compile-time error, mismatch + +A non-``const`` lvalue reference is not allowed to bind to an rvalue. However, +note that a constructor taking ``const T &`` can still be registered using +``py::init()`` because a ``const`` lvalue reference can bind to an rvalue. + +v2.1 +==== + +Minimum compiler versions are enforced at compile time +------------------------------------------------------ + +The minimums also apply to v2.0 but the check is now explicit and a compile-time +error is raised if the compiler does not meet the requirements: + +* GCC >= 4.8 +* clang >= 3.3 (appleclang >= 5.0) +* MSVC >= 2015u3 +* Intel C++ >= 15.0 + + +The ``py::metaclass`` attribute is not required for static properties +--------------------------------------------------------------------- + +Binding classes with static properties is now possible by default. The +zero-parameter version of ``py::metaclass()`` is deprecated. However, a new +one-parameter ``py::metaclass(python_type)`` version was added for rare +cases when a custom metaclass is needed to override pybind11's default. + +.. code-block:: cpp + + // old -- emits a deprecation warning + py::class_(m, "Foo", py::metaclass()) + .def_property_readonly_static("foo", ...); + + // new -- static properties work without the attribute + py::class_(m, "Foo") + .def_property_readonly_static("foo", ...); + + // new -- advanced feature, override pybind11's default metaclass + py::class_(m, "Bar", py::metaclass(custom_python_type)) + ... + + +v2.0 +==== + +Breaking changes in ``py::class_`` +---------------------------------- + +These changes were necessary to make type definitions in pybind11 +future-proof, to support PyPy via its ``cpyext`` mechanism (`#527 +`_), and to improve efficiency +(`rev. 86d825 `_). + +1. Declarations of types that provide access via the buffer protocol must + now include the ``py::buffer_protocol()`` annotation as an argument to + the ``py::class_`` constructor. + + .. code-block:: cpp + + py::class_("Matrix", py::buffer_protocol()) + .def(py::init<...>()) + .def_buffer(...); + +2. Classes which include static properties (e.g. ``def_readwrite_static()``) + must now include the ``py::metaclass()`` attribute. Note: this requirement + has since been removed in v2.1. If you're upgrading from 1.x, it's + recommended to skip directly to v2.1 or newer. + +3. This version of pybind11 uses a redesigned mechanism for instantiating + trampoline classes that are used to override virtual methods from within + Python. This led to the following user-visible syntax change: + + .. code-block:: cpp + + // old v1.x syntax + py::class_("MyClass") + .alias() + ... + + // new v2.x syntax + py::class_("MyClass") + ... + + Importantly, both the original and the trampoline class are now specified + as arguments to the ``py::class_`` template, and the ``alias<..>()`` call + is gone. The new scheme has zero overhead in cases when Python doesn't + override any functions of the underlying C++ class. + `rev. 86d825 `_. + + The class type must be the first template argument given to ``py::class_`` + while the trampoline can be mixed in arbitrary order with other arguments + (see the following section). + + +Deprecation of the ``py::base()`` attribute +---------------------------------------------- + +``py::base()`` was deprecated in favor of specifying ``T`` as a template +argument to ``py::class_``. This new syntax also supports multiple inheritance. +Note that, while the type being exported must be the first argument in the +``py::class_`` template, the order of the following types (bases, +holder and/or trampoline) is not important. + +.. code-block:: cpp + + // old v1.x + py::class_("Derived", py::base()); + + // new v2.x + py::class_("Derived"); + + // new -- multiple inheritance + py::class_("Derived"); + + // new -- apart from `Derived` the argument order can be arbitrary + py::class_("Derived"); + + +Out-of-the-box support for ``std::shared_ptr`` +---------------------------------------------- + +The relevant type caster is now built in, so it's no longer necessary to +include a declaration of the form: + +.. code-block:: cpp + + PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) + +Continuing to do so won’t cause an error or even a deprecation warning, +but it's completely redundant. + + +Deprecation of a few ``py::object`` APIs +---------------------------------------- + +All of the old-style calls emit deprecation warnings. + ++---------------------------------------+---------------------------------------------+ +| Old syntax | New syntax | ++=======================================+=============================================+ +| ``obj.call(args...)`` | ``obj(args...)`` | ++---------------------------------------+---------------------------------------------+ +| ``obj.str()`` | ``py::str(obj)`` | ++---------------------------------------+---------------------------------------------+ +| ``auto l = py::list(obj); l.check()`` | ``py::isinstance(obj)`` | ++---------------------------------------+---------------------------------------------+ +| ``py::object(ptr, true)`` | ``py::reinterpret_borrow(ptr)`` | ++---------------------------------------+---------------------------------------------+ +| ``py::object(ptr, false)`` | ``py::reinterpret_steal(ptr)`` | ++---------------------------------------+---------------------------------------------+ +| ``if (obj.attr("foo"))`` | ``if (py::hasattr(obj, "foo"))`` | ++---------------------------------------+---------------------------------------------+ +| ``if (obj["bar"])`` | ``if (obj.contains("bar"))`` | ++---------------------------------------+---------------------------------------------+ diff --git a/external/pybind11/include/pybind11/attr.h b/external/pybind11/include/pybind11/attr.h new file mode 100644 index 0000000..dce875a --- /dev/null +++ b/external/pybind11/include/pybind11/attr.h @@ -0,0 +1,489 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "cast.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for operators +struct is_operator { }; + +/// Annotation for parent scope +struct scope { handle value; scope(const handle &s) : value(s) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { + PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() { } +}; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance { }; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr { }; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol { }; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() {} + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) { } +}; + +/// Annotation that marks a class as local to the module: +struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic { }; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template struct call_guard; + +template <> struct call_guard<> { using type = detail::void_type; }; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + function_record() + : is_constructor(false), is_new_style_constructor(false), is_stateless(false), + is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = { }; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a new-style `__init__` defined in `detail/init.h` + bool is_new_style_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True if this is a method + bool is_method : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { } + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = ::operator new; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(detail::value_and_holder &) = nullptr; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the default (unique_ptr) holder type used? + bool default_holder : 1; + + /// Is the class definition local to the module shared object? + bool module_local : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + if (default_holder != base_info->default_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + + if (caster) + base_info->implicit_casts.emplace_back(type, caster); + } +}; + +inline function_call::function_call(function_record &f, handle p) : + func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/// Tag for a new-style `__init__` defined in `detail/init.h` +struct is_new_style_constructor { }; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(function_call &) { } + static void postcall(function_call &, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +template <> struct process_attribute : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +}; + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + + if (!a.value) { +#if !defined(NDEBUG) + std::string descr("'"); + if (a.name) descr += std::string(a.name) + ": "; + descr += a.type + "'"; + if (r->is_method) { + if (r->name) + descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; + else + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + + descr + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "Compile in debug mode for more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +template +struct process_attribute::value>> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const module_local &l, type_record *r) { r->module_local = l.value; } +}; + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> { }; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + template = 0> + static void postcall(function_call &, handle) { } + template = 0> + static void precall(function_call &) { } + template = 0> + static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } +}; + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void precall(function_call &call) { + int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; + ignore_unused(unused); + } + static void postcall(function_call &call, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; + ignore_unused(unused); + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + return named == 0 || (self + named + has_args + has_kwargs) == nargs; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/buffer_info.h b/external/pybind11/include/pybind11/buffer_info.h new file mode 100644 index 0000000..9f072fa --- /dev/null +++ b/external/pybind11/include/pybind11/buffer_info.h @@ -0,0 +1,108 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of entries between adjacent entries (for each per dimension) + + buffer_info() { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container shape_in, detail::any_container strides_in) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + for (size_t i = 0; i < (size_t) ndim; ++i) + size *= shape[i]; + } + + template + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } + + template + buffer_info(T *ptr, ssize_t size) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) { + this->view = view; + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info& operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) { + (*this) = std::move(other); + } + + buffer_info& operator=(buffer_info &&rhs) { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(view, rhs.view); + std::swap(ownview, rhs.ownview); + return *this; + } + + ~buffer_info() { + if (view && ownview) { PyBuffer_Release(view); delete view; } + } + +private: + struct private_ctr_tag { }; + + buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container &&shape_in, detail::any_container &&strides_in) + : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } + + Py_buffer *view = nullptr; + bool ownview = false; +}; + +NAMESPACE_BEGIN(detail) + +template struct compare_buffer_info { + static bool compare(const buffer_info& b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || + ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || + ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/cast.h b/external/pybind11/include/pybind11/cast.h new file mode 100644 index 0000000..eab904b --- /dev/null +++ b/external/pybind11/include/pybind11/cast.h @@ -0,0 +1,2053 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pytypes.h" +#include "detail/typeid.h" +#include "detail/descr.h" +#include "detail/internals.h" +#include +#include +#include + +#if defined(PYBIND11_CPP17) +# if defined(__has_include) +# if __has_include() +# define PYBIND11_HAS_STRING_VIEW +# endif +# elif defined(_MSC_VER) +# define PYBIND11_HAS_STRING_VIEW +# endif +#endif +#ifdef PYBIND11_HAS_STRING_VIEW +#include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// A life support system for temporary objects created by `type_caster::load()`. +/// Adding a patient will keep it alive up until the enclosing function returns. +class loader_life_support { +public: + /// A new patient frame is created when a function is entered + loader_life_support() { + get_internals().loader_patient_stack.push_back(nullptr); + } + + /// ... and destroyed after it returns + ~loader_life_support() { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + pybind11_fail("loader_life_support: internal error"); + + auto ptr = stack.back(); + stack.pop_back(); + Py_CLEAR(ptr); + + // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) + if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) + stack.shrink_to_fit(); + } + + /// This can only be used inside a pybind11-bound function, either by `argument_loader` + /// at argument preparation time or by `py::cast()` at execution time. + PYBIND11_NOINLINE static void add_patient(handle h) { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + throw cast_error("When called outside a bound function, py::cast() cannot " + "do Python -> C++ conversions which require the creation " + "of temporary values"); + + auto &list_ptr = stack.back(); + if (list_ptr == nullptr) { + list_ptr = PyList_New(1); + if (!list_ptr) + pybind11_fail("loader_life_support: error allocating list"); + PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); + } else { + auto result = PyList_Append(list_ptr, h.ptr()); + if (result == -1) + pybind11_fail("loader_life_support: error adding patient"); + } + } +}; + +// Gets the cache entry for the given type, creating it if necessary. The return value is the pair +// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was +// just created. +inline std::pair all_type_info_get_cache(PyTypeObject *type); + +// Populates a just-created cache entry. +PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { + std::vector check; + for (handle parent : reinterpret_borrow(t->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + + auto const &type_dict = get_internals().registered_types_py; + for (size_t i = 0; i < check.size(); i++) { + auto type = check[i]; + // Ignore Python2 old-style class super type: + if (!PyType_Check((PyObject *) type)) continue; + + // Check `type` in the current set of registered python types: + auto it = type_dict.find(type); + if (it != type_dict.end()) { + // We found a cache entry for it, so it's either pybind-registered or has pre-computed + // pybind bases, but we have to make sure we haven't already seen the type(s) before: we + // want to follow Python/virtual C++ rules that there should only be one instance of a + // common base. + for (auto *tinfo : it->second) { + // NB: Could use a second set here, rather than doing a linear search, but since + // having a large number of immediate pybind11-registered types seems fairly + // unlikely, that probably isn't worthwhile. + bool found = false; + for (auto *known : bases) { + if (known == tinfo) { found = true; break; } + } + if (!found) bases.push_back(tinfo); + } + } + else if (type->tp_bases) { + // It's some python type, so keep follow its bases classes to look for one or more + // registered types + if (i + 1 == check.size()) { + // When we're at the end, we can pop off the current element to avoid growing + // `check` when adding just one base (which is typical--i.e. when there is no + // multiple inheritance) + check.pop_back(); + i--; + } + for (handle parent : reinterpret_borrow(type->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + } + } +} + +/** + * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will + * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side + * derived class that uses single inheritance. Will contain as many types as required for a Python + * class that uses multiple inheritance to inherit (directly or indirectly) from multiple + * pybind-registered classes. Will be empty if neither the type nor any base classes are + * pybind-registered. + * + * The value is cached for the lifetime of the Python type. + */ +inline const std::vector &all_type_info(PyTypeObject *type) { + auto ins = all_type_info_get_cache(type); + if (ins.second) + // New cache entry: populate it + all_type_info_populate(type, ins.first->second); + + return ins.first->second; +} + +/** + * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any + * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use + * `all_type_info` instead if you want to support multiple bases. + */ +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto &bases = all_type_info(type); + if (bases.size() == 0) + return nullptr; + if (bases.size() > 1) + pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); + return bases.front(); +} + +inline detail::type_info *get_local_type_info(const std::type_index &tp) { + auto &locals = registered_local_types_cpp(); + auto it = locals.find(tp); + if (it != locals.end()) + return it->second; + return nullptr; +} + +inline detail::type_info *get_global_type_info(const std::type_index &tp) { + auto &types = get_internals().registered_types_cpp; + auto it = types.find(tp); + if (it != types.end()) + return it->second; + return nullptr; +} + +/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, + bool throw_if_missing = false) { + if (auto ltype = get_local_type_info(tp)) + return ltype; + if (auto gtype = get_global_type_info(tp)) + return gtype; + + if (throw_if_missing) { + std::string tname = tp.name(); + detail::clean_type_id(tname); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); + } + return nullptr; +} + +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { + detail::type_info *type_info = get_type_info(tp, throw_if_missing); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +struct value_and_holder { + instance *inst; + size_t index; + const detail::type_info *type; + void **vh; + + // Main constructor for a found value/holder: + value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : + inst{i}, index{index}, type{type}, + vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} + {} + + // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) + value_and_holder() : inst{nullptr} {} + + // Used for past-the-end iterator + value_and_holder(size_t index) : index{index} {} + + template V *&value_ptr() const { + return reinterpret_cast(vh[0]); + } + // True if this `value_and_holder` has a non-null value pointer + explicit operator bool() const { return value_ptr(); } + + template H &holder() const { + return reinterpret_cast(vh[1]); + } + bool holder_constructed() const { + return inst->simple_layout + ? inst->simple_holder_constructed + : inst->nonsimple.status[index] & instance::status_holder_constructed; + } + void set_holder_constructed(bool v = true) { + if (inst->simple_layout) + inst->simple_holder_constructed = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_holder_constructed; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; + } + bool instance_registered() const { + return inst->simple_layout + ? inst->simple_instance_registered + : inst->nonsimple.status[index] & instance::status_instance_registered; + } + void set_instance_registered(bool v = true) { + if (inst->simple_layout) + inst->simple_instance_registered = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_instance_registered; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; + } +}; + +// Container for accessing and iterating over an instance's values/holders +struct values_and_holders { +private: + instance *inst; + using type_vec = std::vector; + const type_vec &tinfo; + +public: + values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} + + struct iterator { + private: + instance *inst; + const type_vec *types; + value_and_holder curr; + friend struct values_and_holders; + iterator(instance *inst, const type_vec *tinfo) + : inst{inst}, types{tinfo}, + curr(inst /* instance */, + types->empty() ? nullptr : (*types)[0] /* type info */, + 0, /* vpos: (non-simple types only): the first vptr comes first */ + 0 /* index */) + {} + // Past-the-end iterator: + iterator(size_t end) : curr(end) {} + public: + bool operator==(const iterator &other) { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) { return curr.index != other.curr.index; } + iterator &operator++() { + if (!inst->simple_layout) + curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; + ++curr.index; + curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; + return *this; + } + value_and_holder &operator*() { return curr; } + value_and_holder *operator->() { return &curr; } + }; + + iterator begin() { return iterator(inst, &tinfo); } + iterator end() { return iterator(tinfo.size()); } + + iterator find(const type_info *find_type) { + auto it = begin(), endit = end(); + while (it != endit && it->type != find_type) ++it; + return it; + } + + size_t size() { return tinfo.size(); } +}; + +/** + * Extracts C++ value and holder pointer references from an instance (which may contain multiple + * values/holders for python-side multiple inheritance) that match the given type. Throws an error + * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If + * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, + * regardless of type (and the resulting .type will be nullptr). + * + * The returned object should be short-lived: in particular, it must not outlive the called-upon + * instance. + */ +PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { + // Optimize common case: + if (!find_type || Py_TYPE(this) == find_type->type) + return value_and_holder(this, find_type, 0, 0); + + detail::values_and_holders vhs(this); + auto it = vhs.find(find_type); + if (it != vhs.end()) + return *it; + + if (!throw_if_missing) + return value_and_holder(); + +#if defined(NDEBUG) + pybind11_fail("pybind11::detail::instance::get_value_and_holder: " + "type is not a pybind11 base of the given instance " + "(compile in debug mode for type details)"); +#else + pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + + std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + + std::string(Py_TYPE(this)->tp_name) + "' instance"); +#endif +} + +PYBIND11_NOINLINE inline void instance::allocate_layout() { + auto &tinfo = all_type_info(Py_TYPE(this)); + + const size_t n_types = tinfo.size(); + + if (n_types == 0) + pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); + + simple_layout = + n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); + + // Simple path: no python-side multiple inheritance, and a small-enough holder + if (simple_layout) { + simple_value_holder[0] = nullptr; + simple_holder_constructed = false; + simple_instance_registered = false; + } + else { // multiple base types or a too-large holder + // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, + // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool + // values that tracks whether each associated holder has been initialized. Each [block] is + // padded, if necessary, to an integer multiple of sizeof(void *). + size_t space = 0; + for (auto t : tinfo) { + space += 1; // value pointer + space += t->holder_size_in_ptrs; // holder instance + } + size_t flags_at = space; + space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) + + // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, + // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 + // they default to using pymalloc, which is designed to be efficient for small allocations + // like the one we're doing here; in earlier versions (and for larger allocations) they are + // just wrappers around malloc. +#if PY_VERSION_HEX >= 0x03050000 + nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); +#else + nonsimple.values_and_holders = (void **) PyMem_New(void *, space); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); + std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); +#endif + nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); + } + owned = true; +} + +PYBIND11_NOINLINE inline void instance::deallocate_layout() { + if (!simple_layout) + PyMem_Free(nonsimple.values_and_holders); +} + +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + handle type = detail::get_type_handle(tp, false); + if (!type) + return false; + return isinstance(obj, type); +} + +PYBIND11_NOINLINE inline std::string error_string() { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); + return "Unknown internal error occurred"; + } + + error_scope scope; // Preserve error state + + std::string errorString; + if (scope.type) { + errorString += handle(scope.type).attr("__name__").cast(); + errorString += ": "; + } + if (scope.value) + errorString += (std::string) str(scope.value); + + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + +#if PY_MAJOR_VERSION >= 3 + if (scope.trace != nullptr) + PyException_SetTraceback(scope.value, scope.trace); +#endif + +#if !defined(PYPY_VERSION) + if (scope.trace) { + PyTracebackObject *trace = (PyTracebackObject *) scope.trace; + + /* Get the deepest trace possible */ + while (trace->tb_next) + trace = trace->tb_next; + + PyFrameObject *frame = trace->tb_frame; + errorString += "\n\nAt:\n"; + while (frame) { + int lineno = PyFrame_GetLineNumber(frame); + errorString += + " " + handle(frame->f_code->co_filename).cast() + + "(" + std::to_string(lineno) + "): " + + handle(frame->f_code->co_name).cast() + "\n"; + frame = frame->f_back; + } + } +#endif + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { + auto &instances = get_internals().registered_instances; + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + for (auto vh : values_and_holders(it->second)) { + if (vh.type == type) + return handle((PyObject *) it->second); + } + } + return handle(); +} + +inline PyThreadState *get_thread_state_unchecked() { +#if defined(PYPY_VERSION) + return PyThreadState_GET(); +#elif PY_VERSION_HEX < 0x03000000 + return _PyThreadState_Current; +#elif PY_VERSION_HEX < 0x03050000 + return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); +#elif PY_VERSION_HEX < 0x03050200 + return (PyThreadState*) _PyThreadState_Current.value; +#else + return _PyThreadState_UncheckedGet(); +#endif +} + +// Forward declarations +inline void keep_alive_impl(handle nurse, handle patient); +inline PyObject *make_new_instance(PyTypeObject *type); + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } + + type_caster_generic(const type_info *typeinfo) + : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } + + bool load(handle src, bool convert) { + return load_impl(src, convert); + } + + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const detail::type_info *tinfo, + void *(*copy_constructor)(const void *), + void *(*move_constructor)(const void *), + const void *existing_holder = nullptr) { + if (!tinfo) // no type info: error will be set already + return handle(); + + void *src = const_cast(_src); + if (src == nullptr) + return none().release(); + + auto it_instances = get_internals().registered_instances.equal_range(src); + for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { + for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { + if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) + return handle((PyObject *) it_i->second).inc_ref(); + } + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type)); + auto wrapper = reinterpret_cast(inst.ptr()); + wrapper->owned = false; + void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); + + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::take_ownership: + valueptr = src; + wrapper->owned = true; + break; + + case return_value_policy::automatic_reference: + case return_value_policy::reference: + valueptr = src; + wrapper->owned = false; + break; + + case return_value_policy::copy: + if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = copy, but the " + "object is non-copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::move: + if (move_constructor) + valueptr = move_constructor(src); + else if (copy_constructor) + valueptr = copy_constructor(src); + else + throw cast_error("return_value_policy = move, but the " + "object is neither movable nor copyable!"); + wrapper->owned = true; + break; + + case return_value_policy::reference_internal: + valueptr = src; + wrapper->owned = false; + keep_alive_impl(inst, parent); + break; + + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + } + + tinfo->init_instance(wrapper, existing_holder); + + return inst.release(); + } + + // Base methods for generic caster; there are overridden in copyable_holder_caster + void load_value(value_and_holder &&v_h) { + auto *&vptr = v_h.value_ptr(); + // Lazy allocation for unallocated values: + if (vptr == nullptr) { + auto *type = v_h.type ? v_h.type : typeinfo; + vptr = type->operator_new(type->type_size); + } + value = vptr; + } + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + type_caster_generic sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + return true; + } + } + return false; + } + bool try_direct_conversions(handle src) { + for (auto &converter : *typeinfo->direct_conversions) { + if (converter(src.ptr(), value)) + return true; + } + return false; + } + void check_holder_compat() {} + + PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { + auto caster = type_caster_generic(ti); + if (caster.load(src, false)) + return caster.value; + return nullptr; + } + + /// Try to load with foreign typeinfo, if available. Used when there is no + /// native typeinfo, or when the native one wasn't able to produce a value. + PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { + constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; + const auto pytype = src.get_type(); + if (!hasattr(pytype, local_key)) + return false; + + type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); + // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type + if (foreign_typeinfo->module_local_load == &local_load + || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) + return false; + + if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { + value = result; + return true; + } + return false; + } + + // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant + // bits of code between here and copyable_holder_caster where the two classes need different + // logic (without having to resort to virtual inheritance). + template + PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { + if (!src) return false; + if (!typeinfo) return try_load_foreign_module_local(src); + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + value = nullptr; + return true; + } + + auto &this_ = static_cast(*this); + this_.check_holder_compat(); + + PyTypeObject *srctype = Py_TYPE(src.ptr()); + + // Case 1: If src is an exact type match for the target type then we can reinterpret_cast + // the instance's value pointer to the target type: + if (srctype == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2: We have a derived class + else if (PyType_IsSubtype(srctype, typeinfo->type)) { + auto &bases = all_type_info(srctype); + bool no_cpp_mi = typeinfo->simple_type; + + // Case 2a: the python type is a Python-inherited derived class that inherits from just + // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of + // the right type and we can use reinterpret_cast. + // (This is essentially the same as case 2b, but because not using multiple inheritance + // is extremely common, we handle it specially to avoid the loop iterator and type + // pointer lookup overhead) + if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if + // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we + // can safely reinterpret_cast to the relevant pointer. + else if (bases.size() > 1) { + for (auto base : bases) { + if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); + return true; + } + } + } + + // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match + // in the registered bases, above, so try implicit casting (needed for proper C++ casting + // when MI is involved). + if (this_.try_implicit_casts(src, convert)) + return true; + } + + // Perform an implicit conversion + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); + if (load_impl(temp, false)) { + loader_life_support::add_patient(temp); + return true; + } + } + if (this_.try_direct_conversions(src)) + return true; + } + + // Failed to match local typeinfo. Try again with global. + if (typeinfo->module_local) { + if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { + typeinfo = gtype; + return load(src, false); + } + } + + // Global typeinfo has precedence over foreign module_local + return try_load_foreign_module_local(src); + } + + + // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast + // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair + // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). + PYBIND11_NOINLINE static std::pair src_and_type( + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { + if (auto *tpi = get_type_info(cast_type)) + return {src, const_cast(tpi)}; + + // Not found, set error: + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); + detail::clean_type_id(tname); + std::string msg = "Unregistered type : " + tname; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return {nullptr, nullptr}; + } + + const type_info *typeinfo = nullptr; + const std::type_info *cpptype = nullptr; + void *value = nullptr; +}; + +/** + * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster + * needs to provide `operator T*()` and `operator T&()` operators. + * + * If the type supports moving the value away via an `operator T&&() &&` method, it should use + * `movable_cast_op_type` instead. + */ +template +using cast_op_type = + conditional_t>::value, + typename std::add_pointer>::type, + typename std::add_lvalue_reference>::type>; + +/** + * Determine suitable casting operator for a type caster with a movable value. Such a type caster + * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be + * called in appropriate contexts where the value can be moved rather than copied. + * + * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. + */ +template +using movable_cast_op_type = + conditional_t::type>::value, + typename std::add_pointer>::type, + conditional_t::value, + typename std::add_rvalue_reference>::type, + typename std::add_lvalue_reference>::type>>; + +// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when +// T is non-copyable, but code containing such a copy constructor fails to actually compile. +template struct is_copy_constructible : std::is_copy_constructible {}; + +// Specialization for types that appear to be copy constructible but also look like stl containers +// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if +// so, copy constructability depends on whether the value_type is copy constructible. +template struct is_copy_constructible, + std::is_same + >::value>> : is_copy_constructible {}; + +#if !defined(PYBIND11_CPP17) +// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the +// two types aren't themselves copy constructible). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; +#endif + +/// Generic type caster for objects stored on the heap +template class type_caster_base : public type_caster_generic { + using itype = intrinsic_t; +public: + static PYBIND11_DESCR name() { return type_descr(_()); } + + type_caster_base() : type_caster_base(typeid(type)) { } + explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } + + static handle cast(const itype &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + + static handle cast(itype &&src, return_value_policy, handle parent) { + return cast(&src, return_value_policy::move, parent); + } + + // Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a + // polymorphic type. If the instance isn't derived, returns the non-RTTI base version. + template ::value, int> = 0> + static std::pair src_and_type(const itype *src) { + const void *vsrc = src; + auto &cast_type = typeid(itype); + const std::type_info *instance_type = nullptr; + if (vsrc) { + instance_type = &typeid(*src); + if (!same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type; if it is a pybind11-registered type, we + // can get the correct derived pointer (which may be != base pointer) by a + // dynamic_cast to most derived type: + if (auto *tpi = get_type_info(*instance_type)) + return {dynamic_cast(src), const_cast(tpi)}; + } + } + // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so + // don't do a cast + return type_caster_generic::src_and_type(vsrc, cast_type, instance_type); + } + + // Non-polymorphic type, so no dynamic casting; just call the generic version directly + template ::value, int> = 0> + static std::pair src_and_type(const itype *src) { + return type_caster_generic::src_and_type(src, typeid(itype)); + } + + static handle cast(const itype *src, return_value_policy policy, handle parent) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, policy, parent, st.second, + make_copy_constructor(src), make_move_constructor(src)); + } + + static handle cast_holder(const itype *src, const void *holder) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, return_value_policy::take_ownership, {}, st.second, + nullptr, nullptr, holder); + } + + template using cast_op_type = cast_op_type; + + operator itype*() { return (type *) value; } + operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } + +protected: + using Constructor = void *(*)(const void *); + + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementation. */ + template ::value>> + static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { + return [](const void *arg) -> void * { + return new T(*reinterpret_cast(arg)); + }; + } + + template ::value>> + static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { + return [](const void *arg) -> void * { + return new T(std::move(*const_cast(reinterpret_cast(arg)))); + }; + } + + static Constructor make_copy_constructor(...) { return nullptr; } + static Constructor make_move_constructor(...) { return nullptr; } +}; + +template class type_caster : public type_caster_base { }; +template using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator + typename make_caster::template cast_op_type::type>(); +} + +template class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + static_assert(std::is_same::type &, subcaster_cast_op_type>::value, + "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static PYBIND11_DESCR name() { return caster_t::name(); } + static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + policy = return_value_policy::automatic_reference; + return caster_t::cast(&src.get(), policy, parent); + } + template using cast_op_type = std::reference_wrapper; + operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ + protected: \ + type value; \ + public: \ + static PYBIND11_DESCR name() { return type_descr(py_name); } \ + template >::value, int> = 0> \ + static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + if (!src) return none().release(); \ + if (policy == return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); delete src; return h; \ + } else { \ + return cast(*src, policy, parent); \ + } \ + } \ + operator type*() { return &value; } \ + operator type&() { return value; } \ + operator type&&() && { return std::move(value); } \ + template using cast_op_type = pybind11::detail::movable_cast_op_type + + +template using is_std_char_type = any_of< + std::is_same, /* std::string */ + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ +>; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; +public: + + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) + return false; + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + else + return false; + } else if (PyFloat_Check(src.ptr())) { + return false; + } else if (std::is_unsigned::value) { + py_value = as_unsigned(src.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } + + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && + (py_value < (py_type) std::numeric_limits::min() || + py_value > (py_type) std::numeric_limits::max()))) { + bool type_error = py_err && PyErr_ExceptionMatches( +#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) + PyExc_SystemError +#else + PyExc_TypeError +#endif + ); + PyErr_Clear(); + if (type_error && convert && PyNumber_Check(src.ptr())) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + static handle cast(T src, return_value_policy /* policy */, handle /* parent */) { + if (std::is_floating_point::value) { + return PyFloat_FromDouble((double) src); + } else if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + return PyLong_FromLong((long) src); + else + return PyLong_FromUnsignedLong((unsigned long) src); + } else { + if (std::is_signed::value) + return PyLong_FromLongLong((long long) src); + else + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + } + + PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); +}; + +template struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) + return true; + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().inc_ref(); + } + PYBIND11_TYPE_CASTER(T, _("None")); +}; + +template <> class type_caster : public void_caster {}; + +template <> class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } else if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) + return capsule(ptr).release(); + else + return none().inc_ref(); + } + + template using cast_op_type = void*&; + operator void *&() { return value; } + static PYBIND11_DESCR name() { return type_descr(_("capsule")); } +private: + void *value = nullptr; +}; + +template <> class type_caster : public void_caster { }; + +template <> class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) return false; + else if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == Py_False) { value = false; return true; } + else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } + #if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } + #else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } + #endif + if (res == 0 || res == 1) { + value = (bool) res; + return true; + } + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, _("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { +#if PY_MAJOR_VERSION < 3 + object temp; +#endif + handle load_src = src; + if (!src) { + return false; + } else if (!PyUnicode_Check(load_src.ptr())) { +#if PY_MAJOR_VERSION >= 3 + return load_bytes(load_src); +#else + if (sizeof(CharT) == 1) { + return load_bytes(load_src); + } + + // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false + if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + return false; + + temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); + if (!temp) { PyErr_Clear(); return false; } + load_src = temp; +#endif + } + + object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( + load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); + if (!utfNbytes) { PyErr_Clear(); return false; } + + const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) + loader_life_support::add_patient(utfNbytes); + + return true; + } + + static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) throw error_already_set(); + return s; + } + + PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return + UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : + UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : + PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version + // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a + // non-const char * arguments, which is also a nuissance, so bypass the whole thing by just + // passing the encoding as a string value, which works properly: + return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_bytes(enable_if_t src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (bytes) { + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + } + + return false; + } + + template + bool load_bytes(enable_if_t) { return false; } +}; + +template +struct type_caster, enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = type_caster; + StringCaster str_caster; + bool none = false; +public: + bool load(handle src, bool convert) { + if (!src) return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) return pybind11::none().inc_ref(); + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) throw error_already_set(); + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } + operator CharT() { + if (none) + throw value_error("Cannot convert None to a character"); + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) + throw value_error("Cannot convert empty string to a character"); + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to figure + // out how long the first encoded character is in bytes to distinguish between these two + // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those + // can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + unsigned char v0 = static_cast(value[0]); + size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 + (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence + (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence + 4; // 0b11110xxx - start of 4-byte sequence + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + return static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + char16_t v0 = static_cast(value[0]); + if (v0 >= 0xD800 && v0 < 0xE000) + throw value_error("Character code point not in range(0x10000)"); + } + + if (str_len != 1) + throw value_error("Expected a character, but multi-character string found"); + + return value[0]; + } + + static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } + template using cast_op_type = remove_reference_t>; +}; + +// Base implementation for std::tuple and std::pair +template class Tuple, typename... Ts> class tuple_caster { + using type = Tuple; + static constexpr auto size = sizeof...(Ts); + using indices = make_index_sequence; +public: + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + const auto seq = reinterpret_borrow(src); + if (seq.size() != size) + return false; + return load_impl(seq, convert, indices{}); + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + return cast_impl(std::forward(src), policy, parent, indices{}); + } + + static PYBIND11_DESCR name() { + return type_descr(_("Tuple[") + detail::concat(make_caster::name()...) + _("]")); + } + + template using cast_op_type = type; + + operator type() & { return implicit_cast(indices{}); } + operator type() && { return std::move(*this).implicit_cast(indices{}); } + +protected: + template + type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } + template + type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } + + static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } + + template + bool load_impl(const sequence &seq, bool convert, index_sequence) { + for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) + if (!r) + return false; + return true; + } + + /* Implementation: Convert a C++ tuple into a Python tuple */ + template + static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { + std::array entries{{ + reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... + }}; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); + int counter = 0; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); + } + + Tuple...> subcasters; +}; + +template class type_caster> + : public tuple_caster {}; + +template class type_caster> + : public tuple_caster {}; + +/// Helper class which abstracts away certain actions. Users can provide specializations for +/// custom holders, but it's only necessary if the type has a non-standard interface. +template +struct holder_helper { + static auto get(const T &p) -> decltype(p.get()) { return p.get(); } +}; + +/// Type caster for holder types like std::shared_ptr, etc. +template +struct copyable_holder_caster : public type_caster_base { +public: + using base = type_caster_base; + static_assert(std::is_base_of>::value, + "Holder classes are only supported for custom types"); + using base::base; + using base::cast; + using base::typeinfo; + using base::value; + + bool load(handle src, bool convert) { + return base::template load_impl>(src, convert); + } + + explicit operator type*() { return this->value; } + explicit operator type&() { return *(this->value); } + explicit operator holder_type*() { return &holder; } + + // Workaround for Intel compiler bug + // see pybind11 issue 94 + #if defined(__ICC) || defined(__INTEL_COMPILER) + operator holder_type&() { return holder; } + #else + explicit operator holder_type&() { return holder; } + #endif + + static handle cast(const holder_type &src, return_value_policy, handle) { + const auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + +protected: + friend class type_caster_generic; + void check_holder_compat() { + if (typeinfo->default_holder) + throw cast_error("Unable to load a custom holder type from a default-holder instance"); + } + + bool load_value(value_and_holder &&v_h) { + if (v_h.holder_constructed()) { + value = v_h.value_ptr(); + holder = v_h.holder(); + return true; + } else { + throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " +#if defined(NDEBUG) + "(compile in debug mode for type information)"); +#else + "of type '" + type_id() + "''"); +#endif + } + } + + template ::value, int> = 0> + bool try_implicit_casts(handle, bool) { return false; } + + template ::value, int> = 0> + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + copyable_holder_caster sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + holder = holder_type(sub_caster.holder, (type *) value); + return true; + } + } + return false; + } + + static bool try_direct_conversions(handle) { return false; } + + + holder_type holder; +}; + +/// Specialize for the common std::shared_ptr, so users don't need to +template +class type_caster> : public copyable_holder_caster> { }; + +template +struct move_only_holder_caster { + static_assert(std::is_base_of, type_caster>::value, + "Holder classes are only supported for custom types"); + + static handle cast(holder_type &&src, return_value_policy, handle) { + auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + static PYBIND11_DESCR name() { return type_caster_base::name(); } +}; + +template +class type_caster> + : public move_only_holder_caster> { }; + +template +using type_caster_holder = conditional_t::value, + copyable_holder_caster, + move_only_holder_caster>; + +template struct always_construct_holder { static constexpr bool value = Value; }; + +/// Create a specialization for custom holder types (silently ignores std::shared_ptr) +#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ + namespace pybind11 { namespace detail { \ + template \ + struct always_construct_holder : always_construct_holder { }; \ + template \ + class type_caster::value>> \ + : public type_caster_holder { }; \ + }} + +// PYBIND11_DECLARE_HOLDER_TYPE holder types: +template struct is_holder_type : + std::is_base_of, detail::type_caster> {}; +// Specialization for always-supported unique_ptr holders: +template struct is_holder_type> : + std::true_type {}; + +template struct handle_type_name { static PYBIND11_DESCR name() { return _(); } }; +template <> struct handle_type_name { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } }; +template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("*args"); } }; +template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("**kwargs"); } }; + +template +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } + + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = reinterpret_borrow(src); + return true; + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name()); +}; + +template +class type_caster::value>> : public pyobject_caster { }; + +// Our conditions for enabling moving are quite restrictive: +// At compile time: +// - T needs to be a non-const, non-pointer, non-reference type +// - type_caster::operator T&() must exist +// - the type must be move constructible (obviously) +// At run-time: +// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it +// must have ref_count() == 1)h +// If any of the above are not satisfied, we fall back to copying. +template using move_is_plain_type = satisfies_none_of; +template struct move_always : std::false_type {}; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template struct move_if_unreferenced : std::false_type {}; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; + +// Detect whether returning a `type` from a cast on type's type_caster is going to result in a +// reference or pointer to a local variable of the type_caster. Basically, only +// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; +// everything else returns a reference/pointer to a local variable. +template using cast_is_temporary_value_reference = bool_constant< + (std::is_reference::value || std::is_pointer::value) && + !std::is_base_of>::value +>; + +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by +// specializing this struct. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + +// Basic python -> C++ casting; throws if casting fails +template type_caster &load_type(type_caster &conv, const handle &handle) { + if (!conv.load(handle, true)) { +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); +#else + throw cast_error("Unable to cast Python instance of type " + + (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "''"); +#endif + } + return conv; +} +// Wrapper around the above that also constructs and returns a type_caster +template make_caster load_type(const handle &handle) { + make_caster conv; + load_type(conv, handle); + return conv; +} + +NAMESPACE_END(detail) + +// pytype -> C++ type +template ::value, int> = 0> +T cast(const handle &handle) { + using namespace detail; + static_assert(!cast_is_temporary_value_reference::value, + "Unable to cast type to reference: value is local to type caster"); + return cast_op(load_type(handle)); +} + +// pytype -> pytype (calls converting constructor) +template ::value, int> = 0> +T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } + +// C++ type -> py::object +template ::value, int> = 0> +object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, + handle parent = handle()) { + if (policy == return_value_policy::automatic) + policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; + else if (policy == return_value_policy::automatic_reference) + policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; + return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); +} + +template T handle::cast() const { return pybind11::cast(*this); } +template <> inline void handle::cast() const { return; } + +template +detail::enable_if_t::value, T> move(object &&obj) { + if (obj.ref_count() > 1) +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" + " (compile in debug mode for details)"); +#else + throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + + " instance to C++ " + type_id() + " instance: instance has multiple references"); +#endif + + // Move into a temporary and return that, because the reference may be a local value of `conv` + T ret = std::move(detail::load_type(obj).operator T&()); + return ret; +} + +// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: +// - If we have to move (because T has no copy constructor), do it. This will fail if the moved +// object has multiple references, but trying to copy will fail to compile. +// - If both movable and copyable, check ref count: if 1, move; otherwise copy +// - Otherwise (not movable), copy. +template detail::enable_if_t::value, T> cast(object &&object) { + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + if (object.ref_count() > 1) + return cast(object); + else + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + return cast(object); +} + +template T object::cast() const & { return pybind11::cast(*this); } +template T object::cast() && { return pybind11::cast(std::move(*this)); } +template <> inline void object::cast() const & { return; } +template <> inline void object::cast() && { return; } + +NAMESPACE_BEGIN(detail) + +// Declared in pytypes.h: +template ::value, int>> +object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } + +struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro +template using overload_caster_t = conditional_t< + cast_is_temporary_value_reference::value, make_caster, overload_unused>; + +// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then +// store the result in the given variable. For other types, this is a no-op. +template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { + return cast_op(load_type(caster, o)); +} +template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { + pybind11_fail("Internal error: cast_ref fallback invoked"); } + +// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even +// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in +// cases where pybind11::cast is valid. +template enable_if_t::value, T> cast_safe(object &&o) { + return pybind11::cast(std::move(o)); } +template enable_if_t::value, T> cast_safe(object &&) { + pybind11_fail("Internal error: cast_safe fallback invoked"); } +template <> inline void cast_safe(object &&) {} + +NAMESPACE_END(detail) + +template tuple make_tuple(Args&&... args_) { + constexpr size_t size = sizeof...(Args); + std::array args { + { reinterpret_steal(detail::make_caster::cast( + std::forward(args_), policy, nullptr))... } + }; + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { +#if defined(NDEBUG) + throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); +#else + std::array argtypes { {type_id()...} }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); +#endif + } + } + tuple result(size); + int counter = 0; + for (auto &arg_value : args) + PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); + return result; +} + +/// \ingroup annotations +/// Annotation for arguments +struct arg { + /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. + constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } + /// Assign a value to this argument + template arg_v operator=(T &&value) const; + /// Indicate that the type should not be converted in the type caster + arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } + /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) + arg &none(bool flag = true) { flag_none = flag; return *this; } + + const char *name; ///< If non-null, this is a named kwargs argument + bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) + bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument +}; + +/// \ingroup annotations +/// Annotation for arguments with values +struct arg_v : arg { +private: + template + arg_v(arg &&base, T &&x, const char *descr = nullptr) + : arg(base), + value(reinterpret_steal( + detail::make_caster::cast(x, return_value_policy::automatic, {}) + )), + descr(descr) +#if !defined(NDEBUG) + , type(type_id()) +#endif + { } + +public: + /// Direct construction with name, default, and description + template + arg_v(const char *name, T &&x, const char *descr = nullptr) + : arg_v(arg(name), std::forward(x), descr) { } + + /// Called internally when invoking `py::arg("a") = value` + template + arg_v(const arg &base, T &&x, const char *descr = nullptr) + : arg_v(arg(base), std::forward(x), descr) { } + + /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& + arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } + + /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& + arg_v &none(bool flag = true) { arg::none(flag); return *this; } + + /// The default value + object value; + /// The (optional) description of the default value + const char *descr; +#if !defined(NDEBUG) + /// The C++ type name of the default value (only available when compiled in debug mode) + std::string type; +#endif +}; + +template +arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } + +/// Alias for backward compatibility -- to be removed in version 2.0 +template using arg_t = arg_v; + +inline namespace literals { +/** \rst + String literal version of `arg` + \endrst */ +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +} + +NAMESPACE_BEGIN(detail) + +// forward declaration (definition in attr.h) +struct function_record; + +/// Internal data associated with a single function call +struct function_call { + function_call(function_record &f, handle p); // Implementation in attr.h + + /// The function data: + const function_record &func; + + /// Arguments passed to the function: + std::vector args; + + /// The `convert` value the arguments should be loaded with + std::vector args_convert; + + /// The parent, if any + handle parent; + + /// If this is a call to an initializer, this argument contains `self` + handle init_self; +}; + + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using indices = make_index_sequence; + + template using argument_is_args = std::is_same, args>; + template using argument_is_kwargs = std::is_same, kwargs>; + // Get args/kwargs argument positions relative to the end of the argument list: + static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), + kwargs_pos = constexpr_first() - (int) sizeof...(Args); + + static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; + + static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); + +public: + static constexpr bool has_kwargs = kwargs_pos < 0; + static constexpr bool has_args = args_pos < 0; + + static PYBIND11_DESCR arg_names() { return detail::concat(make_caster::name()...); } + + bool load_args(function_call &call) { + return load_impl_sequence(call, indices{}); + } + + template + enable_if_t::value, Return> call(Func &&f) && { + return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) && { + std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + return void_type(); + } + +private: + + static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + + template + bool load_impl_sequence(function_call &call, index_sequence) { + for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) + if (!r) + return false; + return true; + } + + template + Return call_impl(Func &&f, index_sequence, Guard &&) { + return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); + } + + std::tuple...> argcasters; +}; + +/// Helper class which collects only positional arguments for a Python function call. +/// A fancier version below can collect any argument, but this one is optimal for simple calls. +template +class simple_collector { +public: + template + explicit simple_collector(Ts &&...values) + : m_args(pybind11::make_tuple(std::forward(values)...)) { } + + const tuple &args() const & { return m_args; } + dict kwargs() const { return {}; } + + tuple args() && { return std::move(m_args); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + tuple m_args; +}; + +/// Helper class which collects positional, keyword, * and ** arguments for a Python function call +template +class unpacking_collector { +public: + template + explicit unpacking_collector(Ts &&...values) { + // Tuples aren't (easily) resizable so a list is needed for collection, + // but the actual function call strictly requires a tuple. + auto args_list = list(); + int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; + ignore_unused(_); + + m_args = std::move(args_list); + } + + const tuple &args() const & { return m_args; } + const dict &kwargs() const & { return m_kwargs; } + + tuple args() && { return std::move(m_args); } + dict kwargs() && { return std::move(m_kwargs); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + template + void process(list &args_list, T &&x) { + auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); + if (!o) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(std::to_string(args_list.size()), type_id()); +#endif + } + args_list.append(o); + } + + void process(list &args_list, detail::args_proxy ap) { + for (const auto &a : ap) + args_list.append(a); + } + + void process(list &/*args_list*/, arg_v a) { + if (!a.name) +#if defined(NDEBUG) + nameless_argument_error(); +#else + nameless_argument_error(a.type); +#endif + + if (m_kwargs.contains(a.name)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(a.name); +#endif + } + if (!a.value) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(a.name, a.type); +#endif + } + m_kwargs[a.name] = a.value; + } + + void process(list &/*args_list*/, detail::kwargs_proxy kp) { + if (!kp) + return; + for (const auto &k : reinterpret_borrow(kp)) { + if (m_kwargs.contains(k.first)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(str(k.first)); +#endif + } + m_kwargs[k.first] = k.second; + } + } + + [[noreturn]] static void nameless_argument_error() { + throw type_error("Got kwargs without a name; only named arguments " + "may be passed via py::arg() to a python function call. " + "(compile in debug mode for details)"); + } + [[noreturn]] static void nameless_argument_error(std::string type) { + throw type_error("Got kwargs without a name of type '" + type + "'; only named " + "arguments may be passed via py::arg() to a python function call. "); + } + [[noreturn]] static void multiple_values_error() { + throw type_error("Got multiple values for keyword argument " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void multiple_values_error(std::string name) { + throw type_error("Got multiple values for keyword argument '" + name + "'"); + } + + [[noreturn]] static void argument_cast_error() { + throw cast_error("Unable to convert call argument to Python object " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void argument_cast_error(std::string name, std::string type) { + throw cast_error("Unable to convert call argument '" + name + + "' of type '" + type + "' to Python object"); + } + +private: + tuple m_args; + dict m_kwargs; +}; + +/// Collect only positional arguments for a Python function call +template ...>::value>> +simple_collector collect_arguments(Args &&...args) { + return simple_collector(std::forward(args)...); +} + +/// Collect all arguments, including keywords and unpacking (only instantiated when needed) +template ...>::value>> +unpacking_collector collect_arguments(Args &&...args) { + // Following argument order rules for generalized unpacking according to PEP 448 + static_assert( + constexpr_last() < constexpr_first() + && constexpr_last() < constexpr_first(), + "Invalid function call: positional args must precede keywords and ** unpacking; " + "* unpacking must precede ** unpacking" + ); + return unpacking_collector(std::forward(args)...); +} + +template +template +object object_api::operator()(Args &&...args) const { + return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); +} + +template +template +object object_api::call(Args &&...args) const { + return operator()(std::forward(args)...); +} + +NAMESPACE_END(detail) + +#define PYBIND11_MAKE_OPAQUE(Type) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster : public type_caster_base { }; \ + }} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/chrono.h b/external/pybind11/include/pybind11/chrono.h new file mode 100644 index 0000000..95ada76 --- /dev/null +++ b/external/pybind11/include/pybind11/chrono.h @@ -0,0 +1,162 @@ +/* + pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include + +// Backport the PyDateTime_DELTA functions from Python3.3 if required +#ifndef PyDateTime_DELTA_GET_DAYS +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#endif +#ifndef PyDateTime_DELTA_GET_SECONDS +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#endif +#ifndef PyDateTime_DELTA_GET_MICROSECONDS +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template class duration_caster { +public: + typedef typename type::rep rep; + typedef typename type::period period; + + typedef std::chrono::duration> days; + + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + // If invoked with datetime.delta object + if (PyDelta_Check(src.ptr())) { + value = type(duration_cast>( + days(PyDateTime_DELTA_GET_DAYS(src.ptr())) + + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + return true; + } + // If invoked with a float we assume it is seconds and convert + else if (PyFloat_Check(src.ptr())) { + value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); + return true; + } + else return false; + } + + // If this is a duration just return it back + static const std::chrono::duration& get_duration(const std::chrono::duration &src) { + return src; + } + + // If this is a time_point get the time_since_epoch + template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { + return src.time_since_epoch(); + } + + static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Use overloaded function to get our duration from our source + // Works out if it is a duration or time_point and get the duration + auto d = get_duration(src); + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using dd_t = duration>; + using ss_t = duration>; + using us_t = duration; + + auto dd = duration_cast(d); + auto subd = d - dd; + auto ss = duration_cast(subd); + auto us = duration_cast(subd - ss); + return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); + } + + PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); +}; + +// This is for casting times on the system clock into datetime.datetime instances +template class type_caster> { +public: + typedef std::chrono::time_point type; + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + if (PyDateTime_Check(src.ptr())) { + std::tm cal; + cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + + value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); + return true; + } + else return false; + } + + static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + std::time_t tt = system_clock::to_time_t(src); + // this function uses static memory so it's best to copy it out asap just in case + // otherwise other code that is using localtime may break this (not just python code) + std::tm localtime = *std::localtime(&tt); + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using us_t = duration; + + return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, + localtime.tm_mon + 1, + localtime.tm_mday, + localtime.tm_hour, + localtime.tm_min, + localtime.tm_sec, + (duration_cast(src.time_since_epoch() % seconds(1))).count()); + } + PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); +}; + +// Other clocks that are not the system clock are not measured as datetime.datetime objects +// since they are not measured on calendar time. So instead we just make them timedeltas +// Or if they have passed us a time as a float we convert that +template class type_caster> +: public duration_caster> { +}; + +template class type_caster> +: public duration_caster> { +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/common.h b/external/pybind11/include/pybind11/common.h new file mode 100644 index 0000000..6c8a4f1 --- /dev/null +++ b/external/pybind11/include/pybind11/common.h @@ -0,0 +1,2 @@ +#include "detail/common.h" +#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/external/pybind11/include/pybind11/complex.h b/external/pybind11/include/pybind11/complex.h new file mode 100644 index 0000000..5dac27c --- /dev/null +++ b/external/pybind11/include/pybind11/complex.h @@ -0,0 +1,61 @@ +/* + pybind11/complex.h: Complex number support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +/// glibc defines I as a macro which breaks things, e.g., boost template names +#ifdef I +# undef I +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +template struct format_descriptor, detail::enable_if_t::value>> { + static constexpr const char c = format_descriptor::c; + static constexpr const char value[3] = { 'Z', c, '\0' }; + static std::string format() { return std::string(value); } +}; + +template constexpr const char format_descriptor< + std::complex, detail::enable_if_t::value>>::value[3]; + +NAMESPACE_BEGIN(detail) + +template struct is_fmt_numeric, detail::enable_if_t::value>> { + static constexpr bool value = true; + static constexpr int index = is_fmt_numeric::index + 3; +}; + +template class type_caster> { +public: + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyComplex_Check(src.ptr())) + return false; + Py_complex result = PyComplex_AsCComplex(src.ptr()); + if (result.real == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + value = std::complex((T) result.real, (T) result.imag); + return true; + } + + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND11_TYPE_CASTER(std::complex, _("complex")); +}; +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/detail/class.h b/external/pybind11/include/pybind11/detail/class.h new file mode 100644 index 0000000..f745992 --- /dev/null +++ b/external/pybind11/include/pybind11/detail/class.h @@ -0,0 +1,606 @@ +/* + pybind11/detail/class.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../attr.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It allocates the internal instance layout for +/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast +/// to a reference or pointer), and initialization is done by an `__init__` function. +inline PyObject *make_new_instance(PyTypeObject *type) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + + auto type = Py_TYPE(self); + type->tp_free(self); + + // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called + // as part of a derived type's dealloc, in which case we're not allowed to decref + // the type here. For cross-module compatibility, we shouldn't compare directly + // with `pybind11_object_dealloc`, but with the common one stashed in internals. + auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; + if (type->tp_dealloc == pybind11_object_type->tp_dealloc) + Py_DECREF(type); +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + auto ht_qualname = name; + if (rec.scope && hasattr(rec.scope, "__qualname__")) { + ht_qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); + } +#endif + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + + auto full_name = c_str( +#if !defined(PYPY_VERSION) + module ? str(module).cast() + "." + rec.name : +#endif + rec.name); + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + heap_type->ht_qualname = ht_qualname.release().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = full_name; + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + else + Py_INCREF(type); // Keep it alive forever (reference leak) + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/detail/common.h b/external/pybind11/include/pybind11/detail/common.h new file mode 100644 index 0000000..8f763f0 --- /dev/null +++ b/external/pybind11/include/pybind11/detail/common.h @@ -0,0 +1,800 @@ +/* + pybind11/detail/common.h -- Basic macros + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +// Robust support for some features and loading modules compiled against different pybind versions +// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on +// the main `pybind11` namespace. +#if !defined(PYBIND11_NAMESPACE) +# ifdef __GNUG__ +# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) +# else +# define PYBIND11_NAMESPACE pybind11 +# endif +#endif + +#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# if __cplusplus >= 201402L +# define PYBIND11_CPP14 +# if __cplusplus > 201402L /* Temporary: should be updated to >= the final C++17 value once known */ +# define PYBIND11_CPP17 +# endif +# endif +#elif defined(_MSC_VER) +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +# if _MSVC_LANG >= 201402L +# define PYBIND11_CPP14 +# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# define PYBIND11_CPP17 +# endif +# endif +#endif + +// Compiler version assertions +#if defined(__INTEL_COMPILER) +# if __INTEL_COMPILER < 1500 +# error pybind11 requires Intel C++ compiler v15 or newer +# endif +#elif defined(__clang__) && !defined(__apple_build_version__) +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) +# error pybind11 requires clang 3.3 or newer +# endif +#elif defined(__clang__) +// Apple changes clang version macros to its Xcode version; the first Xcode release based on +// (upstream) clang 3.3 was Xcode 5: +# if __clang_major__ < 5 +# error pybind11 requires Xcode/clang 5.0 or newer +# endif +#elif defined(__GNUG__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error pybind11 requires gcc 4.8 or newer +# endif +#elif defined(_MSC_VER) +// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features +// (e.g. std::negation) added in 2015u3: +# if _MSC_FULL_VER < 190024210 +# error pybind11 requires MSVC 2015 update 3 or newer +# endif +#endif + +#if !defined(PYBIND11_EXPORT) +# if defined(WIN32) || defined(_WIN32) +# define PYBIND11_EXPORT __declspec(dllexport) +# else +# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) +# endif +#endif + +#if defined(_MSC_VER) +# define PYBIND11_NOINLINE __declspec(noinline) +#else +# define PYBIND11_NOINLINE __attribute__ ((noinline)) +#endif + +#if defined(PYBIND11_CPP14) +# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] +#else +# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 2 +#define PYBIND11_VERSION_PATCH 1 + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode +#if defined(_MSC_VER) +# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) +# define HAVE_ROUND 1 +# endif +# pragma warning(push) +# pragma warning(disable: 4510 4610 4512 4005) +# if defined(_DEBUG) +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include + +#if defined(_WIN32) && (defined(min) || defined(max)) +# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows +#endif + +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(_MSC_VER) +# if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +#define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() + +#else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) +#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_BYTES_SIZE PyString_Size +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_BYTES_NAME "str" +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#define PYBIND11_FROM_STRING PyString_FromString +#define PYBIND11_STR_TYPE ::pybind11::bytes +#define PYBIND11_BOOL_ATTR "__nonzero__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +#define PYBIND11_PLUGIN_IMPL(name) \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ + PyObject *pybind11_init_wrapper() +#endif + +#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 +extern "C" { + struct _Py_atomic_address { void *value; }; + PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; +} +#endif + +#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +#define PYBIND11_CONCAT(first, second) first##second + +/** \rst + ***Deprecated in favor of PYBIND11_MODULE*** + + This macro creates the entry point that will be invoked when the Python interpreter + imports a plugin library. Please create a `module` in the function body and return + the pointer to its underlying Python object at the end. + + .. code-block:: cpp + + PYBIND11_PLUGIN(example) { + pybind11::module m("example", "pybind11 example plugin"); + /// Set up bindings here + return m.ptr(); + } +\endrst */ +#define PYBIND11_PLUGIN(name) \ + PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ + static PyObject *pybind11_init(); \ + PYBIND11_PLUGIN_IMPL(name) { \ + int major, minor; \ + if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ + PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ + return nullptr; \ + } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for " \ + "version %i.%i, while the interpreter is running " \ + "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ + major, minor); \ + return nullptr; \ + } \ + try { \ + return pybind11_init(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PyObject *pybind11_init() + +/** \rst + This macro creates the entry point that will be invoked when the Python interpreter + imports an extension module. The module name is given as the fist argument and it + should not be in quotes. The second macro argument defines a variable of type + `py::module` which can be used to initialize the module. + + .. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example module"; + + // Add bindings here + m.def("foo", []() { + return "Hello, World!"; + }); + } +\endrst */ +#define PYBIND11_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + PYBIND11_PLUGIN_IMPL(name) { \ + int major, minor; \ + if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ + PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ + return nullptr; \ + } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for " \ + "version %i.%i, while the interpreter is running " \ + "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ + major, minor); \ + return nullptr; \ + } \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +using ssize_t = Py_ssize_t; +using size_t = std::size_t; + +/// Approach used to cast a previously unknown C++ instance into a Python object +enum class return_value_policy : uint8_t { + /** This is the default return value policy, which falls back to the policy + return_value_policy::take_ownership when the return value is a pointer. + Otherwise, it uses return_value::move or return_value::copy for rvalue + and lvalue references, respectively. See below for a description of what + all of these different policies do. */ + automatic = 0, + + /** As above, but use policy return_value_policy::reference when the return + value is a pointer. This is the default conversion policy for function + arguments when calling Python functions manually from C++ code (i.e. via + handle::operator()). You probably won't need to use this. */ + automatic_reference, + + /** Reference an existing object (i.e. do not create a new copy) and take + ownership. Python will call the destructor and delete operator when the + object’s reference count reaches zero. Undefined behavior ensues when + the C++ side does the same.. */ + take_ownership, + + /** Create a new copy of the returned object, which will be owned by + Python. This policy is comparably safe because the lifetimes of the two + instances are decoupled. */ + copy, + + /** Use std::move to move the return value contents into a new instance + that will be owned by Python. This policy is comparably safe because the + lifetimes of the two instances (move source and destination) are + decoupled. */ + move, + + /** Reference an existing object, but do not take ownership. The C++ side + is responsible for managing the object’s lifetime and deallocating it + when it is no longer used. Warning: undefined behavior will ensue when + the C++ side deletes an object that is still referenced and used by + Python. */ + reference, + + /** This policy only applies to methods and properties. It references the + object without taking ownership similar to the above + return_value_policy::reference policy. In contrast to that policy, the + function or property’s implicit this argument (called the parent) is + considered to be the the owner of the return value (the child). + pybind11 then couples the lifetime of the parent to the child via a + reference relationship that ensures that the parent cannot be garbage + collected while Python is still using the child. More advanced + variations of this scheme are also possible using combinations of + return_value_policy::reference and the keep_alive call policy */ + reference_internal +}; + +NAMESPACE_BEGIN(detail) + +inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } + +// Returns the size as a multiple of sizeof(void *), rounded up. +inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } + +/** + * The space to allocate for simple layout instance holders (see below) in multiple of the size of + * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required + * to holder either a std::unique_ptr or std::shared_ptr (which is almost always + * sizeof(std::shared_ptr)). + */ +constexpr size_t instance_simple_holder_in_ptrs() { + static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), + "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); + return size_in_ptrs(sizeof(std::shared_ptr)); +} + +// Forward declarations +struct type_info; +struct value_and_holder; + +/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') +struct instance { + PyObject_HEAD + /// Storage for pointers and holder; see simple_layout, below, for a description + union { + void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; + struct { + void **values_and_holders; + uint8_t *status; + } nonsimple; + }; + /// Weak references (needed for keep alive): + PyObject *weakrefs; + /// If true, the pointer is owned which means we're free to manage it with a holder. + bool owned : 1; + /** + * An instance has two possible value/holder layouts. + * + * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer + * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied + * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's + * holder will fit in the default space (which is large enough to hold either a std::unique_ptr + * or std::shared_ptr). + * + * Non-simple layout applies when using custom holders that require more space than `shared_ptr` + * (which is typically the size of two pointers), or when multiple inheritance is used on the + * python side. Non-simple layout allocates the required amount of memory to have multiple + * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a + * pointer to allocated space of the required space to hold a a sequence of value pointers and + * holders followed `status`, a set of bit flags (1 byte each), i.e. + * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of + * `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the + * beginning of the [bb...] block (but not independently allocated). + * + * Status bits indicate whether the associated holder is constructed (& + * status_holder_constructed) and whether the value pointer is registered (& + * status_instance_registered) in `registered_instances`. + */ + bool simple_layout : 1; + /// For simple layout, tracks whether the holder has been constructed + bool simple_holder_constructed : 1; + /// For simple layout, tracks whether the instance is registered in `registered_instances` + bool simple_instance_registered : 1; + /// If true, get_internals().patients has an entry for this object + bool has_patients : 1; + + /// Initializes all of the above type/values/holders data (but not the instance values themselves) + void allocate_layout(); + + /// Destroys/deallocates all of the above + void deallocate_layout(); + + /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` + /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if + /// `throw_if_missing` is false. + value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); + + /// Bit values for the non-simple status flags + static constexpr uint8_t status_holder_constructed = 1; + static constexpr uint8_t status_instance_registered = 2; +}; + +static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); + +/// from __cpp_future__ import (convenient aliases from C++14/17) +#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +using std::enable_if_t; +using std::conditional_t; +using std::remove_cv_t; +using std::remove_reference_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_reference_t = typename std::remove_reference::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) +using std::index_sequence; +using std::make_index_sequence; +#else +template struct index_sequence { }; +template struct make_index_sequence_impl : make_index_sequence_impl { }; +template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; +template using make_index_sequence = typename make_index_sequence_impl::type; +#endif + +/// Make an index sequence of the indices of true arguments +template struct select_indices_impl { using type = ISeq; }; +template struct select_indices_impl, I, B, Bs...> + : select_indices_impl, index_sequence>, I + 1, Bs...> {}; +template using select_indices = typename select_indices_impl, 0, Bs...>::type; + +/// Backports of std::bool_constant and std::negation to accomodate older compilers +template using bool_constant = std::integral_constant; +template struct negation : bool_constant { }; + +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; + +/// Compile-time all/any/none of that check the boolean value of all template types +#ifdef __cpp_fold_expressions +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#else +// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit +// at a slight loss of compilation efficiency). +template using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + +template class... Predicates> using satisfies_all_of = all_of...>; +template class... Predicates> using satisfies_any_of = any_of...>; +template class... Predicates> using satisfies_none_of = none_of...>; + +/// Strip the class from a method type +template struct remove_class { }; +template struct remove_class { typedef R type(A...); }; +template struct remove_class { typedef R type(A...); }; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { typedef T type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template using intrinsic_t = typename intrinsic_type::type; + +/// Helper type to replace 'void' in some expressions +struct void_type { }; + +/// Helper template which holds a list of types +template struct type_list { }; + +/// Compile-time integer sum +#ifdef __cpp_fold_expressions +template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } +#else +constexpr size_t constexpr_sum() { return 0; } +template +constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } +#endif + +NAMESPACE_BEGIN(constexpr_impl) +/// Implementation details for constexpr functions +constexpr int first(int i) { return i; } +template +constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } + +constexpr int last(int /*i*/, int result) { return result; } +template +constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } +NAMESPACE_END(constexpr_impl) + +/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if +/// none match. +template class Predicate, typename... Ts> +constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } + +/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. +template class Predicate, typename... Ts> +constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } + +/// Return the Nth element from the parameter pack +template +struct pack_element { using type = typename pack_element::type; }; +template +struct pack_element<0, T, Ts...> { using type = T; }; + +/// Return the one and only type which matches the predicate, or Default if none match. +/// If more than one type matches the predicate, fail at compile-time. +template class Predicate, typename Default, typename... Ts> +struct exactly_one { + static constexpr auto found = constexpr_sum(Predicate::value...); + static_assert(found <= 1, "Found more than one type matching the predicate"); + + static constexpr auto index = found ? constexpr_first() : 0; + using type = conditional_t::type, Default>; +}; +template class P, typename Default> +struct exactly_one { using type = Default; }; + +template class Predicate, typename Default, typename... Ts> +using exactly_one_t = typename exactly_one::type; + +/// Defer the evaluation of type T until types Us are instantiated +template struct deferred_type { using type = T; }; +template using deferred_t = typename deferred_type::type; + +/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, +/// unlike `std::is_base_of`) +template using is_strict_base_of = bool_constant< + std::is_base_of::value && !std::is_same::value>; + +template class Base> +struct is_template_base_of_impl { + template static std::true_type check(Base *); + static std::false_type check(...); +}; + +/// Check if a template is the base of a type. For example: +/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything +template class Base, typename T> +#if !defined(_MSC_VER) +using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); +#else // MSVC2015 has trouble with decltype in template aliases +struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; +#endif + +/// Check if T is an instantiation of the template `Class`. For example: +/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. +template class Class, typename T> +struct is_instantiation : std::false_type { }; +template class Class, typename... Us> +struct is_instantiation> : std::true_type { }; + +/// Check if T is std::shared_ptr where U can be anything +template using is_shared_ptr = is_instantiation; + +/// Check if T looks like an input iterator +template struct is_input_iterator : std::false_type {}; +template +struct is_input_iterator()), decltype(++std::declval())>> + : std::true_type {}; + +template using is_function_pointer = bool_constant< + std::is_pointer::value && std::is_function::type>::value>; + +template struct strip_function_object { + using type = typename remove_class::type; +}; + +// Extracts the function signature from a function, function pointer or lambda. +template > +using function_signature_t = conditional_t< + std::is_function::value, + F, + typename conditional_t< + std::is_pointer::value || std::is_member_pointer::value, + std::remove_pointer, + strip_function_object + >::type +>; + +/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member +/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used +/// in a place where passing a lambda makes sense. +template using is_lambda = satisfies_none_of, + std::is_function, std::is_pointer, std::is_member_pointer>; + +/// Ignore that a variable is unused in compiler warnings +inline void ignore_unused(const int *) { } + +/// Apply a function over each element of a parameter pack +#ifdef __cpp_fold_expressions +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) +#else +using expand_side_effects = bool[]; +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#endif + +NAMESPACE_END(detail) + +/// C++ bindings of builtin Python exceptions +class builtin_exception : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + /// Set the error using the Python C API + virtual void set_error() const = 0; +}; + +#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ + class name : public builtin_exception { public: \ + using builtin_exception::builtin_exception; \ + name() : name("") { } \ + void set_error() const override { PyErr_SetString(type, what()); } \ + }; + +PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) +PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) +PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) +PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) +PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error +PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally + +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + +template struct format_descriptor { }; + +NAMESPACE_BEGIN(detail) +// Returns the index of the given type in the type char array below, and in the list in numpy.h +// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; +// complex float,double,long double. Note that the long double types only participate when long +// double is actually longer than double (it isn't under MSVC). +// NB: not only the string below but also complex.h and numpy.h rely on this order. +template struct is_fmt_numeric { static constexpr bool value = false; }; +template struct is_fmt_numeric::value>> { + static constexpr bool value = true; + static constexpr int index = std::is_same::value ? 0 : 1 + ( + std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( + std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); +}; +NAMESPACE_END(detail) + +template struct format_descriptor::value>> { + static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; + static constexpr const char value[2] = { c, '\0' }; + static std::string format() { return std::string(1, c); } +}; + +template constexpr const char format_descriptor< + T, detail::enable_if_t::value>>::value[2]; + +/// RAII wrapper that temporarily clears any Python error state +struct error_scope { + PyObject *type, *value, *trace; + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +}; + +/// Dummy destructor wrapper that can be used to expose classes with a private destructor +struct nodelete { template void operator()(T*) { } }; + +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 + +NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + constexpr overload_cast_impl() {} // MSVC 2015 needs this + + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +NAMESPACE_END(detail) + +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#else // no overload_cast: providing something that static_assert-fails: +template struct overload_cast { + static_assert(detail::deferred_t::value, + "pybind11::overload_cast<...> requires compiling in C++14 mode"); +}; +#endif // overload_cast + +NAMESPACE_BEGIN(detail) + +// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from +// any standard container (or C-style array) supporting std::begin/std::end, any singleton +// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. +template +class any_container { + std::vector v; +public: + any_container() = default; + + // Can construct from a pair of iterators + template ::value>> + any_container(It first, It last) : v(first, last) { } + + // Implicit conversion constructor from any arbitrary container type with values convertible to T + template ())), T>::value>> + any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } + + // initializer_list's aren't deducible, so don't get matched by the above template; we need this + // to explicitly allow implicit conversion from one: + template ::value>> + any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } + + // Avoid copying if given an rvalue vector of the correct type. + any_container(std::vector &&v) : v(std::move(v)) { } + + // Moves the vector out of an rvalue any_container + operator std::vector &&() && { return std::move(v); } + + // Dereferencing obtains a reference to the underlying vector + std::vector &operator*() { return v; } + const std::vector &operator*() const { return v; } + + // -> lets you call methods on the underlying vector + std::vector *operator->() { return &v; } + const std::vector *operator->() const { return &v; } +}; + +NAMESPACE_END(detail) + + + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/detail/descr.h b/external/pybind11/include/pybind11/detail/descr.h new file mode 100644 index 0000000..e3bf2ba --- /dev/null +++ b/external/pybind11/include/pybind11/detail/descr.h @@ -0,0 +1,185 @@ +/* + pybind11/detail/descr.h: Helper type for concatenating type signatures + either at runtime (C++11) or compile time (C++14) + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/* Concatenate type signatures at compile time using C++14 */ +#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) +#define PYBIND11_CONSTEXPR_DESCR + +template class descr { + template friend class descr; +public: + constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) + : descr(text, types, + make_index_sequence(), + make_index_sequence()) { } + + constexpr const char *text() const { return m_text; } + constexpr const std::type_info * const * types() const { return m_types; } + + template + constexpr descr operator+(const descr &other) const { + return concat(other, + make_index_sequence(), + make_index_sequence(), + make_index_sequence(), + make_index_sequence()); + } + +protected: + template + constexpr descr( + char const (&text) [Size1+1], + const std::type_info * const (&types) [Size2+1], + index_sequence, index_sequence) + : m_text{text[Indices1]..., '\0'}, + m_types{types[Indices2]..., nullptr } {} + + template + constexpr descr + concat(const descr &other, + index_sequence, index_sequence, + index_sequence, index_sequence) const { + return descr( + { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, + { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } + ); + } + +protected: + char m_text[Size1 + 1]; + const std::type_info * m_types[Size2 + 1]; +}; + +template constexpr descr _(char const(&text)[Size]) { + return descr(text, { nullptr }); +} + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr({ ('0' + Digits)..., '\0' }, { nullptr }); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[Size1], char const(&)[Size2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[Size1], char const(&text2)[Size2]) { + return _(text2); +} +template +constexpr enable_if_t> _(descr d, descr) { return d; } +template +constexpr enable_if_t> _(descr, descr d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, 1> _() { + return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); +} + +inline constexpr descr<0, 0> concat() { return _(""); } +template auto constexpr concat(descr descr) { return descr; } +template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } +template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } + +#define PYBIND11_DESCR constexpr auto + +#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ + +class descr { +public: + PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { + size_t nChars = len(text), nTypes = len(types); + m_text = new char[nChars]; + m_types = new const std::type_info *[nTypes]; + memcpy(m_text, text, nChars * sizeof(char)); + memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); + } + + PYBIND11_NOINLINE descr operator+(descr &&d2) && { + descr r; + + size_t nChars1 = len(m_text), nTypes1 = len(m_types); + size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); + + r.m_text = new char[nChars1 + nChars2 - 1]; + r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; + memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); + memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); + memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); + memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); + + delete[] m_text; delete[] m_types; + delete[] d2.m_text; delete[] d2.m_types; + + return r; + } + + char *text() { return m_text; } + const std::type_info * * types() { return m_types; } + +protected: + PYBIND11_NOINLINE descr() { } + + template static size_t len(const T *ptr) { // return length including null termination + const T *it = ptr; + while (*it++ != (T) 0) + ; + return static_cast(it - ptr); + } + + const std::type_info **m_types = nullptr; + char *m_text = nullptr; +}; + +/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ + +PYBIND11_NOINLINE inline descr _(const char *text) { + const std::type_info *types[1] = { nullptr }; + return descr(text, types); +} + +template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } +template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } +template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } +template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[2] = { &typeid(Type), nullptr }; + return descr("%", types); +} + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[1] = { nullptr }; + return descr(std::to_string(Size).c_str(), types); +} + +PYBIND11_NOINLINE inline descr concat() { return _(""); } +PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } +template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } +PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } + +#define PYBIND11_DESCR ::pybind11::detail::descr +#endif + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/detail/init.h b/external/pybind11/include/pybind11/detail/init.h new file mode 100644 index 0000000..c3594a1 --- /dev/null +++ b/external/pybind11/include/pybind11/detail/init.h @@ -0,0 +1,325 @@ +/* + pybind11/detail/init.h: init factory function implementation and support code. + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "class.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template <> +class type_caster { +public: + bool load(handle h, bool) { + value = reinterpret_cast(h.ptr()); + return true; + } + + template using cast_op_type = value_and_holder &; + operator value_and_holder &() { return *value; } + static PYBIND11_DESCR name() { return type_descr(_()); } + +private: + value_and_holder *value = nullptr; +}; + +NAMESPACE_BEGIN(initimpl) + +inline void no_nullptr(void *ptr) { + if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); +} + +// Implementing functions for all forms of py::init<...> and py::init(...) +template using Cpp = typename Class::type; +template using Alias = typename Class::type_alias; +template using Holder = typename Class::holder_type; + +template using is_alias_constructible = std::is_constructible, Cpp &&>; + +// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. +template = 0> +bool is_alias(Cpp *ptr) { + return dynamic_cast *>(ptr) != nullptr; +} +// Failing fallback version of the above for a no-alias class (always returns false) +template +constexpr bool is_alias(void *) { return false; } + +// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with +// an alias to provide only a single Cpp factory function as long as the Alias can be +// constructed from an rvalue reference of the base Cpp type. This means that Alias classes +// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to +// inherit all the base class constructors. +template +void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, + value_and_holder &v_h, Cpp &&base) { + v_h.value_ptr() = new Alias(std::move(base)); +} +template +[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, + value_and_holder &, Cpp &&) { + throw type_error("pybind11::init(): unable to convert returned instance to required " + "alias class: no `Alias(Class &&)` constructor available"); +} + +// Error-generating fallback for factories that don't match one of the below construction +// mechanisms. +template +void construct(...) { + static_assert(!std::is_same::value /* always false */, + "pybind11::init(): init function must return a compatible pointer, " + "holder, or value"); +} + +// Pointer return v1: the factory function returns a class pointer for a registered class. +// If we don't need an alias (because this class doesn't have one, or because the final type is +// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to +// construct an Alias from the returned base instance. +template +void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + // We're going to try to construct an alias by moving the cpp type. Whether or not + // that succeeds, we still need to destroy the original cpp pointer (either the + // moved away leftover, if the alias construction works, or the value itself if we + // throw an error), but we can't just call `delete ptr`: it might have a special + // deleter, or might be shared_from_this. So we construct a holder around it as if + // it was a normal instance, then steal the holder away into a local variable; thus + // the holder and destruction happens when we leave the C++ scope, and the holder + // class gets to handle the destruction however it likes. + v_h.value_ptr() = ptr; + v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder + Holder temp_holder(std::move(v_h.holder>())); // Steal the holder + v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null + v_h.set_instance_registered(false); + + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); + } else { + // Otherwise the type isn't inherited, so we don't need an Alias + v_h.value_ptr() = ptr; + } +} + +// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over +// ownership of the pointer. +template = 0> +void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { + no_nullptr(alias_ptr); + v_h.value_ptr() = static_cast *>(alias_ptr); +} + +// Holder return: copy its pointer, and move or copy the returned holder into the new instance's +// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a +// derived type (through those holder's implicit conversion from derived class holder constructors). +template +void construct(value_and_holder &v_h, Holder holder, bool need_alias) { + auto *ptr = holder_helper>::get(holder); + // If we need an alias, check that the held pointer is actually an alias instance + if (Class::has_alias && need_alias && !is_alias(ptr)) + throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " + "is not an alias instance"); + + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &holder); +} + +// return-by-value version 1: returning a cpp class by value. If the class has an alias and an +// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct +// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't +// need it, we simply move-construct the cpp value into a new instance. +template +void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-value factory function requires a movable class"); + if (Class::has_alias && need_alias) + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); + else + v_h.value_ptr() = new Cpp(std::move(result)); +} + +// return-by-value version 2: returning a value of the alias type itself. We move-construct an +// Alias instance (even if no the python-side inheritance is involved). The is intended for +// cases where Alias initialization is always desired. +template +void construct(value_and_holder &v_h, Alias &&result, bool) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-alias-value factory function requires a movable alias class"); + v_h.value_ptr() = new Alias(std::move(result)); +} + +// Implementing class for py::init<...>() +template +struct constructor { + template = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = new Cpp{std::forward(args)...}; + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + v_h.value_ptr() = new Cpp{std::forward(args)...}; + else + v_h.value_ptr() = new Alias{std::forward(args)...}; + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = new Alias{std::forward(args)...}; + }, is_new_style_constructor(), extra...); + } +}; + +// Implementing class for py::init_alias<...>() +template struct alias_constructor { + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = new Alias{std::forward(args)...}; + }, is_new_style_constructor(), extra...); + } +}; + +// Implementation class for py::init(Func) and py::init(Func, AliasFunc) +template , typename = function_signature_t> +struct factory; + +// Specialization for py::init(Func) +template +struct factory { + remove_reference_t class_factory; + + factory(Func &&f) : class_factory(std::forward(f)) { } + + // The given class either has no alias or has no separate alias factory; + // this always constructs the class itself. If the class is registered with an alias + // type and an alias instance is needed (i.e. because the final type is a Python class + // inheriting from the C++ type) the returned value needs to either already be an alias + // instance, or the alias needs to be constructible from a `Class &&` argument. + template + void execute(Class &cl, const Extra &...extra) && { + #if defined(PYBIND11_CPP14) + cl.def("__init__", [func = std::move(class_factory)] + #else + auto &func = class_factory; + cl.def("__init__", [func] + #endif + (value_and_holder &v_h, Args... args) { + construct(v_h, func(std::forward(args)...), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +// Specialization for py::init(Func, AliasFunc) +template +struct factory { + static_assert(sizeof...(CArgs) == sizeof...(AArgs), + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + static_assert(all_of...>::value, + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + + remove_reference_t class_factory; + remove_reference_t alias_factory; + + factory(CFunc &&c, AFunc &&a) + : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } + + // The class factory is called when the `self` type passed to `__init__` is the direct + // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. + template + void execute(Class &cl, const Extra&... extra) && { + static_assert(Class::has_alias, "The two-argument version of `py::init()` can " + "only be used if the class has an alias"); + #if defined(PYBIND11_CPP14) + cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] + #else + auto &class_func = class_factory; + auto &alias_func = alias_factory; + cl.def("__init__", [class_func, alias_func] + #endif + (value_and_holder &v_h, CArgs... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + // If the instance type equals the registered type we don't have inheritance, so + // don't need the alias and can construct using the class function: + construct(v_h, class_func(std::forward(args)...), false); + else + construct(v_h, alias_func(std::forward(args)...), true); + }, is_new_style_constructor(), extra...); + } +}; + +/// Set just the C++ state. Same as `__init__`. +template +void setstate(value_and_holder &v_h, T &&result, bool need_alias) { + construct(v_h, std::forward(result), need_alias); +} + +/// Set both the C++ and Python states +template ::value, int> = 0> +void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { + construct(v_h, std::move(result.first), need_alias); + setattr((PyObject *) v_h.inst, "__dict__", result.second); +} + +/// Implementation for py::pickle(GetState, SetState) +template , typename = function_signature_t> +struct pickle_factory; + +template +struct pickle_factory { + static_assert(std::is_same, intrinsic_t>::value, + "The type returned by `__getstate__` must be the same " + "as the argument accepted by `__setstate__`"); + + remove_reference_t get; + remove_reference_t set; + + pickle_factory(Get get, Set set) + : get(std::forward(get)), set(std::forward(set)) { } + + template + void execute(Class &cl, const Extra &...extra) && { + cl.def("__getstate__", std::move(get)); + +#if defined(PYBIND11_CPP14) + cl.def("__setstate__", [func = std::move(set)] +#else + auto &func = set; + cl.def("__setstate__", [func] +#endif + (value_and_holder &v_h, ArgState state) { + setstate(v_h, func(std::forward(state)), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +NAMESPACE_END(initimpl) +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/external/pybind11/include/pybind11/detail/internals.h b/external/pybind11/include/pybind11/detail/internals.h new file mode 100644 index 0000000..213cbae --- /dev/null +++ b/external/pybind11/include/pybind11/detail/internals.h @@ -0,0 +1,247 @@ +/* + pybind11/detail/internals.h: Internal data structure and related functions + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../pytypes.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +// Forward declarations +inline PyTypeObject *make_static_property_type(); +inline PyTypeObject *make_default_metaclass(); +inline PyObject *make_object_base_type(PyTypeObject *metaclass); + +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hash and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; +} + +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; + +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + +struct overload_hash { + inline size_t operator()(const std::pair& v) const { + size_t value = std::hash()(v.first); + value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); + return value; + } +}; + +/// Internal data structure used to track registered instances and types. +/// Whenever binary incompatible changes are made to this structure, +/// `PYBIND11_INTERNALS_VERSION` must be incremented. +struct internals { + type_map registered_types_cpp; // std::type_index -> pybind11's type information + std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) + std::unordered_multimap registered_instances; // void * -> instance* + std::unordered_set, overload_hash> inactive_overload_cache; + type_map> direct_conversions; + std::unordered_map> patients; + std::forward_list registered_exception_translators; + std::unordered_map shared_data; // Custom data to be shared across extensions + std::vector loader_patient_stack; // Used by `loader_life_support` + std::forward_list static_strings; // Stores the std::strings backing detail::c_str() + PyTypeObject *static_property_type; + PyTypeObject *default_metaclass; + PyObject *instance_base; +#if defined(WITH_THREAD) + decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x + PyInterpreterState *istate = nullptr; +#endif +}; + +/// Additional type information which does not fit into the PyTypeObject. +/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. +struct type_info { + PyTypeObject *type; + const std::type_info *cpptype; + size_t type_size, holder_size_in_ptrs; + void *(*operator_new)(size_t); + void (*init_instance)(instance *, const void *); + void (*dealloc)(value_and_holder &v_h); + std::vector implicit_conversions; + std::vector> implicit_casts; + std::vector *direct_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; + void *(*module_local_load)(PyObject *, const type_info *) = nullptr; + /* A simple type never occurs as a (direct or indirect) parent + * of a class that makes use of multiple inheritance */ + bool simple_type : 1; + /* True if there is no multiple inheritance in this type's inheritance tree */ + bool simple_ancestors : 1; + /* for base vs derived holder_type checks */ + bool default_holder : 1; + /* true if this is a type registered with py::module_local */ + bool module_local : 1; +}; + +/// Tracks the `internals` and `type_info` ABI version independent of the main library version +#define PYBIND11_INTERNALS_VERSION 1 + +#if defined(WITH_THREAD) +# define PYBIND11_INTERNALS_KIND "" +#else +# define PYBIND11_INTERNALS_KIND "_without_thread" +#endif + +#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__" + +#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__" + +/// Each module locally stores a pointer to the `internals` data. The data +/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. +inline internals *&get_internals_ptr() { + static internals *internals_ptr = nullptr; + return internals_ptr; +} + +/// Return a reference to the current `internals` data +PYBIND11_NOINLINE inline internals &get_internals() { + auto *&internals_ptr = get_internals_ptr(); + if (internals_ptr) + return *internals_ptr; + + constexpr auto *id = PYBIND11_INTERNALS_ID; + auto builtins = handle(PyEval_GetBuiltins()); + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_ptr = *static_cast(capsule(builtins[id])); + + // We loaded builtins through python's builtins, which means that our `error_already_set` + // and `builtin_exception` may be different local classes than the ones set up in the + // initial exception translator, below, so add another for our local exception classes. + // + // libstdc++ doesn't require this (types there are identified only by name) +#if !defined(__GLIBCXX__) + internals_ptr->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } + } + ); +#endif + } else { + internals_ptr = new internals(); +#if defined(WITH_THREAD) + PyEval_InitThreads(); + PyThreadState *tstate = PyThreadState_Get(); + internals_ptr->tstate = PyThread_create_key(); + PyThread_set_key_value(internals_ptr->tstate, tstate); + internals_ptr->istate = tstate->interp; +#endif + builtins[id] = capsule(&internals_ptr); + internals_ptr->registered_exception_translators.push_front( + [](std::exception_ptr p) -> void { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } + } + ); + internals_ptr->static_property_type = make_static_property_type(); + internals_ptr->default_metaclass = make_default_metaclass(); + internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); + } + return *internals_ptr; +} + +/// Works like `internals.registered_types_cpp`, but for module-local registered types: +inline type_map ®istered_local_types_cpp() { + static type_map locals{}; + return locals; +} + +/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its +/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only +/// cleared when the program exits or after interpreter shutdown (when embedding), and so are +/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). +template +const char *c_str(Args &&...args) { + auto &strings = get_internals().static_strings; + strings.emplace_front(std::forward(args)...); + return strings.front().c_str(); +} + +NAMESPACE_END(detail) + +/// Returns a named pointer that is shared among all extension modules (using the same +/// pybind11 version) running in the current interpreter. Names starting with underscores +/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. +inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; +} + +/// Set the shared data that can be later recovered by `get_shared_data()`. +inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { + detail::get_internals().shared_data[name] = data; + return data; +} + +/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if +/// such entry exists. Otherwise, a new object of default-constructible type `T` is +/// added to the shared data under the given name and a reference to it is returned. +template +T &get_or_create_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); + if (!ptr) { + ptr = new T(); + internals.shared_data[name] = ptr; + } + return *ptr; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/detail/typeid.h b/external/pybind11/include/pybind11/detail/typeid.h new file mode 100644 index 0000000..6f36aab --- /dev/null +++ b/external/pybind11/include/pybind11/detail/typeid.h @@ -0,0 +1,53 @@ +/* + pybind11/detail/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/eigen.h b/external/pybind11/include/pybind11/eigen.h new file mode 100644 index 0000000..a702bf3 --- /dev/null +++ b/external/pybind11/include/pybind11/eigen.h @@ -0,0 +1,610 @@ +/* + pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "numpy.h" + +#if defined(__INTEL_COMPILER) +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +#elif defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif +#endif + +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit +// move constructors that break things. We could detect this an explicitly copy, but an extra copy +// of matrices seems highly undesirable. +static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: +using EigenDStride = Eigen::Stride; +template using EigenDRef = Eigen::Ref; +template using EigenDMap = Eigen::Map; + +NAMESPACE_BEGIN(detail) + +#if EIGEN_VERSION_AT_LEAST(3,3,0) +using EigenIndex = Eigen::Index; +#else +using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; +#endif + +// Matches Eigen::Map, Eigen::Ref, blocks, etc: +template using is_eigen_dense_map = all_of, std::is_base_of, T>>; +template using is_eigen_mutable_map = std::is_base_of, T>; +template using is_eigen_dense_plain = all_of>, is_template_base_of>; +template using is_eigen_sparse = is_template_base_of; +// Test for objects inheriting from EigenBase that aren't captured by the above. This +// basically covers anything that can be assigned to a dense matrix but that don't have a typical +// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and +// SelfAdjointView fall into this category. +template using is_eigen_other = all_of< + is_template_base_of, + negation, is_eigen_dense_plain, is_eigen_sparse>> +>; + +// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): +template struct EigenConformable { + bool conformable = false; + EigenIndex rows = 0, cols = 0; + EigenDStride stride{0, 0}; // Only valid if negativestrides is false! + bool negativestrides = false; // If true, do not use stride! + + EigenConformable(bool fits = false) : conformable{fits} {} + // Matrix type: + EigenConformable(EigenIndex r, EigenIndex c, + EigenIndex rstride, EigenIndex cstride) : + conformable{true}, rows{r}, cols{c} { + // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 + if (rstride < 0 || cstride < 0) { + negativestrides = true; + } else { + stride = {EigenRowMajor ? rstride : cstride /* outer stride */, + EigenRowMajor ? cstride : rstride /* inner stride */ }; + } + } + // Vector type: + EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) + : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} + + template bool stride_compatible() const { + // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, + // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) + return + !negativestrides && + (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || + (EigenRowMajor ? cols : rows) == 1) && + (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || + (EigenRowMajor ? rows : cols) == 1); + } + operator bool() const { return conformable; } +}; + +template struct eigen_extract_stride { using type = Type; }; +template +struct eigen_extract_stride> { using type = StrideType; }; +template +struct eigen_extract_stride> { using type = StrideType; }; + +// Helper struct for extracting information from an Eigen type +template struct EigenProps { + using Type = Type_; + using Scalar = typename Type::Scalar; + using StrideType = typename eigen_extract_stride::type; + static constexpr EigenIndex + rows = Type::RowsAtCompileTime, + cols = Type::ColsAtCompileTime, + size = Type::SizeAtCompileTime; + static constexpr bool + row_major = Type::IsRowMajor, + vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 + fixed_rows = rows != Eigen::Dynamic, + fixed_cols = cols != Eigen::Dynamic, + fixed = size != Eigen::Dynamic, // Fully-fixed size + dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size + + template using if_zero = std::integral_constant; + static constexpr EigenIndex inner_stride = if_zero::value, + outer_stride = if_zero::value; + static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; + static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; + static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; + + // Takes an input array and determines whether we can make it fit into the Eigen type. If + // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector + // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). + static EigenConformable conformable(const array &a) { + const auto dims = a.ndim(); + if (dims < 1 || dims > 2) + return false; + + if (dims == 2) { // Matrix type: require exact match (or dynamic) + + EigenIndex + np_rows = a.shape(0), + np_cols = a.shape(1), + np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), + np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); + if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) + return false; + + return {np_rows, np_cols, np_rstride, np_cstride}; + } + + // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever + // is used, we want the (single) numpy stride value. + const EigenIndex n = a.shape(0), + stride = a.strides(0) / static_cast(sizeof(Scalar)); + + if (vector) { // Eigen type is a compile-time vector + if (fixed && size != n) + return false; // Vector size mismatch + return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; + } + else if (fixed) { + // The type has a fixed size, but is not a vector: abort + return false; + } + else if (fixed_cols) { + // Since this isn't a vector, cols must be != 1. We allow this only if it exactly + // equals the number of elements (rows is Dynamic, and so 1 row is allowed). + if (cols != n) return false; + return {1, n, stride}; + } + else { + // Otherwise it's either fully dynamic, or column dynamic; both become a column vector + if (fixed_rows && rows != n) return false; + return {n, 1, stride}; + } + } + + static PYBIND11_DESCR descriptor() { + constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + constexpr bool show_order = is_eigen_dense_map::value; + constexpr bool show_c_contiguous = show_order && requires_row_major; + constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + + return type_descr(_("numpy.ndarray[") + npy_format_descriptor::name() + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]") + ); + } +}; + +// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, +// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. +template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { + constexpr ssize_t elem_size = sizeof(typename props::Scalar); + array a; + if (props::vector) + a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); + else + a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, + src.data(), base); + + if (!writeable) + array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; + + return a.release(); +} + +// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that +// reference the Eigen object's data with `base` as the python-registered base class (if omitted, +// the base will be set to None, and lifetime management is up to the caller). The numpy array is +// non-writeable if the given type is const. +template +handle eigen_ref_array(Type &src, handle parent = none()) { + // none here is to get past array's should-we-copy detection, which currently always + // copies when there is no base. Setting the base to None should be harmless. + return eigen_array_cast(src, parent, !std::is_const::value); +} + +// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy +// array that references the encapsulated data with a python-side reference to the capsule to tie +// its destruction to that of any dependent python objects. Const-ness is determined by whether or +// not the Type of the pointer given is const. +template ::value>> +handle eigen_encapsulate(Type *src) { + capsule base(src, [](void *o) { delete static_cast(o); }); + return eigen_ref_array(*src, base); +} + +// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense +// types. +template +struct type_caster::value>> { + using Scalar = typename Type::Scalar; + using props = EigenProps; + + bool load(handle src, bool convert) { + // If we're in no-convert mode, only load if given an array of the correct type + if (!convert && !isinstance>(src)) + return false; + + // Coerce into an array, but don't do type conversion yet; the copy below handles it. + auto buf = array::ensure(src); + + if (!buf) + return false; + + auto dims = buf.ndim(); + if (dims < 1 || dims > 2) + return false; + + auto fits = props::conformable(buf); + if (!fits) + return false; + + // Allocate the new type, then build a numpy reference into it + value = Type(fits.rows, fits.cols); + auto ref = reinterpret_steal(eigen_ref_array(value)); + if (dims == 1) ref = ref.squeeze(); + + int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); + + if (result < 0) { // Copy failed! + PyErr_Clear(); + return false; + } + + return true; + } + +private: + + // Cast implementation + template + static handle cast_impl(CType *src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::take_ownership: + case return_value_policy::automatic: + return eigen_encapsulate(src); + case return_value_policy::move: + return eigen_encapsulate(new CType(std::move(*src))); + case return_value_policy::copy: + return eigen_array_cast(*src); + case return_value_policy::reference: + case return_value_policy::automatic_reference: + return eigen_ref_array(*src); + case return_value_policy::reference_internal: + return eigen_ref_array(*src, parent); + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + }; + } + +public: + + // Normal returned non-reference, non-const value: + static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // If you return a non-reference const, we mark the numpy array readonly: + static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // lvalue reference return; default (automatic) becomes copy + static handle cast(Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast_impl(&src, policy, parent); + } + // const lvalue reference return; default (automatic) becomes copy + static handle cast(const Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + // non-const pointer return + static handle cast(Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + // const pointer return + static handle cast(const Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + + static PYBIND11_DESCR name() { return props::descriptor(); } + + operator Type*() { return &value; } + operator Type&() { return value; } + operator Type&&() && { return std::move(value); } + template using cast_op_type = movable_cast_op_type; + +private: + Type value; +}; + +// Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force +// `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about +// the underlying data, not the outer shell). +template +struct return_value_policy_override::value>> { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +// Base class for casting reference/map/block/etc. objects back to python. +template struct eigen_map_caster { +private: + using props = EigenProps; + +public: + + // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has + // to stay around), but we'll allow it under the assumption that you know what you're doing (and + // have an appropriate keep_alive in place). We return a numpy array pointing directly at the + // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note + // that this means you need to ensure you don't destroy the object in some other way (e.g. with + // an appropriate keep_alive, or with a reference to a statically allocated matrix). + static handle cast(const MapType &src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::copy: + return eigen_array_cast(src); + case return_value_policy::reference_internal: + return eigen_array_cast(src, parent, is_eigen_mutable_map::value); + case return_value_policy::reference: + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + return eigen_array_cast(src, none(), is_eigen_mutable_map::value); + default: + // move, take_ownership don't make any sense for a ref/map: + pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); + } + } + + static PYBIND11_DESCR name() { return props::descriptor(); } + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator MapType() = delete; + template using cast_op_type = MapType; +}; + +// We can return any map-like object (but can only load Refs, specialized next): +template struct type_caster::value>> + : eigen_map_caster {}; + +// Loader for Ref<...> arguments. See the documentation for info on how to make this work without +// copying (it requires some extra effort in many cases). +template +struct type_caster< + Eigen::Ref, + enable_if_t>::value> +> : public eigen_map_caster> { +private: + using Type = Eigen::Ref; + using props = EigenProps; + using Scalar = typename props::Scalar; + using MapType = Eigen::Map; + using Array = array_t; + static constexpr bool need_writeable = is_eigen_mutable_map::value; + // Delay construction (these have no default constructor) + std::unique_ptr map; + std::unique_ptr ref; + // Our array. When possible, this is just a numpy array pointing to the source data, but + // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible + // layout, or is an array of a type that needs to be converted). Using a numpy temporary + // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and + // storage order conversion. (Note that we refuse to use this temporary copy when loading an + // argument for a Ref with M non-const, i.e. a read-write reference). + Array copy_or_ref; +public: + bool load(handle src, bool convert) { + // First check whether what we have is already an array of the right type. If not, we can't + // avoid a copy (because the copy is also going to do type conversion). + bool need_copy = !isinstance(src); + + EigenConformable fits; + if (!need_copy) { + // We don't need a converting copy, but we also need to check whether the strides are + // compatible with the Ref's stride requirements + Array aref = reinterpret_borrow(src); + + if (aref && (!need_writeable || aref.writeable())) { + fits = props::conformable(aref); + if (!fits) return false; // Incompatible dimensions + if (!fits.template stride_compatible()) + need_copy = true; + else + copy_or_ref = std::move(aref); + } + else { + need_copy = true; + } + } + + if (need_copy) { + // We need to copy: If we need a mutable reference, or we're not supposed to convert + // (either because we're in the no-convert overload pass, or because we're explicitly + // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. + if (!convert || need_writeable) return false; + + Array copy = Array::ensure(src); + if (!copy) return false; + fits = props::conformable(copy); + if (!fits || !fits.template stride_compatible()) + return false; + copy_or_ref = std::move(copy); + loader_life_support::add_patient(copy_or_ref); + } + + ref.reset(); + map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); + ref.reset(new Type(*map)); + + return true; + } + + operator Type*() { return ref.get(); } + operator Type&() { return *ref; } + template using cast_op_type = pybind11::detail::cast_op_type<_T>; + +private: + template ::value, int> = 0> + Scalar *data(Array &a) { return a.mutable_data(); } + + template ::value, int> = 0> + const Scalar *data(Array &a) { return a.data(); } + + // Attempt to figure out a constructor of `Stride` that will work. + // If both strides are fixed, use a default constructor: + template using stride_ctor_default = bool_constant< + S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_default_constructible::value>; + // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like + // Eigen::Stride, and use it: + template using stride_ctor_dual = bool_constant< + !stride_ctor_default::value && std::is_constructible::value>; + // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use + // it (passing whichever stride is dynamic). + template using stride_ctor_outer = bool_constant< + !any_of, stride_ctor_dual>::value && + S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + template using stride_ctor_inner = bool_constant< + !any_of, stride_ctor_dual>::value && + S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex) { return S(); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } + +}; + +// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not +// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). +// load() is not supported, but we can cast them into the python domain by first copying to a +// regular Eigen::Matrix, then casting that. +template +struct type_caster::value>> { +protected: + using Matrix = Eigen::Matrix; + using props = EigenProps; +public: + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + handle h = eigen_encapsulate(new Matrix(src)); + return h; + } + static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } + + static PYBIND11_DESCR name() { return props::descriptor(); } + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator Type() = delete; + template using cast_op_type = Type; +}; + +template +struct type_caster::value>> { + typedef typename Type::Scalar Scalar; + typedef remove_reference_t().outerIndexPtr())> StorageIndex; + typedef typename Type::Index Index; + static constexpr bool rowMajor = Type::IsRowMajor; + + bool load(handle src, bool) { + if (!src) + return false; + + auto obj = reinterpret_borrow(src); + object sparse_module = module::import("scipy.sparse"); + object matrix_type = sparse_module.attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + if (!obj.get_type().is(matrix_type)) { + try { + obj = matrix_type(obj); + } catch (const error_already_set &) { + return false; + } + } + + auto values = array_t((object) obj.attr("data")); + auto innerIndices = array_t((object) obj.attr("indices")); + auto outerIndices = array_t((object) obj.attr("indptr")); + auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); + auto nnz = obj.attr("nnz").cast(); + + if (!values || !innerIndices || !outerIndices) + return false; + + value = Eigen::MappedSparseMatrix( + shape[0].cast(), shape[1].cast(), nnz, + outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); + + return true; + } + + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + const_cast(src).makeCompressed(); + + object matrix_type = module::import("scipy.sparse").attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + array data(src.nonZeros(), src.valuePtr()); + array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); + array innerIndices(src.nonZeros(), src.innerIndexPtr()); + + return matrix_type( + std::make_tuple(data, innerIndices, outerIndices), + std::make_pair(src.rows(), src.cols()) + ).release(); + } + + PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + + npy_format_descriptor::name() + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/external/pybind11/include/pybind11/embed.h b/external/pybind11/include/pybind11/embed.h new file mode 100644 index 0000000..6664967 --- /dev/null +++ b/external/pybind11/include/pybind11/embed.h @@ -0,0 +1,194 @@ +/* + pybind11/embed.h: Support for embedding the interpreter + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "eval.h" + +#if defined(PYPY_VERSION) +# error Embedding the interpreter is not supported with PyPy +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name() { \ + return pybind11_init_wrapper_##name(); \ + } +#else +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" void pybind11_init_impl_##name() { \ + pybind11_init_wrapper_##name(); \ + } +#endif + +/** \rst + Add a new module to the table of builtins for the interpreter. Must be + defined in global scope. The first macro parameter is the name of the + module (without quotes). The second parameter is the variable which will + be used as the interface to add functions and classes to the module. + + .. code-block:: cpp + + PYBIND11_EMBEDDED_MODULE(example, m) { + // ... initialize functions and classes here + m.def("foo", []() { + return "Hello, World!"; + }); + } + \endrst */ +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ + PYBIND11_CONCAT(pybind11_init_impl_, name)); \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. +struct embedded_module { +#if PY_MAJOR_VERSION >= 3 + using init_t = PyObject *(*)(); +#else + using init_t = void (*)(); +#endif + embedded_module(const char *name, init_t init) { + if (Py_IsInitialized()) + pybind11_fail("Can't add new modules after the interpreter has been initialized"); + + auto result = PyImport_AppendInittab(name, init); + if (result == -1) + pybind11_fail("Insufficient memory to add a new module"); + } +}; + +NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional parameter can be used to skip the registration of signal handlers (see the + Python documentation for details). Calling this function again after the interpreter + has already been initialized is a fatal error. + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true) { + if (Py_IsInitialized()) + pybind11_fail("The interpreter is already running"); + + Py_InitializeEx(init_signal_handlers ? 1 : 0); + + // Make .py files in the working directory available by default + module::import("sys").attr("path").cast().append("."); +} + +/** \rst + Shut down the Python interpreter. No pybind11 or CPython API functions can be called + after this. In addition, pybind11 objects must not outlive the interpreter: + + .. code-block:: cpp + + { // BAD + py::initialize_interpreter(); + auto hello = py::str("Hello, World!"); + py::finalize_interpreter(); + } // <-- BOOM, hello's destructor is called after interpreter shutdown + + { // GOOD + py::initialize_interpreter(); + { // scoped + auto hello = py::str("Hello, World!"); + } // <-- OK, hello is cleaned up properly + py::finalize_interpreter(); + } + + { // BETTER + py::scoped_interpreter guard{}; + auto hello = py::str("Hello, World!"); + } + + .. warning:: + + The interpreter can be restarted by calling `initialize_interpreter` again. + Modules created using pybind11 can be safely re-initialized. However, Python + itself cannot completely unload binary extension modules and there are several + caveats with regard to interpreter restarting. All the details can be found + in the CPython documentation. In short, not all interpreter memory may be + freed, either due to reference cycles or user-created global data. + + \endrst */ +inline void finalize_interpreter() { + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + + // Get the internals pointer (without creating it if it doesn't exist). It's possible for the + // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` + // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). + detail::internals **internals_ptr_ptr = &detail::get_internals_ptr(); + // It could also be stashed in builtins, so look there too: + if (builtins.contains(id) && isinstance(builtins[id])) + internals_ptr_ptr = capsule(builtins[id]); + + Py_Finalize(); + + if (internals_ptr_ptr) { + delete *internals_ptr_ptr; + *internals_ptr_ptr = nullptr; + } +} + +/** \rst + Scope guard version of `initialize_interpreter` and `finalize_interpreter`. + This a move-only guard and only a single instance can exist. + + .. code-block:: cpp + + #include + + int main() { + py::scoped_interpreter guard{}; + py::print(Hello, World!); + } // <-- interpreter shutdown + \endrst */ +class scoped_interpreter { +public: + scoped_interpreter(bool init_signal_handlers = true) { + initialize_interpreter(init_signal_handlers); + } + + scoped_interpreter(const scoped_interpreter &) = delete; + scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } + scoped_interpreter &operator=(const scoped_interpreter &) = delete; + scoped_interpreter &operator=(scoped_interpreter &&) = delete; + + ~scoped_interpreter() { + if (is_valid) + finalize_interpreter(); + } + +private: + bool is_valid = true; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/eval.h b/external/pybind11/include/pybind11/eval.h new file mode 100644 index 0000000..ea85ba1 --- /dev/null +++ b/external/pybind11/include/pybind11/eval.h @@ -0,0 +1,117 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(str expr, object global = globals(), object local = object()) { + if (!local) + local = global; + + /* PyRun_String does not accept a PyObject / encoding specifier, + this seems to be the only alternative */ + std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +template +object eval(const char (&s)[N], object global = globals(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + +inline void exec(str expr, object global = globals(), object local = object()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], object global = globals(), object local = object()) { + eval(s, global, local); +} + +template +object eval_file(str fname, object global = globals(), object local = object()) { + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + int closeFile = 1; + std::string fname_str = (std::string) fname; +#if PY_VERSION_HEX >= 0x03040000 + FILE *f = _Py_fopen_obj(fname.ptr(), "r"); +#elif PY_VERSION_HEX >= 0x03000000 + FILE *f = _Py_fopen(fname.ptr(), "r"); +#else + /* No unicode support in open() :( */ + auto fobj = reinterpret_steal(PyFile_FromString( + const_cast(fname_str.c_str()), + const_cast("r"))); + FILE *f = nullptr; + if (fobj) + f = PyFile_AsFile(fobj.ptr()); + closeFile = 0; +#endif + if (!f) { + PyErr_Clear(); + pybind11_fail("File \"" + fname_str + "\" could not be opened!"); + } + +#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) + PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), + local.ptr()); + (void) closeFile; +#else + PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), + local.ptr(), closeFile); +#endif + + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/functional.h b/external/pybind11/include/pybind11/functional.h new file mode 100644 index 0000000..eda14ba --- /dev/null +++ b/external/pybind11/include/pybind11/functional.h @@ -0,0 +1,85 @@ +/* + pybind11/functional.h: std::function<> support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...); + +public: + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + return true; + } + + if (!isinstance(src)) + return false; + + auto func = reinterpret_borrow(src); + + /* + When passing a C++ function as an argument to another C++ + function via Python, every function call would normally involve + a full C++ -> Python -> C++ roundtrip, which can be prohibitive. + Here, we try to at least detect the case where the function is + stateless (i.e. function pointer or lambda function without + captured variables), in which case the roundtrip can be avoided. + */ + if (auto cfunc = func.cpp_function()) { + auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); + auto rec = (function_record *) c; + + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { + struct capture { function_type f; }; + value = ((capture *) &rec->data)->f; + return true; + } + } + + value = [func](Args... args) -> Return { + gil_scoped_acquire acq; + object retval(func(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + }; + return true; + } + + template + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + if (!f_) + return none().inc_ref(); + + auto result = f_.template target(); + if (result) + return cpp_function(*result, policy).release(); + else + return cpp_function(std::forward(f_), policy).release(); + } + + PYBIND11_TYPE_CASTER(type, _("Callable[[") + + argument_loader::arg_names() + _("], ") + + make_caster::name() + + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/iostream.h b/external/pybind11/include/pybind11/iostream.h new file mode 100644 index 0000000..a9c27aa --- /dev/null +++ b/external/pybind11/include/pybind11/iostream.h @@ -0,0 +1,200 @@ +/* + pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python + + Copyright (c) 2017 Henry F. Schreiner + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#include +#include +#include +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +// Buffer that writes to Python instead of C++ +class pythonbuf : public std::streambuf { +private: + using traits_type = std::streambuf::traits_type; + + char d_buffer[1024]; + object pywrite; + object pyflush; + + int overflow(int c) { + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *pptr() = traits_type::to_char_type(c); + pbump(1); + } + return sync() ? traits_type::not_eof(c) : traits_type::eof(); + } + + int sync() { + if (pbase() != pptr()) { + // This subtraction cannot be negative, so dropping the sign + str line(pbase(), static_cast(pptr() - pbase())); + + pywrite(line); + pyflush(); + + setp(pbase(), epptr()); + } + return 0; + } + +public: + pythonbuf(object pyostream) + : pywrite(pyostream.attr("write")), + pyflush(pyostream.attr("flush")) { + setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); + } + + /// Sync before destroy + ~pythonbuf() { + sync(); + } +}; + +NAMESPACE_END(detail) + + +/** \rst + This a move-only guard that redirects output. + + .. code-block:: cpp + + #include + + ... + + { + py::scoped_ostream_redirect output; + std::cout << "Hello, World!"; // Python stdout + } // <-- return std::cout to normal + + You can explicitly pass the c++ stream and the python object, + for example to guard stderr instead. + + .. code-block:: cpp + + { + py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; + std::cerr << "Hello, World!"; + } + \endrst */ +class scoped_ostream_redirect { +protected: + std::streambuf *old; + std::ostream &costream; + detail::pythonbuf buffer; + +public: + scoped_ostream_redirect( + std::ostream &costream = std::cout, + object pyostream = module::import("sys").attr("stdout")) + : costream(costream), buffer(pyostream) { + old = costream.rdbuf(&buffer); + } + + ~scoped_ostream_redirect() { + costream.rdbuf(old); + } + + scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; + scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; +}; + + +/** \rst + Like `scoped_ostream_redirect`, but redirects cerr by default. This class + is provided primary to make ``py::call_guard`` easier to make. + + .. code-block:: cpp + + m.def("noisy_func", &noisy_func, + py::call_guard()); + +\endrst */ +class scoped_estream_redirect : public scoped_ostream_redirect { +public: + scoped_estream_redirect( + std::ostream &costream = std::cerr, + object pyostream = module::import("sys").attr("stderr")) + : scoped_ostream_redirect(costream,pyostream) {} +}; + + +NAMESPACE_BEGIN(detail) + +// Class to redirect output as a context manager. C++ backend. +class OstreamRedirect { + bool do_stdout_; + bool do_stderr_; + std::unique_ptr redirect_stdout; + std::unique_ptr redirect_stderr; + +public: + OstreamRedirect(bool do_stdout = true, bool do_stderr = true) + : do_stdout_(do_stdout), do_stderr_(do_stderr) {} + + void enter() { + if (do_stdout_) + redirect_stdout.reset(new scoped_ostream_redirect()); + if (do_stderr_) + redirect_stderr.reset(new scoped_estream_redirect()); + } + + void exit() { + redirect_stdout.reset(); + redirect_stderr.reset(); + } +}; + +NAMESPACE_END(detail) + +/** \rst + This is a helper function to add a C++ redirect context manager to Python + instead of using a C++ guard. To use it, add the following to your binding code: + + .. code-block:: cpp + + #include + + ... + + py::add_ostream_redirect(m, "ostream_redirect"); + + You now have a Python context manager that redirects your output: + + .. code-block:: python + + with m.ostream_redirect(): + m.print_to_cout_function() + + This manager can optionally be told which streams to operate on: + + .. code-block:: python + + with m.ostream_redirect(stdout=true, stderr=true): + m.noisy_function_with_error_printing() + + \endrst */ +inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { + return class_(m, name.c_str(), module_local()) + .def(init(), arg("stdout")=true, arg("stderr")=true) + .def("__enter__", &detail::OstreamRedirect::enter) + .def("__exit__", [](detail::OstreamRedirect &self, args) { self.exit(); }); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/numpy.h b/external/pybind11/include/pybind11/numpy.h new file mode 100644 index 0000000..55bb816 --- /dev/null +++ b/external/pybind11/include/pybind11/numpy.h @@ -0,0 +1,1600 @@ +/* + pybind11/numpy.h: Basic NumPy support, vectorize() wrapper + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "complex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +/* This will be true on all flat address space platforms and allows us to reduce the + whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size + and dimension types (e.g. shape, strides, indexing), instead of inflicting this + upon the library user. */ +static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class array; // Forward declaration + +NAMESPACE_BEGIN(detail) +template struct npy_format_descriptor; + +struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; +}; + +struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; +}; + +struct PyVoidScalarObject_Proxy { + PyObject_VAR_HEAD + char *obval; + PyArrayDescr_Proxy *descr; + int flags; + PyObject *base; +}; + +struct numpy_type_info { + PyObject* dtype_ptr; + std::string format_str; +}; + +struct numpy_internals { + std::unordered_map registered_dtypes; + + numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { + auto it = registered_dtypes.find(std::type_index(tinfo)); + if (it != registered_dtypes.end()) + return &(it->second); + if (throw_if_missing) + pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); + return nullptr; + } + + template numpy_type_info *get_type_info(bool throw_if_missing = true) { + return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); + } +}; + +inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { + ptr = &get_or_create_shared_data("_numpy_internals"); +} + +inline numpy_internals& get_numpy_internals() { + static numpy_internals* ptr = nullptr; + if (!ptr) + load_numpy_internals(ptr); + return *ptr; +} + +struct npy_api { + enum constants { + NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, + NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, + NPY_ARRAY_OWNDATA_ = 0x0004, + NPY_ARRAY_FORCECAST_ = 0x0010, + NPY_ARRAY_ENSUREARRAY_ = 0x0040, + NPY_ARRAY_ALIGNED_ = 0x0100, + NPY_ARRAY_WRITEABLE_ = 0x0400, + NPY_BOOL_ = 0, + NPY_BYTE_, NPY_UBYTE_, + NPY_SHORT_, NPY_USHORT_, + NPY_INT_, NPY_UINT_, + NPY_LONG_, NPY_ULONG_, + NPY_LONGLONG_, NPY_ULONGLONG_, + NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, + NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, + NPY_OBJECT_ = 17, + NPY_STRING_, NPY_UNICODE_, NPY_VOID_ + }; + + typedef struct { + Py_intptr_t *ptr; + int len; + } PyArray_Dims; + + static npy_api& get() { + static npy_api api = lookup(); + return api; + } + + bool PyArray_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArray_Type_); + } + bool PyArrayDescr_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); + } + + unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); + PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_NewFromDescr_) + (PyTypeObject *, PyObject *, int, Py_intptr_t *, + Py_intptr_t *, void *, int, PyObject *); + PyObject *(*PyArray_DescrNewFromType_)(int); + int (*PyArray_CopyInto_)(PyObject *, PyObject *); + PyObject *(*PyArray_NewCopy_)(PyObject *, int); + PyTypeObject *PyArray_Type_; + PyTypeObject *PyVoidArrType_Type_; + PyTypeObject *PyArrayDescr_Type_; + PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); + int (*PyArray_DescrConverter_) (PyObject *, PyObject **); + bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, + Py_ssize_t *, PyObject **, PyObject *); + PyObject *(*PyArray_Squeeze_)(PyObject *); + int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); + PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); +private: + enum functions { + API_PyArray_GetNDArrayCFeatureVersion = 211, + API_PyArray_Type = 2, + API_PyArrayDescr_Type = 3, + API_PyVoidArrType_Type = 39, + API_PyArray_DescrFromType = 45, + API_PyArray_DescrFromScalar = 57, + API_PyArray_FromAny = 69, + API_PyArray_Resize = 80, + API_PyArray_CopyInto = 82, + API_PyArray_NewCopy = 85, + API_PyArray_NewFromDescr = 94, + API_PyArray_DescrNewFromType = 9, + API_PyArray_DescrConverter = 174, + API_PyArray_EquivTypes = 182, + API_PyArray_GetArrayParamsFromObject = 278, + API_PyArray_Squeeze = 136, + API_PyArray_SetBaseObject = 282 + }; + + static npy_api lookup() { + module m = module::import("numpy.core.multiarray"); + auto c = m.attr("_ARRAY_API"); +#if PY_MAJOR_VERSION >= 3 + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); +#else + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); +#endif + npy_api api; +#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; + DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); + if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) + pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); + DECL_NPY_API(PyArray_Type); + DECL_NPY_API(PyVoidArrType_Type); + DECL_NPY_API(PyArrayDescr_Type); + DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_FromAny); + DECL_NPY_API(PyArray_Resize); + DECL_NPY_API(PyArray_CopyInto); + DECL_NPY_API(PyArray_NewCopy); + DECL_NPY_API(PyArray_NewFromDescr); + DECL_NPY_API(PyArray_DescrNewFromType); + DECL_NPY_API(PyArray_DescrConverter); + DECL_NPY_API(PyArray_EquivTypes); + DECL_NPY_API(PyArray_GetArrayParamsFromObject); + DECL_NPY_API(PyArray_Squeeze); + DECL_NPY_API(PyArray_SetBaseObject); +#undef DECL_NPY_API + return api; + } +}; + +inline PyArray_Proxy* array_proxy(void* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArray_Proxy* array_proxy(const void* ptr) { + return reinterpret_cast(ptr); +} + +inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline bool check_flags(const void* ptr, int flag) { + return (flag == (array_proxy(ptr)->flags & flag)); +} + +template struct is_std_array : std::false_type { }; +template struct is_std_array> : std::true_type { }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template struct array_info_scalar { + typedef T type; + static constexpr bool is_array = false; + static constexpr bool is_empty = false; + static PYBIND11_DESCR extents() { return _(""); } + static void append_extents(list& /* shape */) { } +}; +// Computes underlying type and a comma-separated list of extents for array +// types (any mix of std::array and built-in arrays). An array of char is +// treated as scalar because it gets special handling. +template struct array_info : array_info_scalar { }; +template struct array_info> { + using type = typename array_info::type; + static constexpr bool is_array = true; + static constexpr bool is_empty = (N == 0) || array_info::is_empty; + static constexpr size_t extent = N; + + // appends the extents to shape + static void append_extents(list& shape) { + shape.append(N); + array_info::append_extents(shape); + } + + template::is_array, int> = 0> + static PYBIND11_DESCR extents() { + return _(); + } + + template::is_array, int> = 0> + static PYBIND11_DESCR extents() { + return concat(_(), array_info::extents()); + } +}; +// For numpy we have special handling for arrays of characters, so we don't include +// the size in the array extents. +template struct array_info : array_info_scalar { }; +template struct array_info> : array_info_scalar> { }; +template struct array_info : array_info> { }; +template using remove_all_extents_t = typename array_info::type; + +template using is_pod_struct = all_of< + std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) + // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent + // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). + std::is_trivially_copyable, +#else + // GCC 4 doesn't implement is_trivially_copyable, so approximate it + std::is_trivially_destructible, + satisfies_any_of, +#endif + satisfies_none_of +>; + +template ssize_t byte_offset_unsafe(const Strides &) { return 0; } +template +ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + byte_offset_unsafe(strides, index...); +} + +/** + * Proxy class providing unsafe, unchecked const access to array data. This is constructed through + * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` + * will be -1 for dimensions determined at runtime. + */ +template +class unchecked_reference { +protected: + static constexpr bool Dynamic = Dims < 0; + const unsigned char *data_; + // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to + // make large performance gains on big, nested loops, but requires compile-time dimensions + conditional_t> + shape_, strides_; + const ssize_t dims_; + + friend class pybind11::array; + // Constructor for compile-time dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) + : data_{reinterpret_cast(data)}, dims_{Dims} { + for (size_t i = 0; i < (size_t) dims_; i++) { + shape_[i] = shape[i]; + strides_[i] = strides[i]; + } + } + // Constructor for runtime dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) + : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} + +public: + /** + * Unchecked const reference access to data at the given indices. For a compile-time known + * number of dimensions, this requires the correct number of arguments; for run-time + * dimensionality, this is not checked (and so is up to the caller to use safely). + */ + template const T &operator()(Ix... index) const { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); + } + /** + * Unchecked const reference access to data; this operator only participates if the reference + * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. + */ + template > + const T &operator[](ssize_t index) const { return operator()(index); } + + /// Pointer access to the data at the given indices. + template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } + + /// Returns the item size, i.e. sizeof(T) + constexpr static ssize_t itemsize() { return sizeof(T); } + + /// Returns the shape (i.e. size) of dimension `dim` + ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } + + /// Returns the number of dimensions of the array + ssize_t ndim() const { return dims_; } + + /// Returns the total number of elements in the referenced array, i.e. the product of the shapes + template + enable_if_t size() const { + return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); + } + template + enable_if_t size() const { + return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Returns the total number of bytes used by the referenced data. Note that the actual span in + /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). + ssize_t nbytes() const { + return size() * itemsize(); + } +}; + +template +class unchecked_mutable_reference : public unchecked_reference { + friend class pybind11::array; + using ConstBase = unchecked_reference; + using ConstBase::ConstBase; + using ConstBase::Dynamic; +public: + /// Mutable, unchecked access to data at the given indices. + template T& operator()(Ix... index) { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return const_cast(ConstBase::operator()(index...)); + } + /** + * Mutable, unchecked access data at the given index; this operator only participates if the + * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is + * exactly equivalent to `obj(index)`. + */ + template > + T &operator[](ssize_t index) { return operator()(index); } + + /// Mutable pointer access to the data at the given indices. + template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } +}; + +template +struct type_caster> { + static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); +}; +template +struct type_caster> : type_caster> {}; + +NAMESPACE_END(detail) + +class dtype : public object { +public: + PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); + + explicit dtype(const buffer_info &info) { + dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + // If info.itemsize == 0, use the value calculated from the format string + m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); + } + + explicit dtype(const std::string &format) { + m_ptr = from_args(pybind11::str(format)).release().ptr(); + } + + dtype(const char *format) : dtype(std::string(format)) { } + + dtype(list names, list formats, list offsets, ssize_t itemsize) { + dict args; + args["names"] = names; + args["formats"] = formats; + args["offsets"] = offsets; + args["itemsize"] = pybind11::int_(itemsize); + m_ptr = from_args(args).release().ptr(); + } + + /// This is essentially the same as calling numpy.dtype(args) in Python. + static dtype from_args(object args) { + PyObject *ptr = nullptr; + if (!detail::npy_api::get().PyArray_DescrConverter_(args.release().ptr(), &ptr) || !ptr) + throw error_already_set(); + return reinterpret_steal(ptr); + } + + /// Return dtype associated with a C++ type. + template static dtype of() { + return detail::npy_format_descriptor::type>::dtype(); + } + + /// Size of the data type in bytes. + ssize_t itemsize() const { + return detail::array_descriptor_proxy(m_ptr)->elsize; + } + + /// Returns true for structured data types. + bool has_fields() const { + return detail::array_descriptor_proxy(m_ptr)->names != nullptr; + } + + /// Single-character type code. + char kind() const { + return detail::array_descriptor_proxy(m_ptr)->kind; + } + +private: + static object _dtype_from_pep3118() { + static PyObject *obj = module::import("numpy.core._internal") + .attr("_dtype_from_pep3118").cast().release().ptr(); + return reinterpret_borrow(obj); + } + + dtype strip_padding(ssize_t itemsize) { + // Recursively strip all void fields with empty names that are generated for + // padding fields (as of NumPy v1.11). + if (!has_fields()) + return *this; + + struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; + std::vector field_descriptors; + + for (auto field : attr("fields").attr("items")()) { + auto spec = field.cast(); + auto name = spec[0].cast(); + auto format = spec[1].cast()[0].cast(); + auto offset = spec[1].cast()[1].cast(); + if (!len(name) && format.kind() == 'V') + continue; + field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + } + + std::sort(field_descriptors.begin(), field_descriptors.end(), + [](const field_descr& a, const field_descr& b) { + return a.offset.cast() < b.offset.cast(); + }); + + list names, formats, offsets; + for (auto& descr : field_descriptors) { + names.append(descr.name); + formats.append(descr.format); + offsets.append(descr.offset); + } + return dtype(names, formats, offsets, itemsize); + } +}; + +class array : public buffer { +public: + PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) + + enum { + c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, + f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, + forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ + }; + + array() : array({{0}}, static_cast(nullptr)) {} + + using ShapeContainer = detail::any_container; + using StridesContainer = detail::any_container; + + // Constructs an array taking shape/strides from arbitrary container types + array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, + const void *ptr = nullptr, handle base = handle()) { + + if (strides->empty()) + *strides = c_strides(*shape, dt.itemsize()); + + auto ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); + auto descr = dt; + + int flags = 0; + if (base && ptr) { + if (isinstance(base)) + /* Copy flags from base (except ownership bit) */ + flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + else + /* Writable by default, easy to downgrade later on if needed */ + flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; + } + + auto &api = detail::npy_api::get(); + auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( + api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), + const_cast(ptr), flags, nullptr)); + if (!tmp) + throw error_already_set(); + if (ptr) { + if (base) { + api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); + } else { + tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); + } + } + m_ptr = tmp.release().ptr(); + } + + array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) + : array(dt, std::move(shape), {}, ptr, base) { } + + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } + + template + array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) + : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } + + template + array(ShapeContainer shape, const T *ptr, handle base = handle()) + : array(std::move(shape), {}, ptr, base) { } + + template + explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + + explicit array(const buffer_info &info) + : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } + + /// Array descriptor (dtype) + pybind11::dtype dtype() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); + } + + /// Total number of elements + ssize_t size() const { + return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Byte size of a single element + ssize_t itemsize() const { + return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; + } + + /// Total number of bytes + ssize_t nbytes() const { + return size() * itemsize(); + } + + /// Number of dimensions + ssize_t ndim() const { + return detail::array_proxy(m_ptr)->nd; + } + + /// Base object + object base() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->base); + } + + /// Dimensions of the array + const ssize_t* shape() const { + return detail::array_proxy(m_ptr)->dimensions; + } + + /// Dimension along a given axis + ssize_t shape(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return shape()[dim]; + } + + /// Strides of the array + const ssize_t* strides() const { + return detail::array_proxy(m_ptr)->strides; + } + + /// Stride along a given axis + ssize_t strides(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return strides()[dim]; + } + + /// Return the NumPy array flags + int flags() const { + return detail::array_proxy(m_ptr)->flags; + } + + /// If set, the array is writeable (otherwise the buffer is read-only) + bool writeable() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); + } + + /// If set, the array owns the data (will be freed when the array is deleted) + bool owndata() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); + } + + /// Pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + template const void* data(Ix... index) const { + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Mutable pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + /// May throw if the array is not writeable. + template void* mutable_data(Ix... index) { + check_writeable(); + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Byte offset from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t offset_at(Ix... index) const { + if ((ssize_t) sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return byte_offset(ssize_t(index)...); + } + + ssize_t offset_at() const { return 0; } + + /// Item count from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the + * underlying array have the `writable` flag. Use with care: the array must not be destroyed or + * reshaped for the duration of the returned object, and the caller must take care not to access + * invalid dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_reference(data(), shape(), strides(), ndim()); + } + + /// Return a new view with all of the dimensions of length 1 removed + array squeeze() { + auto& api = detail::npy_api::get(); + return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); + } + + /// Resize array to given shape + /// If refcheck is true and more that one reference exist to this array + /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change + void resize(ShapeContainer new_shape, bool refcheck = true) { + detail::npy_api::PyArray_Dims d = { + new_shape->data(), int(new_shape->size()) + }; + // try to resize, set ordering param to -1 cause it's not used anyway + object new_array = reinterpret_steal( + detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) + ); + if (!new_array) throw error_already_set(); + if (isinstance(new_array)) { *this = std::move(new_array); } + } + + /// Ensure that the argument is a NumPy array + /// In case of an error, nullptr is returned and the Python error is cleared. + static array ensure(handle h, int ExtraFlags = 0) { + auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); + if (!result) + PyErr_Clear(); + return result; + } + +protected: + template friend struct detail::npy_format_descriptor; + + void fail_dim_check(ssize_t dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template ssize_t byte_offset(Ix... index) const { + check_dimensions(index...); + return detail::byte_offset_unsafe(strides(), ssize_t(index)...); + } + + void check_writeable() const { + if (!writeable()) + throw std::domain_error("array is not writeable"); + } + + // Default, C-style strides + static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; + return strides; + } + + // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` + static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + strides[i] = strides[i - 1] * shape[i - 1]; + return strides; + } + + template void check_dimensions(Ix... index) const { + check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); + } + + void check_dimensions_impl(ssize_t, const ssize_t*) const { } + + template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { + if (i >= *shape) { + throw index_error(std::string("index ") + std::to_string(i) + + " is out of bounds for axis " + std::to_string(axis) + + " with size " + std::to_string(*shape)); + } + check_dimensions_impl(axis + 1, shape + 1, index...); + } + + /// Create array from any object -- always returns a new reference + static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template class array_t : public array { +private: + struct private_ctor {}; + // Delegating constructor needed when both moving and accessing in the same constructor + array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) + : array(std::move(shape), std::move(strides), ptr, base) {} +public: + static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); + + using value_type = T; + + array_t() : array(0, static_cast(nullptr)) {} + array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } + array_t(handle h, stolen_t) : array(h, stolen_t{}) { } + + PYBIND11_DEPRECATED("Use array_t::ensure() instead") + array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { + if (!m_ptr) PyErr_Clear(); + if (!is_borrowed) Py_XDECREF(h.ptr()); + } + + array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { + if (!m_ptr) throw error_already_set(); + } + + explicit array_t(const buffer_info& info) : array(info) { } + + array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) + : array(std::move(shape), std::move(strides), ptr, base) { } + + explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) + : array_t(private_ctor{}, std::move(shape), + ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), + ptr, base) { } + + explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + + constexpr ssize_t itemsize() const { + return sizeof(T); + } + + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + template const T* data(Ix... index) const { + return static_cast(array::data(index...)); + } + + template T* mutable_data(Ix... index) { + return static_cast(array::mutable_data(index...)); + } + + // Reference to element at a given index + template const T& at(Ix... index) const { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + // Mutable reference to element at a given index + template T& mutable_at(Ix... index) { + if (sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + return array::mutable_unchecked(); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying + * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped + * for the duration of the returned object, and the caller must take care not to access invalid + * dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + return array::unchecked(); + } + + /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert + /// it). In case of an error, nullptr is returned and the Python error is cleared. + static array_t ensure(handle h) { + auto result = reinterpret_steal(raw_array_t(h.ptr())); + if (!result) + PyErr_Clear(); + return result; + } + + static bool check_(handle h) { + const auto &api = detail::npy_api::get(); + return api.PyArray_Check_(h.ptr()) + && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); + } + +protected: + /// Create array from any object -- always returns a new reference + static PyObject *raw_array_t(PyObject *ptr) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, dtype::of().release().ptr(), 0, 0, + detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return detail::npy_format_descriptor::type>::format(); + } +}; + +template struct format_descriptor { + static std::string format() { return std::to_string(N) + "s"; } +}; +template struct format_descriptor> { + static std::string format() { return std::to_string(N) + "s"; } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return format_descriptor< + typename std::remove_cv::type>::type>::format(); + } +}; + +template +struct format_descriptor::is_array>> { + static std::string format() { + using detail::_; + PYBIND11_DESCR extents = _("(") + detail::array_info::extents() + _(")"); + return extents.text() + format_descriptor>::format(); + } +}; + +NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool convert) { + if (!convert && !type::check_(src)) + return false; + value = type::ensure(src); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name()); +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); + } +}; + +template struct npy_format_descriptor::value>> { +private: + // NB: the order here must match the one in common.h + constexpr static const int values[15] = { + npy_api::NPY_BOOL_, + npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_, + npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_, + npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, + npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ + }; + +public: + static constexpr int value = values[detail::is_fmt_numeric::index]; + + static pybind11::dtype dtype() { + if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) + return reinterpret_borrow(ptr); + pybind11_fail("Unsupported buffer format!"); + } + template ::value, int> = 0> + static PYBIND11_DESCR name() { + return _::value>(_("bool"), + _::value>("int", "uint") + _()); + } + template ::value, int> = 0> + static PYBIND11_DESCR name() { + return _::value || std::is_same::value>( + _("float") + _(), _("longdouble")); + } + template ::value, int> = 0> + static PYBIND11_DESCR name() { + return _::value || std::is_same::value>( + _("complex") + _(), _("longcomplex")); + } +}; + +#define PYBIND11_DECL_CHAR_FMT \ + static PYBIND11_DESCR name() { return _("S") + _(); } \ + static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } +template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; +template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; +#undef PYBIND11_DECL_CHAR_FMT + +template struct npy_format_descriptor::is_array>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); + + static PYBIND11_DESCR name() { return _("(") + array_info::extents() + _(")") + base_descr::name(); } + static pybind11::dtype dtype() { + list shape; + array_info::append_extents(shape); + return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + } +}; + +template struct npy_format_descriptor::value>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static PYBIND11_DESCR name() { return base_descr::name(); } + static pybind11::dtype dtype() { return base_descr::dtype(); } +}; + +struct field_descriptor { + const char *name; + ssize_t offset; + ssize_t size; + std::string format; + dtype descr; +}; + +inline PYBIND11_NOINLINE void register_structured_dtype( + const std::initializer_list& fields, + const std::type_info& tinfo, ssize_t itemsize, + bool (*direct_converter)(PyObject *, void *&)) { + + auto& numpy_internals = get_numpy_internals(); + if (numpy_internals.get_type_info(tinfo, false)) + pybind11_fail("NumPy: dtype is already registered"); + + list names, formats, offsets; + for (auto field : fields) { + if (!field.descr) + pybind11_fail(std::string("NumPy: unsupported field dtype: `") + + field.name + "` @ " + tinfo.name()); + names.append(PYBIND11_STR_TYPE(field.name)); + formats.append(field.descr); + offsets.append(pybind11::int_(field.offset)); + } + auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); + + // There is an existing bug in NumPy (as of v1.11): trailing bytes are + // not encoded explicitly into the format string. This will supposedly + // get fixed in v1.12; for further details, see these: + // - https://github.com/numpy/numpy/issues/7797 + // - https://github.com/numpy/numpy/pull/7798 + // Because of this, we won't use numpy's logic to generate buffer format + // strings and will just do it ourselves. + std::vector ordered_fields(fields); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + ssize_t offset = 0; + std::ostringstream oss; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; + for (auto& field : ordered_fields) { + if (field.offset > offset) + oss << (field.offset - offset) << 'x'; + oss << field.format << ':' << field.name << ':'; + offset = field.offset + field.size; + } + if (itemsize > offset) + oss << (itemsize - offset) << 'x'; + oss << '}'; + auto format_str = oss.str(); + + // Sanity check: verify that NumPy properly parses our buffer format string + auto& api = npy_api::get(); + auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); + if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) + pybind11_fail("NumPy: invalid buffer descriptor!"); + + auto tindex = std::type_index(tinfo); + numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; + get_internals().direct_conversions[tindex].push_back(direct_converter); +} + +template struct npy_format_descriptor { + static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); + + static PYBIND11_DESCR name() { return make_caster::name(); } + + static pybind11::dtype dtype() { + return reinterpret_borrow(dtype_ptr()); + } + + static std::string format() { + static auto format_str = get_numpy_internals().get_type_info(true)->format_str; + return format_str; + } + + static void register_dtype(const std::initializer_list& fields) { + register_structured_dtype(fields, typeid(typename std::remove_cv::type), + sizeof(T), &direct_converter); + } + +private: + static PyObject* dtype_ptr() { + static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; + return ptr; + } + + static bool direct_converter(PyObject *obj, void*& value) { + auto& api = npy_api::get(); + if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) + return false; + if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { + if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { + value = ((PyVoidScalarObject_Proxy *) obj)->obval; + return true; + } + } + return false; + } +}; + +#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) +# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) +# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) +#else + +#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ + ::pybind11::detail::field_descriptor { \ + Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ + ::pybind11::format_descriptor().Field)>::format(), \ + ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ + } + +// Extract name, offset and format descriptor for a struct field +#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) + +// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro +// (C) William Swanson, Paul Fultz +#define PYBIND11_EVAL0(...) __VA_ARGS__ +#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) +#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) +#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) +#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) +#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) +#define PYBIND11_MAP_END(...) +#define PYBIND11_MAP_OUT +#define PYBIND11_MAP_COMMA , +#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END +#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT +#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) +#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) +#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP_LIST_NEXT(test, next) \ + PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... +#define PYBIND11_MAP_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + ({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) + +#ifdef _MSC_VER +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP2_LIST_NEXT(test, next) \ + PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... +#define PYBIND11_MAP2_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + ({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) + +#endif // __CLION_IDE__ + +template +using array_iterator = typename std::add_pointer::type; + +template +array_iterator array_begin(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr)); +} + +template +array_iterator array_end(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); +} + +class common_iterator { +public: + using container_type = std::vector; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + + common_iterator() : p_ptr(0), m_strides() {} + + common_iterator(void* ptr, const container_type& strides, const container_type& shape) + : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { + m_strides.back() = static_cast(strides.back()); + for (size_type i = m_strides.size() - 1; i != 0; --i) { + size_type j = i - 1; + value_type s = static_cast(shape[i]); + m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; + } + } + + void increment(size_type dim) { + p_ptr += m_strides[dim]; + } + + void* data() const { + return p_ptr; + } + +private: + char* p_ptr; + container_type m_strides; +}; + +template class multi_array_iterator { +public: + using container_type = std::vector; + + multi_array_iterator(const std::array &buffers, + const container_type &shape) + : m_shape(shape.size()), m_index(shape.size(), 0), + m_common_iterator() { + + // Manual copy to avoid conversion warning if using std::copy + for (size_t i = 0; i < shape.size(); ++i) + m_shape[i] = shape[i]; + + container_type strides(shape.size()); + for (size_t i = 0; i < N; ++i) + init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); + } + + multi_array_iterator& operator++() { + for (size_t j = m_index.size(); j != 0; --j) { + size_t i = j - 1; + if (++m_index[i] != m_shape[i]) { + increment_common_iterator(i); + break; + } else { + m_index[i] = 0; + } + } + return *this; + } + + template T* data() const { + return reinterpret_cast(m_common_iterator[K].data()); + } + +private: + + using common_iter = common_iterator; + + void init_common_iterator(const buffer_info &buffer, + const container_type &shape, + common_iter &iterator, + container_type &strides) { + auto buffer_shape_iter = buffer.shape.rbegin(); + auto buffer_strides_iter = buffer.strides.rbegin(); + auto shape_iter = shape.rbegin(); + auto strides_iter = strides.rbegin(); + + while (buffer_shape_iter != buffer.shape.rend()) { + if (*shape_iter == *buffer_shape_iter) + *strides_iter = *buffer_strides_iter; + else + *strides_iter = 0; + + ++buffer_shape_iter; + ++buffer_strides_iter; + ++shape_iter; + ++strides_iter; + } + + std::fill(strides_iter, strides.rend(), 0); + iterator = common_iter(buffer.ptr, strides, shape); + } + + void increment_common_iterator(size_t dim) { + for (auto &iter : m_common_iterator) + iter.increment(dim); + } + + container_type m_shape; + container_type m_index; + std::array m_common_iterator; +}; + +enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; + +// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial +// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a +// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage +// buffer; returns `non_trivial` otherwise. +template +broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { + ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { + return std::max(res, buf.ndim); + }); + + shape.clear(); + shape.resize((size_t) ndim, 1); + + // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or + // the full size). + for (size_t i = 0; i < N; ++i) { + auto res_iter = shape.rbegin(); + auto end = buffers[i].shape.rend(); + for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { + const auto &dim_size_in = *shape_iter; + auto &dim_size_out = *res_iter; + + // Each input dimension can either be 1 or `n`, but `n` values must match across buffers + if (dim_size_out == 1) + dim_size_out = dim_size_in; + else if (dim_size_in != 1 && dim_size_in != dim_size_out) + pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); + } + } + + bool trivial_broadcast_c = true; + bool trivial_broadcast_f = true; + for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { + if (buffers[i].size == 1) + continue; + + // Require the same number of dimensions: + if (buffers[i].ndim != ndim) + return broadcast_trivial::non_trivial; + + // Require all dimensions be full-size: + if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) + return broadcast_trivial::non_trivial; + + // Check for C contiguity (but only if previous inputs were also C contiguous) + if (trivial_broadcast_c) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.crend(); + for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); + trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_c = false; + } + } + + // Check for Fortran contiguity (if previous inputs were also F contiguous) + if (trivial_broadcast_f) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.cend(); + for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); + trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_f = false; + } + } + } + + return + trivial_broadcast_c ? broadcast_trivial::c_trivial : + trivial_broadcast_f ? broadcast_trivial::f_trivial : + broadcast_trivial::non_trivial; +} + +template +struct vectorize_arg { + static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); + // The wrapped function gets called with this type: + using call_type = remove_reference_t; + // Is this a vectorized argument? + static constexpr bool vectorize = + satisfies_any_of::value && + satisfies_none_of::value && + (!std::is_reference::value || + (std::is_lvalue_reference::value && std::is_const::value)); + // Accept this type: an array for vectorized types, otherwise the type as-is: + using type = conditional_t, array::forcecast>, T>; +}; + +template +struct vectorize_helper { +private: + static constexpr size_t N = sizeof...(Args); + static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); + static_assert(NVectorized >= 1, + "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); + +public: + template + explicit vectorize_helper(T &&f) : f(std::forward(f)) { } + + object operator()(typename vectorize_arg::type... args) { + return run(args..., + make_index_sequence(), + select_indices::vectorize...>(), + make_index_sequence()); + } + +private: + remove_reference_t f; + + template using param_n_t = typename pack_element::call_type...>::type; + + // Runs a vectorized function given arguments tuple and three index sequences: + // - Index is the full set of 0 ... (N-1) argument indices; + // - VIndex is the subset of argument indices with vectorized parameters, letting us access + // vectorized arguments (anything not in this sequence is passed through) + // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that + // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at + // index BIndex in the array). + template object run( + typename vectorize_arg::type &...args, + index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { + + // Pointers to values the function was called with; the vectorized ones set here will start + // out as array_t pointers, but they will be changed them to T pointers before we make + // call the wrapped function. Non-vectorized pointers are left as-is. + std::array params{{ &args... }}; + + // The array of `buffer_info`s of vectorized arguments: + std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; + + /* Determine dimensions parameters of output array */ + ssize_t nd = 0; + std::vector shape(0); + auto trivial = broadcast(buffers, nd, shape); + size_t ndim = (size_t) nd; + + size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); + + // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. + // not wrapped in an array). + if (size == 1 && ndim == 0) { + PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); + return cast(f(*reinterpret_cast *>(params[Index])...)); + } + + array_t result; + if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); + else result = array_t(shape); + + if (size == 0) return result; + + /* Call the function */ + if (trivial == broadcast_trivial::non_trivial) + apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); + else + apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); + + return result; + } + + template + void apply_trivial(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + index_sequence, index_sequence, index_sequence) { + + // Initialize an array of mutable byte references and sizes with references set to the + // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size + // (except for singletons, which get an increment of 0). + std::array, NVectorized> vecparams{{ + std::pair( + reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), + buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) + )... + }}; + + for (size_t i = 0; i < size; ++i) { + out[i] = f(*reinterpret_cast *>(params[Index])...); + for (auto &x : vecparams) x.first += x.second; + } + } + + template + void apply_broadcast(std::array &buffers, + std::array ¶ms, + array_t &output_array, + index_sequence, index_sequence, index_sequence) { + + buffer_info output = output_array.request(); + multi_array_iterator input_iter(buffers, output.shape); + + for (array_iterator iter = array_begin(output), end = array_end(output); + iter != end; + ++iter, ++input_iter) { + PYBIND11_EXPAND_SIDE_EFFECTS(( + params[VIndex] = input_iter.template data() + )); + *iter = f(*reinterpret_cast *>(std::get(params))...); + } + } +}; + +template +vectorize_helper +vectorize_extractor(const Func &f, Return (*) (Args ...)) { + return detail::vectorize_helper(f); +} + +template struct handle_type_name> { + static PYBIND11_DESCR name() { + return _("numpy.ndarray[") + npy_format_descriptor::name() + _("]"); + } +}; + +NAMESPACE_END(detail) + +// Vanilla pointer vectorizer: +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...)) { + return detail::vectorize_helper(f); +} + +// lambda vectorizer: +template ::value, int> = 0> +auto vectorize(Func &&f) -> decltype( + detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { + return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); +} + +// Vectorize a class method (non-const): +template ())), Return, Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...)) { + return Helper(std::mem_fn(f)); +} + +// Vectorize a class method (non-const): +template ())), Return, const Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...) const) { + return Helper(std::mem_fn(f)); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/external/pybind11/include/pybind11/operators.h b/external/pybind11/include/pybind11/operators.h new file mode 100644 index 0000000..b3dd62c --- /dev/null +++ b/external/pybind11/include/pybind11/operators.h @@ -0,0 +1,168 @@ +/* + pybind11/operator.h: Metatemplates for operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Enumeration with all supported operator types +enum op_id : int { + op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, + op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, + op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, + op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, + op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, + op_repr, op_truediv, op_itruediv, op_hash +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t { }; +static const self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t { }; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template struct op_impl { }; + +/// Operator implementation generator +template struct op_ { + template void execute(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } + template void execute_cast(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L &l, const R &r) { return B(expr); } \ +}; \ +template struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R &r, const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &, const self_t &) { \ + return op_(); \ +} \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} \ +template op_ op(const T &, const self_t &) { \ + return op_(); \ +} + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ + static B execute_cast(L &l, const R &r) { return B(expr); } \ +}; \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &) { \ + return op_(); \ +} + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) +PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +PYBIND11_UNARY_OPERATOR(int, int_, (int) l) +PYBIND11_UNARY_OPERATOR(float, float_, (double) l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR +NAMESPACE_END(detail) + +using detail::self; + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/external/pybind11/include/pybind11/options.h b/external/pybind11/include/pybind11/options.h new file mode 100644 index 0000000..cc1e1f6 --- /dev/null +++ b/external/pybind11/include/pybind11/options.h @@ -0,0 +1,65 @@ +/* + pybind11/options.h: global settings that are configurable at runtime. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class options { +public: + + // Default RAII constructor, which leaves settings as they currently are. + options() : previous_state(global_state()) {} + + // Class is non-copyable. + options(const options&) = delete; + options& operator=(const options&) = delete; + + // Destructor, which restores settings that were in effect before. + ~options() { + global_state() = previous_state; + } + + // Setter methods (affect the global state): + + options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } + + options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } + + options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } + + options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + + // Getter methods (return the global state): + + static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } + + static bool show_function_signatures() { return global_state().show_function_signatures; } + + // This type is not meant to be allocated on the heap. + void* operator new(size_t) = delete; + +private: + + struct state { + bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. + bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + }; + + static state &global_state() { + static state instance; + return instance; + } + + state previous_state; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/pybind11.h b/external/pybind11/include/pybind11/pybind11.h new file mode 100644 index 0000000..613135a --- /dev/null +++ b/external/pybind11/include/pybind11/pybind11.h @@ -0,0 +1,1965 @@ +/* + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified +#elif defined(__INTEL_COMPILER) +# pragma warning(push) +# pragma warning(disable: 68) // integer conversion resulted in a change of sign +# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero +# pragma warning(disable: 878) // incompatible exception specifications +# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline" +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# pragma GCC diagnostic ignored "-Wattributes" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wnoexcept-type" +# endif +#endif + +#include "attr.h" +#include "options.h" +#include "detail/class.h" +#include "detail/init.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object +class cpp_function : public function { +public: + cpp_function() { } + + /// Construct a cpp_function from a vanilla function pointer + template + cpp_function(Return (*f)(Args...), const Extra&... extra) { + initialize(f, f, extra...); + } + + /// Construct a cpp_function from a lambda function (possibly with internal state) + template ::value>> + cpp_function(Func &&f, const Extra&... extra) { + initialize(std::forward(f), + (detail::function_signature_t *) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Return the function name + object name() const { return attr("__name__"); } + +protected: + /// Space optimization: don't inline this frequently instantiated fragment + PYBIND11_NOINLINE detail::function_record *make_function_record() { + return new detail::function_record(); + } + + /// Special internal constructor for functors, lambda functions, etc. + template + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + + struct capture { detail::remove_reference_t f; }; + + /* Store the function including any extra state it might have (e.g. a lambda capture object) */ + auto rec = make_function_record(); + + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + /* Without these pragmas, GCC warns that there might not be + enough space to use the placement new operator. However, the + 'if' statement above ensures that this is the case. */ +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wplacement-new" +#endif + new ((capture *) &rec->data) capture { std::forward(f) }; +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic pop +#endif + if (!std::is_trivially_destructible::value) + rec->free_data = [](detail::function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](detail::function_record *r) { delete ((capture *) r->data[0]); }; + } + + /* Type casters for the function arguments and return value */ + using cast_in = detail::argument_loader; + using cast_out = detail::make_caster< + detail::conditional_t::value, detail::void_type, Return> + >; + + static_assert(detail::expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), + "The number of argument annotations does not match the number of function arguments"); + + /* Dispatch code which converts function arguments and performs the actual function call */ + rec->impl = [](detail::function_call &call) -> handle { + cast_in args_converter; + + /* Try to cast the function arguments into the C++ domain */ + if (!args_converter.load_args(call)) + return PYBIND11_TRY_NEXT_OVERLOAD; + + /* Invoke call policy pre-call hook */ + detail::process_attributes::precall(call); + + /* Get a pointer to the capture object */ + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + capture *cap = const_cast(reinterpret_cast(data)); + + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + const auto policy = detail::return_value_policy_override::policy(call.func.policy); + + /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ + using Guard = detail::extract_guard_t; + + /* Perform the function call */ + handle result = cast_out::cast( + std::move(args_converter).template call(cap->f), policy, call.parent); + + /* Invoke call policy post-call hook */ + detail::process_attributes::postcall(call, result); + + return result; + }; + + /* Process any user-provided function attributes */ + detail::process_attributes::init(extra..., rec); + + /* Generate a readable signature describing the function's arguments and return value types */ + using detail::descr; using detail::_; + PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name(); + + /* Register the function with Python from generic (non-templated) code */ + initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); + + if (cast_in::has_args) rec->has_args = true; + if (cast_in::has_kwargs) rec->has_kwargs = true; + + /* Stash some additional information used by an important optimization in 'functional.h' */ + using FunctionType = Return (*)(Args...); + constexpr bool is_function_ptr = + std::is_convertible::value && + sizeof(capture) == sizeof(void *); + if (is_function_ptr) { + rec->is_stateless = true; + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); + } + } + + /// Register a function call with Python (generic non-templated code goes here) + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, size_t args) { + + /* Create copies of all referenced C-style strings */ + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); + } + + rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); + +#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) + if (rec->is_constructor && !rec->is_new_style_constructor) { + const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name); + const auto func_name = std::string(rec->name); + PyErr_WarnEx( + PyExc_FutureWarning, + ("pybind11-bound class '" + class_name + "' is using an old-style " + "placement-new '" + func_name + "' which has been deprecated. See " + "the upgrade guide in pybind11's docs. This message is only visible " + "when compiled in debug mode.").c_str(), 0 + ); + } +#endif + + /* Generate a proper function signature */ + std::string signature; + size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0; + while (true) { + char c = text[char_index++]; + if (c == '\0') + break; + + if (c == '{') { + // Write arg name for everything except *args, **kwargs and return type. + if (type_depth == 0 && text[char_index] != '*' && arg_index < args) { + if (!rec->args.empty() && rec->args[arg_index].name) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->is_method) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); + } + signature += ": "; + } + ++type_depth; + } else if (c == '}') { + --type_depth; + if (type_depth == 0) { + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += "="; + signature += rec->args[arg_index].descr; + } + arg_index++; + } + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + pybind11_fail("Internal error while parsing type signature (1)"); + if (auto tinfo = detail::get_type_info(*t)) { +#if defined(PYPY_VERSION) + signature += handle((PyObject *) tinfo->type) + .attr("__module__") + .cast() + "."; +#endif + signature += tinfo->type->tp_name; + } else if (rec->is_new_style_constructor && arg_index == 0) { + // A new-style `__init__` takes `self` as `value_and_holder`. + // Rewrite it to the proper class type. +#if defined(PYPY_VERSION) + signature += rec->scope.attr("__module__").cast() + "."; +#endif + signature += ((PyTypeObject *) rec->scope.ptr())->tp_name; + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + if (type_depth != 0 || types[type_index] != nullptr) + pybind11_fail("Internal error while parsing type signature (2)"); + + #if !defined(PYBIND11_CONSTEXPR_DESCR) + delete[] types; + delete[] text; + #endif + +#if PY_MAJOR_VERSION < 3 + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); + } else if (strcmp(rec->name, "__bool__") == 0) { + std::free(rec->name); + rec->name = strdup("__nonzero__"); + } +#endif + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); + rec->nargs = (std::uint16_t) args; + + if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) + rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); + + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling) { + if (PyCFunction_Check(rec->sibling.ptr())) { + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + chain = (detail::function_record *) rec_capsule; + /* Never append a method to an overload chain of a parent class; + instead, hide the parent's overloads in this case */ + if (!chain->scope.is(rec->scope)) + chain = nullptr; + } + // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing + else if (!rec->sibling.is_none() && rec->name[0] != '_') + pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + + "\" with a function of the same name"); + } + + if (!chain) { + /* No existing overload was found, create a new function object */ + rec->def = new PyMethodDef(); + std::memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(*dispatcher); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + + capsule rec_capsule(rec, [](void *ptr) { + destruct((detail::function_record *) ptr); + }); + + object scope_module; + if (rec->scope) { + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } + } + + m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); + } else { + /* Append at the end of the overload chain */ + m_ptr = rec->sibling.ptr(); + inc_ref(); + chain_start = chain; + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); + while (chain->next) + chain = chain->next; + chain->next = rec; + } + + std::string signatures; + int index = 0; + /* Create a nice pydoc rec including all signatures and + docstrings of the functions in the overload chain */ + if (chain && options::show_function_signatures()) { + // First a generic signature + signatures += rec->name; + signatures += "(*args, **kwargs)\n"; + signatures += "Overloaded function.\n\n"; + } + // Then specific overload signatures + bool first_user_def = true; + for (auto it = chain_start; it != nullptr; it = it->next) { + if (options::show_function_signatures()) { + if (index > 0) signatures += "\n"; + if (chain) + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { + // If we're appending another docstring, and aren't printing function signatures, we + // need to append a newline first: + if (!options::show_function_signatures()) { + if (first_user_def) first_user_def = false; + else signatures += "\n"; + } + if (options::show_function_signatures()) signatures += "\n"; + signatures += it->doc; + if (options::show_function_signatures()) signatures += "\n"; + } + } + + /* Install docstring */ + PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; + if (func->m_ml->ml_doc) + std::free(const_cast(func->m_ml->ml_doc)); + func->m_ml->ml_doc = strdup(signatures.c_str()); + + if (rec->is_method) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); + Py_DECREF(func); + } + } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + arg.value.dec_ref(); + } + if (rec->def) { + std::free(const_cast(rec->def->ml_doc)); + delete rec->def; + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { + using namespace detail; + + /* Iterator over the list of potentially admissible overloads */ + function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); + + handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + + auto self_value_and_holder = value_and_holder(); + if (overloads->is_constructor) { + const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + const auto pi = reinterpret_cast(parent.ptr()); + self_value_and_holder = pi->get_value_and_holder(tinfo, false); + + if (!self_value_and_holder.type || !self_value_and_holder.inst) { + PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); + return nullptr; + } + + // If this value is already registered it must mean __init__ is invoked multiple times; + // we really can't support that in C++, so just ignore the second __init__. + if (self_value_and_holder.instance_registered()) + return none().release().ptr(); + } + + try { + // We do this in two passes: in the first pass, we load arguments with `convert=false`; + // in the second, we allow conversion (except for arguments with an explicit + // py::arg().noconvert()). This lets us prefer calls without conversion, with + // conversion as a fallback. + std::vector second_pass; + + // However, if there are no overloads, we can just skip the no-convert pass entirely + const bool overloaded = it != nullptr && it->next != nullptr; + + for (; it != nullptr; it = it->next) { + + /* For each overload: + 1. Copy all positional arguments we were given, also checking to make sure that + named positional arguments weren't *also* specified via kwarg. + 2. If we weren't given enough, try to make up the omitted ones by checking + whether they were provided by a kwarg matching the `py::arg("name")` name. If + so, use it (and remove it from kwargs; if not, see if the function binding + provided a default that we can use. + 3. Ensure that either all keyword arguments were "consumed", or that the function + takes a kwargs argument to accept unconsumed kwargs. + 4. Any positional arguments still left get put into a tuple (for args), and any + leftover kwargs get put into a dict. + 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an + extra tuple or dict at the end of the positional arguments. + 6. Call the function call dispatcher (function_record::impl) + + If one of these fail, move on to the next overload and keep trying until we get a + result other than PYBIND11_TRY_NEXT_OVERLOAD. + */ + + function_record &func = *it; + size_t pos_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --pos_args; // (but don't count py::args + if (func.has_kwargs) --pos_args; // or py::kwargs) + + if (!func.has_args && n_args_in > pos_args) + continue; // Too many arguments for this overload + + if (n_args_in < pos_args && func.args.size() < pos_args) + continue; // Not enough arguments given, and not enough defaults to fill in the blanks + + function_call call(func, parent); + + size_t args_to_copy = std::min(pos_args, n_args_in); + size_t args_copied = 0; + + // 0. Inject new-style `self` argument + if (func.is_new_style_constructor) { + // The `value` may have been preallocated by an old-style `__init__` + // if it was a preceding candidate for overload resolution. + if (self_value_and_holder) + self_value_and_holder.type->dealloc(self_value_and_holder); + + call.init_self = PyTuple_GET_ITEM(args_in, 0); + call.args.push_back(reinterpret_cast(&self_value_and_holder)); + call.args_convert.push_back(false); + ++args_copied; + } + + // 1. Copy any position arguments given. + bool bad_arg = false; + for (; args_copied < args_to_copy; ++args_copied) { + argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + bad_arg = true; + break; + } + + handle arg(PyTuple_GET_ITEM(args_in, args_copied)); + if (arg_rec && !arg_rec->none && arg.is_none()) { + bad_arg = true; + break; + } + call.args.push_back(arg); + call.args_convert.push_back(arg_rec ? arg_rec->convert : true); + } + if (bad_arg) + continue; // Maybe it was meant for another overload (issue #688) + + // We'll need to copy this if we steal some kwargs for defaults + dict kwargs = reinterpret_borrow(kwargs_in); + + // 2. Check kwargs and, failing that, defaults that may help complete the list + if (args_copied < pos_args) { + bool copied_kwargs = false; + + for (; args_copied < pos_args; ++args_copied) { + const auto &arg = func.args[args_copied]; + + handle value; + if (kwargs_in && arg.name) + value = PyDict_GetItemString(kwargs.ptr(), arg.name); + + if (value) { + // Consume a kwargs value + if (!copied_kwargs) { + kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); + copied_kwargs = true; + } + PyDict_DelItemString(kwargs.ptr(), arg.name); + } else if (arg.value) { + value = arg.value; + } + + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg.convert); + } + else + break; + } + + if (args_copied < pos_args) + continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments + } + + // 3. Check everything was consumed (unless we have a kwargs arg) + if (kwargs && kwargs.size() > 0 && !func.has_kwargs) + continue; // Unconsumed kwargs, but no py::kwargs argument to accept them + + // 4a. If we have a py::args argument, create a new tuple with leftovers + tuple extra_args; + if (func.has_args) { + if (args_to_copy == 0) { + // We didn't copy out any position arguments from the args_in tuple, so we + // can reuse it directly without copying: + extra_args = reinterpret_borrow(args_in); + } else if (args_copied >= n_args_in) { + extra_args = tuple(0); + } else { + size_t args_size = n_args_in - args_copied; + extra_args = tuple(args_size); + for (size_t i = 0; i < args_size; ++i) { + handle item = PyTuple_GET_ITEM(args_in, args_copied + i); + extra_args[i] = item.inc_ref().ptr(); + } + } + call.args.push_back(extra_args); + call.args_convert.push_back(false); + } + + // 4b. If we have a py::kwargs, pass on any remaining kwargs + if (func.has_kwargs) { + if (!kwargs.ptr()) + kwargs = dict(); // If we didn't get one, send an empty one + call.args.push_back(kwargs); + call.args_convert.push_back(false); + } + + // 5. Put everything in a vector. Not technically step 5, we've been building it + // in `call.args` all along. + #if !defined(NDEBUG) + if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) + pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); + #endif + + std::vector second_pass_convert; + if (overloaded) { + // We're in the first no-convert pass, so swap out the conversion flags for a + // set of all-false flags. If the call fails, we'll swap the flags back in for + // the conversion-allowed call below. + second_pass_convert.resize(func.nargs, false); + call.args_convert.swap(second_pass_convert); + } + + // 6. Call the function. + try { + loader_life_support guard{}; + result = func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + + if (overloaded) { + // The (overloaded) call failed; if the call has at least one argument that + // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) + // then add this call to the list of second pass overloads to try. + for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { + if (second_pass_convert[i]) { + // Found one: swap the converting flags back in and store the call for + // the second pass. + call.args_convert.swap(second_pass_convert); + second_pass.push_back(std::move(call)); + break; + } + } + } + } + + if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + // The no-conversion pass finished without success, try again with conversion allowed + for (auto &call : second_pass) { + try { + loader_life_support guard{}; + result = call.func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + } + } + } catch (error_already_set &e) { + e.restore(); + return nullptr; + } catch (...) { + /* When an exception is caught, give each registered exception + translator a chance to translate it to a Python exception + in reverse order of registration. + + A translator may choose to do one of the following: + + - catch the exception and call PyErr_SetString or PyErr_SetObject + to set a standard (or custom) Python exception, or + - do nothing and let the exception fall through to the next translator, or + - delegate translation to the next translator by throwing a new type of exception. */ + + auto last_exception = std::current_exception(); + auto ®istered_exception_translators = get_internals().registered_exception_translators; + for (auto& translator : registered_exception_translators) { + try { + translator(last_exception); + } catch (...) { + last_exception = std::current_exception(); + continue; + } + return nullptr; + } + PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); + return nullptr; + } + + auto append_note_if_missing_header_is_suspected = [](std::string &msg) { + if (msg.find("std::") != std::string::npos) { + msg += "\n\n" + "Did you forget to `#include `? Or ,\n" + ", , etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module."; + } + }; + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + if (overloads->is_operator) + return handle(Py_NotImplemented).inc_ref().ptr(); + + std::string msg = std::string(overloads->name) + "(): incompatible " + + std::string(overloads->is_constructor ? "constructor" : "function") + + " arguments. The following argument types are supported:\n"; + + int ctr = 0; + for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + + bool wrote_sig = false; + if (overloads->is_constructor) { + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + std::string sig = it2->signature; + size_t start = sig.find('(') + 7; // skip "(self: " + if (start < sig.size()) { + // End at the , for the next argument + size_t end = sig.find(", "), next = end + 2; + size_t ret = sig.rfind(" -> "); + // Or the ), if there is no comma: + if (end >= sig.size()) next = end = sig.find(')'); + if (start < end && next < sig.size()) { + msg.append(sig, start, end - start); + msg += '('; + msg.append(sig, next, ret - next); + wrote_sig = true; + } + } + } + if (!wrote_sig) msg += it2->signature; + + msg += "\n"; + } + msg += "\nInvoked with: "; + auto args_ = reinterpret_borrow(args_in); + bool some_args = false; + for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; + msg += pybind11::repr(args_[ti]); + } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (kwargs.size() > 0) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + } + } + } + + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { + auto *pi = reinterpret_cast(parent.ptr()); + self_value_and_holder.type->init_instance(pi, nullptr); + } + return result.ptr(); + } + } +}; + +/// Wrapper for Python extension modules +class module : public object { +public: + PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) + + /// Create a new top-level Python module with the given name and docstring + explicit module(const char *name, const char *doc = nullptr) { + if (!options::show_user_defined_docstrings()) doc = nullptr; +#if PY_MAJOR_VERSION >= 3 + PyModuleDef *def = new PyModuleDef(); + std::memset(def, 0, sizeof(PyModuleDef)); + def->m_name = name; + def->m_doc = doc; + def->m_size = -1; + Py_INCREF(def); + m_ptr = PyModule_Create(def); +#else + m_ptr = Py_InitModule3(name, nullptr, doc); +#endif + if (m_ptr == nullptr) + pybind11_fail("Internal error in module::module()"); + inc_ref(); + } + + /** \rst + Create Python binding for a new function within the module scope. ``Func`` + can be a plain C++ function, a function pointer, or a lambda function. For + details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. + \endrst */ + template + module &def(const char *name_, Func &&f, const Extra& ... extra) { + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + // NB: allow overwriting here because cpp_function sets up a chain with the intention of + // overwriting (and has already checked internally that it isn't overwriting non-functions). + add_object(name_, func, true /* overwrite */); + return *this; + } + + /** \rst + Create and return a new Python submodule with the given name and docstring. + This also works recursively, i.e. + + .. code-block:: cpp + + py::module m("example", "pybind11 example plugin"); + py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); + py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); + \endrst */ + module def_submodule(const char *name, const char *doc = nullptr) { + std::string full_name = std::string(PyModule_GetName(m_ptr)) + + std::string(".") + std::string(name); + auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); + if (doc && options::show_user_defined_docstrings()) + result.attr("__doc__") = pybind11::str(doc); + attr(name) = result; + return result; + } + + /// Import and return a module or throws `error_already_set`. + static module import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) + throw error_already_set(); + return reinterpret_steal(obj); + } + + /// Reload the module or throws `error_already_set`. + void reload() { + PyObject *obj = PyImport_ReloadModule(ptr()); + if (!obj) + throw error_already_set(); + *this = reinterpret_steal(obj); + } + + // Adds an object to the module using the given name. Throws if an object with the given name + // already exists. + // + // overwrite should almost always be false: attempting to overwrite objects that pybind11 has + // established will, in most cases, break things. + PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { + if (!overwrite && hasattr(*this, name)) + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); + } +}; + +/// \ingroup python_builtins +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); +} + +NAMESPACE_BEGIN(detail) +/// Generic support for creating new Python heap types +class generic_type : public object { + template friend class class_; +public: + PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) +protected: + void initialize(const type_record &rec) { + if (rec.scope && hasattr(rec.scope, rec.name)) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + + "\": an object with that name is already defined"); + + if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) + pybind11_fail("generic_type: type \"" + std::string(rec.name) + + "\" is already registered!"); + + m_ptr = make_new_python_type(rec); + + /* Register supplemental type information in C++ dict */ + auto *tinfo = new detail::type_info(); + tinfo->type = (PyTypeObject *) m_ptr; + tinfo->cpptype = rec.type; + tinfo->type_size = rec.type_size; + tinfo->operator_new = rec.operator_new; + tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); + tinfo->init_instance = rec.init_instance; + tinfo->dealloc = rec.dealloc; + tinfo->simple_type = true; + tinfo->simple_ancestors = true; + tinfo->default_holder = rec.default_holder; + tinfo->module_local = rec.module_local; + + auto &internals = get_internals(); + auto tindex = std::type_index(*rec.type); + tinfo->direct_conversions = &internals.direct_conversions[tindex]; + if (rec.module_local) + registered_local_types_cpp()[tindex] = tinfo; + else + internals.registered_types_cpp[tindex] = tinfo; + internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; + + if (rec.bases.size() > 1 || rec.multiple_inheritance) { + mark_parents_nonsimple(tinfo->type); + tinfo->simple_ancestors = false; + } + else if (rec.bases.size() == 1) { + auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); + tinfo->simple_ancestors = parent_tinfo->simple_ancestors; + } + + if (rec.module_local) { + // Stash the local typeinfo and loader so that external modules can access it. + tinfo->module_local_load = &type_caster_generic::local_load; + setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); + } + } + + /// Helper function which tags all parents of a type using mult. inheritance + void mark_parents_nonsimple(PyTypeObject *value) { + auto t = reinterpret_borrow(value->tp_bases); + for (handle h : t) { + auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); + if (tinfo2) + tinfo2->simple_type = false; + mark_parents_nonsimple((PyTypeObject *) h.ptr()); + } + } + + void install_buffer_funcs( + buffer_info *(*get_buffer)(PyObject *, void *), + void *get_buffer_data) { + PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; + auto tinfo = detail::get_type_info(&type->ht_type); + + if (!type->ht_type.tp_as_buffer) + pybind11_fail( + "To be able to register buffer protocol support for the type '" + + std::string(tinfo->type->tp_name) + + "' the associated class<>(..) invocation must " + "include the pybind11::buffer_protocol() annotation!"); + + tinfo->get_buffer = get_buffer; + tinfo->get_buffer_data = get_buffer_data; + } + + void def_property_static_impl(const char *name, + handle fget, handle fset, + detail::function_record *rec_fget) { + const auto is_static = !(rec_fget->is_method && rec_fget->scope); + const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings(); + + auto property = handle((PyObject *) (is_static ? get_internals().static_property_type + : &PyProperty_Type)); + attr(name) = property(fget.ptr() ? fget : none(), + fset.ptr() ? fset : none(), + /*deleter*/none(), + pybind11::str(has_doc ? rec_fget->doc : "")); + } +}; + +/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. +template (T::operator new))>> +void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } + +template void set_operator_new(...) { } + +template struct has_operator_delete : std::false_type { }; +template struct has_operator_delete(T::operator delete))>> + : std::true_type { }; +template struct has_operator_delete_size : std::false_type { }; +template struct has_operator_delete_size(T::operator delete))>> + : std::true_type { }; +/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. +template ::value, int> = 0> +void call_operator_delete(T *p, size_t) { T::operator delete(p); } +template ::value && has_operator_delete_size::value, int> = 0> +void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); } + +inline void call_operator_delete(void *p, size_t) { ::operator delete(p); } + +NAMESPACE_END(detail) + +/// Given a pointer to a member function, cast it to its `Derived` version. +/// Forward everything else unchanged. +template +auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } + +template +auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { return pmf; } + +template +auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { return pmf; } + +template +class class_ : public detail::generic_type { + template using is_holder = detail::is_holder_type; + template using is_subtype = detail::is_strict_base_of; + template using is_base = detail::is_strict_base_of; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; + +public: + using type = type_; + using type_alias = detail::exactly_one_t; + constexpr static bool has_alias = !std::is_void::value; + using holder_type = detail::exactly_one_t, options...>; + + static_assert(detail::all_of...>::value, + "Unknown/invalid class_ template parameters provided"); + + static_assert(!has_alias || std::is_polymorphic::value, + "Cannot use an alias class with a non-polymorphic type"); + + PYBIND11_OBJECT(class_, generic_type, PyType_Check) + + template + class_(handle scope, const char *name, const Extra &... extra) { + using namespace detail; + + // MI can only be specified via class_ template options, not constructor parameters + static_assert( + none_of...>::value || // no base class arguments, or: + ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base + constexpr_sum(is_base::value...) == 0 && // no template option bases + none_of...>::value), // no multiple_inheritance attr + "Error: multiple inheritance bases must be specified via class_ template options"); + + type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(conditional_t); + record.holder_size = sizeof(holder_type); + record.init_instance = init_instance; + record.dealloc = dealloc; + record.default_holder = std::is_same>::value; + + set_operator_new(&record); + + /* Register base classes specified via template arguments to class_, if any */ + PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); + + /* Process optional arguments, if any */ + process_attributes::init(extra..., &record); + + generic_type::initialize(record); + + if (has_alias) { + auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; + instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; + } + } + + template ::value, int> = 0> + static void add_base(detail::type_record &rec) { + rec.add_base(typeid(Base), [](void *src) -> void * { + return static_cast(reinterpret_cast(src)); + }); + } + + template ::value, int> = 0> + static void add_base(detail::type_record &) { } + + template + class_ &def(const char *name_, Func&& f, const Extra&... extra) { + cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template class_ & + def_static(const char *name_, Func &&f, const Extra&... extra) { + static_assert(!std::is_member_function_pointer::value, + "def_static(...) called with a non-static member function pointer"); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { + std::move(init).execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { + std::move(pf).execute(*this, extra...); + return *this; + } + + template class_& def_buffer(Func &&func) { + struct capture { Func func; }; + capture *ptr = new capture { std::forward(func) }; + install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { + detail::make_caster caster; + if (!caster.load(obj, false)) + return nullptr; + return new buffer_info(((capture *) ptr)->func(caster)); + }, ptr); + return *this; + } + + template + class_ &def_buffer(Return (Class::*func)(Args...)) { + return def_buffer([func] (type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_buffer(Return (Class::*func)(Args...) const) { + return def_buffer([func] (const type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { + static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), + fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); + def_property(name, fget, fset, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { + static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); + def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), + fset([pm](object, const D &value) { *pm = value; }, scope(*this)); + def_property_static(name, fget, fset, return_value_policy::reference, extra...); + return *this; + } + + template + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); + def_property_readonly_static(name, fget, return_value_policy::reference, extra...); + return *this; + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly(name, cpp_function(method_adaptor(fget)), + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property(name, fget, cpp_function(), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property_static(name, fget, cpp_function(), extra...); + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { + return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); + } + template + class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property(name, cpp_function(method_adaptor(fget)), fset, + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, fget, fset, is_method(*this), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); + char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ + detail::process_attributes::init(extra..., rec_fget); + if (rec_fget->doc && rec_fget->doc != doc_prev) { + free(doc_prev); + rec_fget->doc = strdup(rec_fget->doc); + } + if (rec_fset) { + doc_prev = rec_fset->doc; + detail::process_attributes::init(extra..., rec_fset); + if (rec_fset->doc && rec_fset->doc != doc_prev) { + free(doc_prev); + rec_fset->doc = strdup(rec_fset->doc); + } + } + def_property_static_impl(name, fget, fset, rec_fget); + return *this; + } + +private: + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { + try { + auto sh = std::dynamic_pointer_cast( + v_h.value_ptr()->shared_from_this()); + if (sh) { + new (&v_h.holder()) holder_type(std::move(sh)); + v_h.set_holder_constructed(); + } + } catch (const std::bad_weak_ptr &) {} + + if (!v_h.holder_constructed() && inst->owned) { + new (&v_h.holder()) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { + new (&v_h.holder()) holder_type(*reinterpret_cast(holder_ptr)); + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { + new (&v_h.holder()) holder_type(std::move(*const_cast(holder_ptr))); + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { + if (holder_ptr) { + init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); + v_h.set_holder_constructed(); + } else if (inst->owned || detail::always_construct_holder::value) { + new (&v_h.holder()) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + } + + /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. + static void dealloc(detail::value_and_holder &v_h) { + if (v_h.holder_constructed()) { + v_h.holder().~holder_type(); + v_h.set_holder_constructed(false); + } + else { + detail::call_operator_delete(v_h.value_ptr(), v_h.type->type_size); + } + v_h.value_ptr() = nullptr; + } + + static detail::function_record *get_function_record(handle h) { + h = detail::get_function(h); + return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) + : nullptr; + } +}; + +/// Binds an existing constructor taking arguments Args... +template detail::initimpl::constructor init() { return {}; } +/// Like `init()`, but the instance is always constructed through the alias class (even +/// when not inheriting on the Python side). +template detail::initimpl::alias_constructor init_alias() { return {}; } + +/// Binds a factory function as a constructor +template > +Ret init(Func &&f) { return {std::forward(f)}; } + +/// Dual-argument factory function: the first function is called when no alias is needed, the second +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. +template > +Ret init(CFunc &&c, AFunc &&a) { + return {std::forward(c), std::forward(a)}; +} + +/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type +/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. +template +detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { + return {std::forward(g), std::forward(s)}; +} + +/// Binds C++ enumerations and enumeration classes to Python +template class enum_ : public class_ { +public: + using class_::def; + using class_::def_property_readonly_static; + using Scalar = typename std::underlying_type::type; + + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_entries(), m_parent(scope) { + + constexpr bool is_arithmetic = detail::any_of...>::value; + + auto m_entries_ptr = m_entries.inc_ref().ptr(); + def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str { + for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { + if (pybind11::cast(kv.second) == value) + return pybind11::str("{}.{}").format(name, kv.first); + } + return pybind11::str("{}.???").format(name); + }); + def_property_readonly_static("__members__", [m_entries_ptr](object /* self */) { + dict m; + for (const auto &kv : reinterpret_borrow(m_entries_ptr)) + m[kv.first] = kv.second; + return m; + }, return_value_policy::copy); + def(init([](Scalar i) { return static_cast(i); })); + def("__int__", [](Type value) { return (Scalar) value; }); + #if PY_MAJOR_VERSION < 3 + def("__long__", [](Type value) { return (Scalar) value; }); + #endif + def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; }); + def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; }); + if (is_arithmetic) { + def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; }); + def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; }); + def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; }); + def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; }); + } + if (std::is_convertible::value) { + // Don't provide comparison with the underlying type if the enum isn't convertible, + // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly + // convert Type to Scalar below anyway because this needs to compile). + def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; }); + def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; }); + if (is_arithmetic) { + def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; }); + def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; }); + def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; }); + def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; }); + def("__invert__", [](const Type &value) { return ~((Scalar) value); }); + def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); + def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); + def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); + def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); + def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); + def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); + def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; }); + def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; }); + def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; }); + } + } + def("__hash__", [](const Type &value) { return (Scalar) value; }); + // Pickling and unpickling -- needed for use with the 'multiprocessing' module + def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); }, + [](tuple t) { return static_cast(t[0].cast()); })); + } + + /// Export enumeration entries into the parent scope + enum_& export_values() { + for (const auto &kv : m_entries) + m_parent.attr(kv.first) = kv.second; + return *this; + } + + /// Add an enumeration entry + enum_& value(char const* name, Type value) { + auto v = pybind11::cast(value, return_value_policy::copy); + this->attr(name) = v; + m_entries[pybind11::str(name)] = v; + return *this; + } + +private: + dict m_entries; + handle m_parent; +}; + +NAMESPACE_BEGIN(detail) + + +inline void keep_alive_impl(handle nurse, handle patient) { + if (!nurse || !patient) + pybind11_fail("Could not activate keep_alive!"); + + if (patient.is_none() || nurse.is_none()) + return; /* Nothing to keep alive or nothing to be kept alive by */ + + auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); + if (!tinfo.empty()) { + /* It's a pybind-registered type, so we can store the patient in the + * internal list. */ + add_patient(nurse.ptr(), patient.ptr()); + } + else { + /* Fall back to clever approach based on weak references taken from + * Boost.Python. This is not used for pybind-registered types because + * the objects can be destroyed out-of-order in a GC pass. */ + cpp_function disable_lifesupport( + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); + + weakref wr(nurse, disable_lifesupport); + + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); + } +} + +PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { + auto get_arg = [&](size_t n) { + if (n == 0) + return ret; + else if (n == 1 && call.init_self) + return call.init_self; + else if (n <= call.args.size()) + return call.args[n - 1]; + return handle(); + }; + + keep_alive_impl(get_arg(Nurse), get_arg(Patient)); +} + +inline std::pair all_type_info_get_cache(PyTypeObject *type) { + auto res = get_internals().registered_types_py +#ifdef __cpp_lib_unordered_map_try_emplace + .try_emplace(type); +#else + .emplace(type, std::vector()); +#endif + if (res.second) { + // New cache entry created; set up a weak reference to automatically remove it if the type + // gets destroyed: + weakref((PyObject *) type, cpp_function([type](handle wr) { + get_internals().registered_types_py.erase(type); + wr.dec_ref(); + })).release(); + } + + return res; +} + +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +NAMESPACE_END(detail) + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ()), + typename... Extra> +iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return *s.it; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ()).first), + typename... Extra> +iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> KeyType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return (*s.it).first; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an iterator over values of an stl container or other container supporting +/// `std::begin()`/`std::end()` +template iterator make_iterator(Type &value, Extra&&... extra) { + return make_iterator(std::begin(value), std::end(value), extra...); +} + +/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting +/// `std::begin()`/`std::end()` +template iterator make_key_iterator(Type &value, Extra&&... extra) { + return make_key_iterator(std::begin(value), std::end(value), extra...); +} + +template void implicitly_convertible() { + struct set_flag { + bool &flag; + set_flag(bool &flag) : flag(flag) { flag = true; } + ~set_flag() { flag = false; } + }; + auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { + static bool currently_used = false; + if (currently_used) // implicit conversions are non-reentrant + return nullptr; + set_flag flag_helper(currently_used); + if (!detail::make_caster().load(obj, false)) + return nullptr; + tuple args(1); + args[0] = obj; + PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); + if (result == nullptr) + PyErr_Clear(); + return result; + }; + + if (auto tinfo = detail::get_type_info(typeid(OutputType))) + tinfo->implicit_conversions.push_back(implicit_caster); + else + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); +} + +template +void register_exception_translator(ExceptionTranslator&& translator) { + detail::get_internals().registered_exception_translators.push_front( + std::forward(translator)); +} + +/** + * Wrapper to generate a new Python exception type. + * + * This should only be used with PyErr_SetString for now. + * It is not (yet) possible to use as a py::base. + * Template type argument is reserved for future use. + */ +template +class exception : public object { +public: + exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { + std::string full_name = scope.attr("__name__").cast() + + std::string(".") + name; + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); + if (hasattr(scope, name)) + pybind11_fail("Error during initialization: multiple incompatible " + "definitions with name \"" + std::string(name) + "\""); + scope.attr(name) = *this; + } + + // Sets the current python exception to this exception object with the given message + void operator()(const char *message) { + PyErr_SetString(m_ptr, message); + } +}; + +/** + * Registers a Python exception in `m` of the given `name` and installs an exception translator to + * translate the C++ exception to the created Python exception using the exceptions what() method. + * This is intended for simple exception translations; for more complex translation, register the + * exception object and translator directly. + */ +template +exception ®ister_exception(handle scope, + const char *name, + PyObject *base = PyExc_Exception) { + static exception ex(scope, name, base); + register_exception_translator([](std::exception_ptr p) { + if (!p) return; + try { + std::rethrow_exception(p); + } catch (const CppException &e) { + ex(e.what()); + } + }); + return ex; +} + +NAMESPACE_BEGIN(detail) +PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { + auto strings = tuple(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + strings[i] = str(args[i]); + } + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); + auto line = sep.attr("join")(strings); + + object file; + if (kwargs.contains("file")) { + file = kwargs["file"].cast(); + } else { + try { + file = module::import("sys").attr("stdout"); + } catch (const error_already_set &) { + /* If print() is called from code that is executed as + part of garbage collection during interpreter shutdown, + importing 'sys' can fail. Give up rather than crashing the + interpreter in this case. */ + return; + } + } + + auto write = file.attr("write"); + write(line); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); + + if (kwargs.contains("flush") && kwargs["flush"].cast()) + file.attr("flush")(); +} +NAMESPACE_END(detail) + +template +void print(Args &&...args) { + auto c = detail::collect_arguments(std::forward(args)...); + detail::print(c.args(), c.kwargs()); +} + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + +/* The functions below essentially reproduce the PyGILState_* API using a RAII + * pattern, but there are a few important differences: + * + * 1. When acquiring the GIL from an non-main thread during the finalization + * phase, the GILState API blindly terminates the calling thread, which + * is often not what is wanted. This API does not do this. + * + * 2. The gil_scoped_release function can optionally cut the relationship + * of a PyThreadState and its associated thread, which allows moving it to + * another thread (this is a fairly rare/advanced use case). + * + * 3. The reference count of an acquired thread state can be controlled. This + * can be handy to prevent cases where callbacks issued from an external + * thread would otherwise constantly construct and destroy thread state data + * structures. + * + * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an + * example which uses features 2 and 3 to migrate the Python thread of + * execution to another thread (to run the event loop on the original thread, + * in this case). + */ + +class gil_scoped_acquire { +public: + PYBIND11_NOINLINE gil_scoped_acquire() { + auto const &internals = detail::get_internals(); + tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate); + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); + #if !defined(NDEBUG) + if (!tstate) + pybind11_fail("scoped_acquire: could not create thread state!"); + #endif + tstate->gilstate_counter = 0; + #if PY_MAJOR_VERSION < 3 + PyThread_delete_key_value(internals.tstate); + #endif + PyThread_set_key_value(internals.tstate, tstate); + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + /* Work around an annoying assertion in PyThreadState_Swap */ + #if defined(Py_DEBUG) + PyInterpreterState *interp = tstate->interp; + tstate->interp = nullptr; + #endif + PyEval_AcquireThread(tstate); + #if defined(Py_DEBUG) + tstate->interp = interp; + #endif + } + + inc_ref(); + } + + void inc_ref() { + ++tstate->gilstate_counter; + } + + PYBIND11_NOINLINE void dec_ref() { + --tstate->gilstate_counter; + #if !defined(NDEBUG) + if (detail::get_thread_state_unchecked() != tstate) + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + if (tstate->gilstate_counter < 0) + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + #endif + if (tstate->gilstate_counter == 0) { + #if !defined(NDEBUG) + if (!release) + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + #endif + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PyThread_delete_key_value(detail::get_internals().tstate); + release = false; + } + } + + PYBIND11_NOINLINE ~gil_scoped_acquire() { + dec_ref(); + if (release) + PyEval_SaveThread(); + } +private: + PyThreadState *tstate = nullptr; + bool release = true; +}; + +class gil_scoped_release { +public: + explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + const auto &internals = detail::get_internals(); + tstate = PyEval_SaveThread(); + if (disassoc) { + auto key = internals.tstate; + #if PY_MAJOR_VERSION < 3 + PyThread_delete_key_value(key); + #else + PyThread_set_key_value(key, nullptr); + #endif + } + } + ~gil_scoped_release() { + if (!tstate) + return; + PyEval_RestoreThread(tstate); + if (disassoc) { + auto key = detail::get_internals().tstate; + #if PY_MAJOR_VERSION < 3 + PyThread_delete_key_value(key); + #endif + PyThread_set_key_value(key, tstate); + } + } +private: + PyThreadState *tstate; + bool disassoc; +}; +#elif defined(PYPY_VERSION) +class gil_scoped_acquire { + PyGILState_STATE state; +public: + gil_scoped_acquire() { state = PyGILState_Ensure(); } + ~gil_scoped_acquire() { PyGILState_Release(state); } +}; + +class gil_scoped_release { + PyThreadState *state; +public: + gil_scoped_release() { state = PyEval_SaveThread(); } + ~gil_scoped_release() { PyEval_RestoreThread(state); } +}; +#else +class gil_scoped_acquire { }; +class gil_scoped_release { }; +#endif + +error_already_set::~error_already_set() { + if (type) { + gil_scoped_acquire gil; + type.release().dec_ref(); + value.release().dec_ref(); + trace.release().dec_ref(); + } +} + +inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { + handle self = detail::get_object_handle(this_ptr, this_type); + if (!self) + return function(); + handle type = self.get_type(); + auto key = std::make_pair(type.ptr(), name); + + /* Cache functions that aren't overloaded in Python to avoid + many costly Python dictionary lookups below */ + auto &cache = detail::get_internals().inactive_overload_cache; + if (cache.find(key) != cache.end()) + return function(); + + function overload = getattr(self, name, function()); + if (overload.is_cpp_function()) { + cache.insert(key); + return function(); + } + + /* Don't call dispatch code if invoked from overridden function. + Unfortunately this doesn't work on PyPy. */ +#if !defined(PYPY_VERSION) + PyFrameObject *frame = PyThreadState_Get()->frame; + if (frame && (std::string) str(frame->f_code->co_name) == name && + frame->f_code->co_argcount > 0) { + PyFrame_FastToLocals(frame); + PyObject *self_caller = PyDict_GetItem( + frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); + if (self_caller == self.ptr()) + return function(); + } +#else + /* PyPy currently doesn't provide a detailed cpyext emulation of + frame objects, so we have to emulate this using Python. This + is going to be slow..*/ + dict d; d["self"] = self; d["name"] = pybind11::str(name); + PyObject *result = PyRun_String( + "import inspect\n" + "frame = inspect.currentframe()\n" + "if frame is not None:\n" + " frame = frame.f_back\n" + " if frame is not None and str(frame.f_code.co_name) == name and " + "frame.f_code.co_argcount > 0:\n" + " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" + " if self_caller == self:\n" + " self = None\n", + Py_file_input, d.ptr(), d.ptr()); + if (result == nullptr) + throw error_already_set(); + if (d["self"].is_none()) + return function(); + Py_DECREF(result); +#endif + + return overload; +} + +template function get_overload(const T *this_ptr, const char *name) { + auto tinfo = detail::get_type_info(typeid(T)); + return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); +} + +#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ + pybind11::gil_scoped_acquire gil; \ + pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ + if (overload) { \ + auto o = overload(__VA_ARGS__); \ + if (pybind11::detail::cast_is_temporary_value_reference::value) { \ + static pybind11::detail::overload_caster_t caster; \ + return pybind11::detail::cast_ref(std::move(o), caster); \ + } \ + else return pybind11::detail::cast_safe(std::move(o)); \ + } \ + } + +#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ + return cname::fn(__VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ + pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\""); + +#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) + +#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__INTEL_COMPILER) +/* Leave ignored warnings on */ +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif diff --git a/external/pybind11/include/pybind11/pytypes.h b/external/pybind11/include/pybind11/pytypes.h new file mode 100644 index 0000000..d7fa177 --- /dev/null +++ b/external/pybind11/include/pybind11/pytypes.h @@ -0,0 +1,1332 @@ +/* + pybind11/pytypes.h: Convenience wrapper classes for basic Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "buffer_info.h" +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/* A few forward declarations */ +class handle; class object; +class str; class iterator; +struct arg; struct arg_v; + +NAMESPACE_BEGIN(detail) +class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); + +// Accessor forward declarations +template class accessor; +namespace accessor_policies { + struct obj_attr; + struct str_attr; + struct generic_item; + struct sequence_item; + struct list_item; + struct tuple_item; +} +using obj_attr_accessor = accessor; +using str_attr_accessor = accessor; +using item_accessor = accessor; +using sequence_accessor = accessor; +using list_accessor = accessor; +using tuple_accessor = accessor; + +/// Tag and check to identify a class which implements the Python object API +class pyobject_tag { }; +template using is_pyobject = std::is_base_of>; + +/** \rst + A mixin class which adds common functions to `handle`, `object` and various accessors. + The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. +\endrst */ +template +class object_api : public pyobject_tag { + const Derived &derived() const { return static_cast(*this); } + +public: + /** \rst + Return an iterator equivalent to calling ``iter()`` in Python. The object + must be a collection which supports the iteration protocol. + \endrst */ + iterator begin() const; + /// Return a sentinel which ends iteration. + iterator end() const; + + /** \rst + Return an internal functor to invoke the object's sequence protocol. Casting + the returned ``detail::item_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` + or `object` subclass causes a call to ``__setitem__``. + \endrst */ + item_accessor operator[](handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + item_accessor operator[](const char *key) const; + + /** \rst + Return an internal functor to access the object's attributes. Casting the + returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``getattr``. Assigning a `handle` + or `object` subclass causes a call to ``setattr``. + \endrst */ + obj_attr_accessor attr(handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + str_attr_accessor attr(const char *key) const; + + /** \rst + Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` + or ``list`` for a function call. Applying another * to the result yields + ** unpacking, e.g. to unpack a dict as function keyword arguments. + See :ref:`calling_python_functions`. + \endrst */ + args_proxy operator*() const; + + /// Check if the given item is contained within this object, i.e. ``item in obj``. + template bool contains(T &&item) const; + + /** \rst + Assuming the Python object is a function or implements the ``__call__`` + protocol, ``operator()`` invokes the underlying function, passing an + arbitrary set of parameters. The result is returned as a `object` and + may need to be converted back into a Python object using `handle::cast()`. + + When some of the arguments cannot be converted to Python objects, the + function will throw a `cast_error` exception. When the Python function + call fails, a `error_already_set` exception is thrown. + \endrst */ + template + object operator()(Args &&...args) const; + template + PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") + object call(Args&&... args) const; + + /// Equivalent to ``obj is other`` in Python. + bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } + /// Equivalent to ``obj is None`` in Python. + bool is_none() const { return derived().ptr() == Py_None; } + PYBIND11_DEPRECATED("Use py::str(obj) instead") + pybind11::str str() const; + + /// Get or set the object's docstring, i.e. ``obj.__doc__``. + str_attr_accessor doc() const; + + /// Return the object's current reference count + int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } + /// Return a handle to the Python type object underlying the instance + handle get_type() const; +}; + +NAMESPACE_END(detail) + +/** \rst + Holds a reference to a Python object (no reference counting) + + The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a + ``PyObject *`` in Python's C API). It does not perform any automatic reference + counting and merely provides a basic C++ interface to various Python API functions. + + .. seealso:: + The `object` class inherits from `handle` and adds automatic reference + counting features. +\endrst */ +class handle : public detail::object_api { +public: + /// The default constructor creates a handle with a ``nullptr``-valued pointer + handle() = default; + /// Creates a ``handle`` from the given raw Python object pointer + handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* + + /// Return the underlying ``PyObject *`` pointer + PyObject *ptr() const { return m_ptr; } + PyObject *&ptr() { return m_ptr; } + + /** \rst + Manually increase the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } + + /** \rst + Manually decrease the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } + + /** \rst + Attempt to cast the Python object into the given C++ type. A `cast_error` + will be throw upon failure. + \endrst */ + template T cast() const; + /// Return ``true`` when the `handle` wraps a valid Python object + explicit operator bool() const { return m_ptr != nullptr; } + /** \rst + Deprecated: Check that the underlying pointers are the same. + Equivalent to ``obj1 is obj2`` in Python. + \endrst */ + PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") + bool check() const { return m_ptr != nullptr; } +protected: + PyObject *m_ptr = nullptr; +}; + +/** \rst + Holds a reference to a Python object (with reference counting) + + Like `handle`, the `object` class is a thin wrapper around an arbitrary Python + object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it + optionally increases the object's reference count upon construction, and it + *always* decreases the reference count when the `object` instance goes out of + scope and is destructed. When using `object` instances consistently, it is much + easier to get reference counting right at the first attempt. +\endrst */ +class object : public handle { +public: + object() = default; + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } + /// Copy constructor; always increases the reference count + object(const object &o) : handle(o) { inc_ref(); } + /// Move constructor; steals the object from ``other`` and preserves its reference count + object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } + /// Destructor; automatically calls `handle::dec_ref()` + ~object() { dec_ref(); } + + /** \rst + Resets the internal pointer to ``nullptr`` without without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. + \endrst */ + handle release() { + PyObject *tmp = m_ptr; + m_ptr = nullptr; + return handle(tmp); + } + + object& operator=(const object &other) { + other.inc_ref(); + dec_ref(); + m_ptr = other.m_ptr; + return *this; + } + + object& operator=(object &&other) noexcept { + if (this != &other) { + handle temp(m_ptr); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + temp.dec_ref(); + } + return *this; + } + + // Calling cast() on an object lvalue just copies (via handle::cast) + template T cast() const &; + // Calling on an object rvalue does a move, if needed and/or possible + template T cast() &&; + +protected: + // Tags for choosing constructors from raw PyObject * + struct borrowed_t { }; + struct stolen_t { }; + + template friend T reinterpret_borrow(handle); + template friend T reinterpret_steal(handle); + +public: + // Only accessible from derived classes and the reinterpret_* functions + object(handle h, borrowed_t) : handle(h) { inc_ref(); } + object(handle h, stolen_t) : handle(h) { } +}; + +/** \rst + Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. + The target type ``T`` must be `object` or one of its derived classes. The function + doesn't do any conversions or checks. It's up to the user to make sure that the + target type is correct. + + .. code-block:: cpp + + PyObject *p = PyList_GetItem(obj, index); + py::object o = reinterpret_borrow(p); + // or + py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` +\endrst */ +template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } + +/** \rst + Like `reinterpret_borrow`, but steals the reference. + + .. code-block:: cpp + + PyObject *p = PyObject_Str(obj); + py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` +\endrst */ +template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } + +NAMESPACE_BEGIN(detail) +inline std::string error_string(); +NAMESPACE_END(detail) + +/// Fetch and hold an error which was already set in Python. An instance of this is typically +/// thrown to propagate python-side errors back through C++ which can either be caught manually or +/// else falls back to the function dispatcher (which then raises the captured error back to +/// python). +class error_already_set : public std::runtime_error { +public: + /// Constructs a new exception from the current Python error indicator, if any. The current + /// Python error indicator will be cleared. + error_already_set() : std::runtime_error(detail::error_string()) { + PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); + } + + inline ~error_already_set(); + + /// Give the currently-held error back to Python, if any. If there is currently a Python error + /// already set it is cleared first. After this call, the current object no longer stores the + /// error variables (but the `.what()` string is still available). + void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } + + // Does nothing; provided for backwards compatibility. + PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") + void clear() {} + + /// Check if the currently trapped error type matches the given Python exception class (or a + /// subclass thereof). May also be passed a tuple to search for any exception class matches in + /// the given tuple. + bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } + +private: + object type, value, trace; +}; + +/** \defgroup python_builtins _ + Unless stated otherwise, the following C++ functions behave the same + as their Python counterparts. + */ + +/** \ingroup python_builtins + \rst + Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of + `object` or a class which was exposed to Python as ``py::class_``. +\endrst */ +template ::value, int> = 0> +bool isinstance(handle obj) { return T::check_(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + +/// \ingroup python_builtins +/// Return true if ``obj`` is an instance of the ``type``. +inline bool isinstance(handle obj, handle type) { + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + +/// \addtogroup python_builtins +/// @{ +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} + +inline ssize_t hash(handle obj) { + auto h = PyObject_Hash(obj.ptr()); + if (h == -1) { throw error_already_set(); } + return h; +} + +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +inline handle get_function(handle value) { + if (value) { +#if PY_MAJOR_VERSION >= 3 + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); + else +#endif + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } + return value; +} + +// Helper aliases/functions to support implicit casting of values given to python accessors/methods. +// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes +// through pybind11::cast(obj) to convert it to an `object`. +template ::value, int> = 0> +auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } +// The following casting version is implemented in cast.h: +template ::value, int> = 0> +object object_or_cast(T &&o); +// Match a PyObject*, which we want to convert directly to handle via its converting constructor +inline handle object_or_cast(PyObject *ptr) { return ptr; } + + +template +class accessor : public object_api> { + using key_type = typename Policy::key_type; + +public: + accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } + accessor(const accessor &) = default; + accessor(accessor &&) = default; + + // accessor overload required to override default assignment operator (templates are not allowed + // to replace default compiler-generated assignments). + void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } + void operator=(const accessor &a) & { operator=(handle(a)); } + + template void operator=(T &&value) && { + Policy::set(obj, key, object_or_cast(std::forward(value))); + } + template void operator=(T &&value) & { + get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); + } + + template + PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") + explicit operator enable_if_t::value || + std::is_same::value, bool>() const { + return hasattr(obj, key); + } + template + PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") + explicit operator enable_if_t::value, bool>() const { + return obj.contains(key); + } + + operator object() const { return get_cache(); } + PyObject *ptr() const { return get_cache().ptr(); } + template T cast() const { return get_cache().template cast(); } + +private: + object &get_cache() const { + if (!cache) { cache = Policy::get(obj, key); } + return cache; + } + +private: + handle obj; + key_type key; + mutable object cache; +}; + +NAMESPACE_BEGIN(accessor_policies) +struct obj_attr { + using key_type = object; + static object get(handle obj, handle key) { return getattr(obj, key); } + static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } +}; + +struct str_attr { + using key_type = const char *; + static object get(handle obj, const char *key) { return getattr(obj, key); } + static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } +}; + +struct generic_item { + using key_type = object; + + static object get(handle obj, handle key) { + PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, handle key, handle val) { + if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } + } +}; + +struct sequence_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, size_t index, handle val) { + // PySequence_SetItem does not steal a reference to 'val' + if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct list_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyList_SetItem steals a reference to 'val' + if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct tuple_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyTuple_SetItem steals a reference to 'val' + if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; +NAMESPACE_END(accessor_policies) + +/// STL iterator template used for tuple, list, sequence and dict +template +class generic_iterator : public Policy { + using It = generic_iterator; + +public: + using difference_type = ssize_t; + using iterator_category = typename Policy::iterator_category; + using value_type = typename Policy::value_type; + using reference = typename Policy::reference; + using pointer = typename Policy::pointer; + + generic_iterator() = default; + generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } + + reference operator*() const { return Policy::dereference(); } + reference operator[](difference_type n) const { return *(*this + n); } + pointer operator->() const { return **this; } + + It &operator++() { Policy::increment(); return *this; } + It operator++(int) { auto copy = *this; Policy::increment(); return copy; } + It &operator--() { Policy::decrement(); return *this; } + It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } + It &operator+=(difference_type n) { Policy::advance(n); return *this; } + It &operator-=(difference_type n) { Policy::advance(-n); return *this; } + + friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } + friend It operator+(difference_type n, const It &b) { return b + n; } + friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } + friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } + + friend bool operator==(const It &a, const It &b) { return a.equal(b); } + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + friend bool operator< (const It &a, const It &b) { return b - a > 0; } + friend bool operator> (const It &a, const It &b) { return b < a; } + friend bool operator>=(const It &a, const It &b) { return !(a < b); } + friend bool operator<=(const It &a, const It &b) { return !(a > b); } +}; + +NAMESPACE_BEGIN(iterator_policies) +/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers +template +struct arrow_proxy { + T value; + + arrow_proxy(T &&value) : value(std::move(value)) { } + T *operator->() const { return &value; } +}; + +/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` +class sequence_fast_readonly { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = handle; + using reference = const handle; + using pointer = arrow_proxy; + + sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } + + reference dereference() const { return *ptr; } + void increment() { ++ptr; } + void decrement() { --ptr; } + void advance(ssize_t n) { ptr += n; } + bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } + ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } + +private: + PyObject **ptr; +}; + +/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` +class sequence_slow_readwrite { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = object; + using reference = sequence_accessor; + using pointer = arrow_proxy; + + sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } + + reference dereference() const { return {obj, static_cast(index)}; } + void increment() { ++index; } + void decrement() { --index; } + void advance(ssize_t n) { index += n; } + bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } + ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } + +private: + handle obj; + ssize_t index; +}; + +/// Python's dictionary protocol permits this to be a forward iterator +class dict_readonly { +protected: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using reference = const value_type; + using pointer = arrow_proxy; + + dict_readonly() = default; + dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } + + reference dereference() const { return {key, value}; } + void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } + bool equal(const dict_readonly &b) const { return pos == b.pos; } + +private: + handle obj; + PyObject *key, *value; + ssize_t pos = -1; +}; +NAMESPACE_END(iterator_policies) + +#if !defined(PYPY_VERSION) +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#else +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#endif + +using sequence_iterator = generic_iterator; +using dict_iterator = generic_iterator; + +inline bool PyIterable_Check(PyObject *obj) { + PyObject *iter = PyObject_GetIter(obj); + if (iter) { + Py_DECREF(iter); + return true; + } else { + PyErr_Clear(); + return false; + } +} + +inline bool PyNone_Check(PyObject *o) { return o == Py_None; } + +inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +/// Python argument categories (using PEP 448 terms) +template using is_keyword = std::is_base_of; +template using is_s_unpacking = std::is_same; // * unpacking +template using is_ds_unpacking = std::is_same; // ** unpacking +template using is_positional = satisfies_none_of; +template using is_keyword_or_ds = satisfies_any_of; + +// Call argument collector forward declarations +template +class simple_collector; +template +class unpacking_collector; + +NAMESPACE_END(detail) + +// TODO: After the deprecated constructors are removed, this macro can be simplified by +// inheriting ctors: `using Parent::Parent`. It's not an option right now because +// the `using` statement triggers the parent deprecation warning even if the ctor +// isn't even used. +#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + public: \ + PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ + Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ + Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ + Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } + +#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) \ + : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + Name(object &&o) \ + : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + template \ + Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } + +#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(o) { } \ + Name(object &&o) : Parent(std::move(o)) { } + +#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT(Name, Parent, CheckFun) \ + Name() : Parent() { } + +/// \addtogroup pytypes +/// @{ + +/** \rst + Wraps a Python iterator so that it can also be used as a C++ input iterator + + Caveat: copying an iterator does not (and cannot) clone the internal + state of the Python iterable. This also applies to the post-increment + operator. This iterator should only be used to retrieve the current + value using ``operator*()``. +\endrst */ +class iterator : public object { +public: + using iterator_category = std::input_iterator_tag; + using difference_type = ssize_t; + using value_type = handle; + using reference = const handle; + using pointer = const handle *; + + PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) + + iterator& operator++() { + advance(); + return *this; + } + + iterator operator++(int) { + auto rv = *this; + advance(); + return rv; + } + + reference operator*() const { + if (m_ptr && !value.ptr()) { + auto& self = const_cast(*this); + self.advance(); + } + return value; + } + + pointer operator->() const { operator*(); return &value; } + + /** \rst + The value which marks the end of the iteration. ``it == iterator::sentinel()`` + is equivalent to catching ``StopIteration`` in Python. + + .. code-block:: cpp + + void foo(py::iterator it) { + while (it != py::iterator::sentinel()) { + // use `*it` + ++it; + } + } + \endrst */ + static iterator sentinel() { return {}; } + + friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } + friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } + +private: + void advance() { + value = reinterpret_steal(PyIter_Next(m_ptr)); + if (PyErr_Occurred()) { throw error_already_set(); } + } + +private: + object value = {}; +}; + +class iterable : public object { +public: + PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) +}; + +class bytes; + +class str : public object { +public: + PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) + + str(const char *c, size_t n) + : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects + str(const char *c = "") + : object(PyUnicode_FromString(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + str(const std::string &s) : str(s.data(), s.size()) { } + + explicit str(const bytes &b); + + /** \rst + Return a string representation of the object. This is analogous to + the ``str()`` function in Python. + \endrst */ + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } + + operator std::string() const { + object temp = *this; + if (PyUnicode_Check(m_ptr)) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + return std::string(buffer, (size_t) length); + } + + template + str format(Args &&...args) const { + return attr("format")(std::forward(args)...); + } + +private: + /// Return string representation -- always returns a new reference, even if already a str + static PyObject *raw_str(PyObject *op) { + PyObject *str_value = PyObject_Str(op); +#if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; +#endif + return str_value; + } +}; +/// @} pytypes + +inline namespace literals { +/** \rst + String literal version of `str` + \endrst */ +inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +} + +/// \addtogroup pytypes +/// @{ +class bytes : public object { +public: + PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) + + // Allow implicit conversion: + bytes(const char *c = "") + : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + bytes(const char *c, size_t n) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + // Allow implicit conversion: + bytes(const std::string &s) : bytes(s.data(), s.size()) { } + + explicit bytes(const pybind11::str &s); + + operator std::string() const { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + return std::string(buffer, (size_t) length); + } +}; + +inline bytes::bytes(const pybind11::str &s) { + object temp = s; + if (PyUnicode_Check(s.ptr())) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); + if (!obj) + pybind11_fail("Could not allocate bytes object!"); + m_ptr = obj.release().ptr(); +} + +inline str::str(const bytes& b) { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); + if (!obj) + pybind11_fail("Could not allocate string object!"); + m_ptr = obj.release().ptr(); +} + +class none : public object { +public: + PYBIND11_OBJECT(none, object, detail::PyNone_Check) + none() : object(Py_None, borrowed_t{}) { } +}; + +class bool_ : public object { +public: + PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) + bool_() : object(Py_False, borrowed_t{}) { } + // Allow implicit conversion from and to `bool`: + bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } + operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } + +private: + /// Return the truth value of an object -- always returns a new reference + static PyObject *raw_bool(PyObject *op) { + const auto value = PyObject_IsTrue(op); + if (value == -1) return nullptr; + return handle(value ? Py_True : Py_False).inc_ref().ptr(); + } +}; + +NAMESPACE_BEGIN(detail) +// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; +// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). +// (The distinction is critically important when casting a returned -1 error value to some other +// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). +template +Unsigned as_unsigned(PyObject *o) { + if (sizeof(Unsigned) <= sizeof(unsigned long) +#if PY_VERSION_HEX < 0x03000000 + || PyInt_Check(o) +#endif + ) { + unsigned long v = PyLong_AsUnsignedLong(o); + return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } + else { + unsigned long long v = PyLong_AsUnsignedLongLong(o); + return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } +} +NAMESPACE_END(detail) + +class int_ : public object { +public: + PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) + int_() : object(PyLong_FromLong(0), stolen_t{}) { } + // Allow implicit conversion from C++ integral types: + template ::value, int> = 0> + int_(T value) { + if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + m_ptr = PyLong_FromLong((long) value); + else + m_ptr = PyLong_FromUnsignedLong((unsigned long) value); + } else { + if (std::is_signed::value) + m_ptr = PyLong_FromLongLong((long long) value); + else + m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); + } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); + } + + template ::value, int> = 0> + operator T() const { + return std::is_unsigned::value + ? detail::as_unsigned(m_ptr) + : sizeof(T) <= sizeof(long) + ? (T) PyLong_AsLong(m_ptr) + : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); + } +}; + +class float_ : public object { +public: + PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) + // Allow implicit conversion from float/double: + float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + operator float() const { return (float) PyFloat_AsDouble(m_ptr); } + operator double() const { return (double) PyFloat_AsDouble(m_ptr); } +}; + +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + explicit weakref(handle obj, handle callback = {}) + : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); + } +}; + +class slice : public object { +public: + PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) + slice(ssize_t start_, ssize_t stop_, ssize_t step_) { + int_ start(start_), stop(stop_), step(step_); + m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + if (!m_ptr) pybind11_fail("Could not allocate slice object!"); + } + bool compute(size_t length, size_t *start, size_t *stop, size_t *step, + size_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + (ssize_t) length, (ssize_t *) start, + (ssize_t *) stop, (ssize_t *) step, + (ssize_t *) slicelength) == 0; + } +}; + +class capsule : public object { +public: + PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } + + explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) + : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") + capsule(const void *value, void (*destruct)(PyObject *)) + : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + capsule(const void *value, void (*destructor)(void *)) { + m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); + void *ptr = PyCapsule_GetPointer(o, nullptr); + destructor(ptr); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + + if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) + pybind11_fail("Could not set capsule context!"); + } + + capsule(void (*destructor)()) { + m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + destructor(); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + template operator T *() const { + auto name = this->name(); + T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); + if (!result) pybind11_fail("Unable to extract capsule contents!"); + return result; + } + + const char *name() const { return PyCapsule_GetName(m_ptr); } +}; + +class tuple : public object { +public: + PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::tuple_iterator begin() const { return {*this, 0}; } + detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } +}; + +class dict : public object { +public: + PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) + dict() : object(PyDict_New(), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } + template ...>::value>, + // MSVC workaround: it can't compile an out-of-line definition, so defer the collector + typename collector = detail::deferred_t, Args...>> + explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } + + size_t size() const { return (size_t) PyDict_Size(m_ptr); } + detail::dict_iterator begin() const { return {*this, 0}; } + detail::dict_iterator end() const { return {}; } + void clear() const { PyDict_Clear(ptr()); } + bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } + bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } + +private: + /// Call the `dict` Python type -- always returns a new reference + static PyObject *raw_dict(PyObject *op) { + if (PyDict_Check(op)) + return handle(op).inc_ref().ptr(); + return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); + } +}; + +class sequence : public object { +public: + PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) + size_t size() const { return (size_t) PySequence_Size(m_ptr); } + detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::sequence_iterator begin() const { return {*this, 0}; } + detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } +}; + +class list : public object { +public: + PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) + explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } + detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::list_iterator begin() const { return {*this, 0}; } + detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } + template void append(T &&val) const { + PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + } +}; + +class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; +class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; + +class set : public object { +public: + PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) + set() : object(PySet_New(nullptr), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } + template bool add(T &&val) const { + return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; + } + void clear() const { PySet_Clear(m_ptr); } +}; + +class function : public object { +public: + PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) + handle cpp_function() const { + handle fun = detail::get_function(m_ptr); + if (fun && PyCFunction_Check(fun.ptr())) + return fun; + return handle(); + } + bool is_cpp_function() const { return (bool) cpp_function(); } +}; + +class buffer : public object { +public: + PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) + + buffer_info request(bool writable = false) { + int flags = PyBUF_STRIDES | PyBUF_FORMAT; + if (writable) flags |= PyBUF_WRITABLE; + Py_buffer *view = new Py_buffer(); + if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { + delete view; + throw error_already_set(); + } + return buffer_info(view); + } +}; + +class memoryview : public object { +public: + explicit memoryview(const buffer_info& info) { + static Py_buffer buf { }; + // Py_buffer uses signed sizes, strides and shape!.. + static std::vector py_strides { }; + static std::vector py_shape { }; + buf.buf = info.ptr; + buf.itemsize = info.itemsize; + buf.format = const_cast(info.format.c_str()); + buf.ndim = (int) info.ndim; + buf.len = info.size; + py_strides.clear(); + py_shape.clear(); + for (size_t i = 0; i < (size_t) info.ndim; ++i) { + py_strides.push_back(info.strides[i]); + py_shape.push_back(info.shape[i]); + } + buf.strides = py_strides.data(); + buf.shape = py_shape.data(); + buf.suboffsets = nullptr; + buf.readonly = false; + buf.internal = nullptr; + + m_ptr = PyMemoryView_FromBuffer(&buf); + if (!m_ptr) + pybind11_fail("Unable to create memoryview from buffer descriptor"); + } + + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) +}; +/// @} pytypes + +/// \addtogroup python_builtins +/// @{ +inline size_t len(handle h) { + ssize_t result = PyObject_Length(h.ptr()); + if (result < 0) + pybind11_fail("Unable to compute length of object"); + return (size_t) result; +} + +inline str repr(handle h) { + PyObject *str_value = PyObject_Repr(h.ptr()); + if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; + if (!str_value) throw error_already_set(); +#endif + return reinterpret_steal(str_value); +} + +inline iterator iter(handle obj) { + PyObject *result = PyObject_GetIter(obj.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +template iterator object_api::begin() const { return iter(derived()); } +template iterator object_api::end() const { return iterator::sentinel(); } +template item_accessor object_api::operator[](handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template item_accessor object_api::operator[](const char *key) const { + return {derived(), pybind11::str(key)}; +} +template obj_attr_accessor object_api::attr(handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template str_attr_accessor object_api::attr(const char *key) const { + return {derived(), key}; +} +template args_proxy object_api::operator*() const { + return args_proxy(derived().ptr()); +} +template template bool object_api::contains(T &&item) const { + return attr("__contains__")(std::forward(item)).template cast(); +} + +template +pybind11::str object_api::str() const { return pybind11::str(derived()); } + +template +str_attr_accessor object_api::doc() const { return attr("__doc__"); } + +template +handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/include/pybind11/stl.h b/external/pybind11/include/pybind11/stl.h new file mode 100644 index 0000000..db900e6 --- /dev/null +++ b/external/pybind11/include/pybind11/stl.h @@ -0,0 +1,369 @@ +/* + pybind11/stl.h: Transparent conversion for STL data types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +#ifdef __has_include +// std::optional (but including it in c++14 mode isn't allowed) +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_OPTIONAL 1 +# endif +// std::experimental::optional (but not allowed in c++11 mode) +# if defined(PYBIND11_CPP14) && __has_include() +# include +# define PYBIND11_HAS_EXP_OPTIONAL 1 +# endif +// std::variant +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_VARIANT 1 +# endif +#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) +# include +# include +# define PYBIND11_HAS_OPTIONAL 1 +# define PYBIND11_HAS_VARIANT 1 +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for +/// forwarding a container element). Typically used indirect via forwarded_type(), below. +template +using forwarded_type = conditional_t< + std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template +forwarded_type forward_like(U &&u) { + return std::forward>(std::forward(u)); +} + +template struct set_caster { + using type = Type; + using key_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + for (auto entry : s) { + key_conv conv; + if (!conv.load(entry, convert)) + return false; + value.insert(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + pybind11::set s; + for (auto &&value : src) { + auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); + if (!value_ || !s.add(value_)) + return handle(); + } + return s.release(); + } + + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]")); +}; + +template struct map_caster { + using key_conv = make_caster; + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto d = reinterpret_borrow(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) + return false; + value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + dict d; + for (auto &&kv : src) { + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy, parent)); + if (!key || !value) + return handle(); + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]")); +}; + +template struct list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + reserve_maybe(s, &value); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.push_back(cast_op(std::move(conv))); + } + return true; + } + +private: + template ().reserve(0)), void>::value, int> = 0> + void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } + void reserve_maybe(sequence, void *) { } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]")); +}; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct array_caster { + using value_conv = make_caster; + +private: + template + bool require_size(enable_if_t size) { + if (value.size() != size) + value.resize(size); + return true; + } + template + bool require_size(enable_if_t size) { + return size == Size; + } + +public: + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto l = reinterpret_borrow(src); + if (!require_size(l.size())) + return false; + size_t ctr = 0; + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value[ctr++] = cast_op(std::move(conv)); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _(_(""), _("[") + _() + _("]")) + _("]")); +}; + +template struct type_caster> + : array_caster, Type, false, Size> { }; + +template struct type_caster> + : array_caster, Type, true> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +// This type caster is intended to be used for std::optional and std::experimental::optional +template struct optional_caster { + using value_conv = make_caster; + + template + static handle cast(T_ &&src, return_value_policy policy, handle parent) { + if (!src) + return none().inc_ref(); + return value_conv::cast(*std::forward(src), policy, parent); + } + + bool load(handle src, bool convert) { + if (!src) { + return false; + } else if (src.is_none()) { + return true; // default-constructed value is already empty + } + value_conv inner_caster; + if (!inner_caster.load(src, convert)) + return false; + + value.emplace(cast_op(std::move(inner_caster))); + return true; + } + + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]")); +}; + +#if PYBIND11_HAS_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +#if PYBIND11_HAS_EXP_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +/// Visit a variant and cast any found type to Python +struct variant_caster_visitor { + return_value_policy policy; + handle parent; + + using result_type = handle; // required by boost::variant in C++11 + + template + result_type operator()(T &&src) const { + return make_caster::cast(std::forward(src), policy, parent); + } +}; + +/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar +/// `namespace::variant` types which provide a `namespace::visit()` function are handled here +/// automatically using argument-dependent lookup. Users can provide specializations for other +/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. +template class Variant> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { + return visit(std::forward(args)...); + } +}; + +/// Generic variant caster +template struct variant_caster; + +template class V, typename... Ts> +struct variant_caster> { + static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); + + template + bool load_alternative(handle src, bool convert, type_list) { + auto caster = make_caster(); + if (caster.load(src, convert)) { + value = cast_op(caster); + return true; + } + return load_alternative(src, convert, type_list{}); + } + + bool load_alternative(handle, bool, type_list<>) { return false; } + + bool load(handle src, bool convert) { + // Do a first pass without conversions to improve constructor resolution. + // E.g. `py::int_(1).cast>()` needs to fill the `int` + // slot of the variant. Without two-pass loading `double` would be filled + // because it appears first and a conversion is possible. + if (convert && load_alternative(src, false, type_list{})) + return true; + return load_alternative(src, convert, type_list{}); + } + + template + static handle cast(Variant &&src, return_value_policy policy, handle parent) { + return visit_helper::call(variant_caster_visitor{policy, parent}, + std::forward(src)); + } + + using Type = V; + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name()...) + _("]")); +}; + +#if PYBIND11_HAS_VARIANT +template +struct type_caster> : variant_caster> { }; +#endif +NAMESPACE_END(detail) + +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) str(obj); + return os; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/external/pybind11/include/pybind11/stl_bind.h b/external/pybind11/include/pybind11/stl_bind.h new file mode 100644 index 0000000..7ef6878 --- /dev/null +++ b/external/pybind11/include/pybind11/stl_bind.h @@ -0,0 +1,599 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "operators.h" + +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, enable_if_t::is_element && + container_traits::is_comparable>> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args &...) { } +template void vector_if_equal_operator(const Args &...) { } +template void vector_if_insertion_operator(const Args &...) { } +template void vector_modifiers(const Args &...) { } + +template +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { + cl.def(init(), "Copy constructor"); +} + +template +void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + cl.def("append", + [](Vector &v, const T &value) { v.push_back(value); }, + arg("x"), + "Add an item to the end of the list"); + + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len(it)); + for (handle h : it) + v->push_back(h.cast()); + return v.release(); + })); + + cl.def("extend", + [](Vector &v, const Vector &src) { + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, SizeType i, const T &x) { + if (i > v.size()) + throw index_error(); + v.insert(v.begin() + (DiffType) i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + T t = v[i]; + v.erase(v.begin() + (DiffType) i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [](Vector &v, SizeType i, const T &t) { + if (i >= v.size()) + throw index_error(); + v[i] = t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i= v.size()) + throw index_error(); + v.erase(v.begin() + DiffType(i)); + }, + "Delete the list elements at index ``i``" + ); + + cl.def("__delitem__", + [](Vector &v, slice slice) { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (step == 1 && false) { + v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); + } else { + for (size_t i = 0; i < slicelength; ++i) { + v.erase(v.begin() + DiffType(start)); + start += step - 1; + } + } + }, + "Delete list elements using a slice object" + ); + +} + +// If the type has an operator[] that doesn't return a reference (most notably std::vector), +// we have to access by copying; otherwise we return by reference. +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; + +// The usual case: access and iterate by reference +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + + cl.def("__getitem__", + [](Vector &v, SizeType i) -> T & { + if (i >= v.size()) + throw index_error(); + return v[i]; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::reference_internal, ItType, ItType, T&>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +// The case for special objects, like std::vector, that have to be returned-by-copy: +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using ItType = typename Vector::iterator; + cl.def("__getitem__", + [](const Vector &v, SizeType i) -> T { + if (i >= v.size()) + throw index_error(); + return v[i]; + } + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::copy, ItType, ItType, T>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +// Provide the buffer interface for vectors if we have data() and we have a format for it +// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer +template +struct vector_has_data_and_format : std::false_type {}; +template +struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; + +// Add the buffer interface to a vector +template +enable_if_t...>::value> +vector_buffer(Class_& cl) { + using T = typename Vector::value_type; + + static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); + + // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here + format_descriptor::format(); + + cl.def_buffer([](Vector& v) -> buffer_info { + return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); + }); + + cl.def(init([](buffer buf) { + auto info = buf.request(); + if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) + throw type_error("Only valid 1D buffers can be copied to a vector"); + if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) + throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); + + auto vec = std::unique_ptr(new Vector()); + vec->reserve((size_t) info.shape[0]); + T *p = static_cast(info.ptr); + ssize_t step = info.strides[0] / static_cast(sizeof(T)); + T *end = p + info.shape[0] * step; + for (; p != end; p += step) + vec->push_back(*p); + return vec.release(); + })); + + return; +} + +template +enable_if_t...>::value> vector_buffer(Class_&) {} + +NAMESPACE_END(detail) + +// +// std::vector +// +template , typename... Args> +class_ bind_vector(handle scope, std::string const &name, Args&&... args) { + using Class_ = class_; + + // If the value_type is unregistered (e.g. a converting type) or is itself registered + // module-local then make the vector binding module-local as well: + using vtype = typename Vector::value_type; + auto vtype_info = detail::get_type_info(typeid(vtype)); + bool local = !vtype_info || vtype_info->module_local; + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + + + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + cl.def("empty", &Vector::empty, "checks whether the container is empty"); + cl.def("size", &Vector::size, "returns the number of elements"); + cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); + cl.def("pop_back", &Vector::pop_back, "removes the last element"); + + cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); + cl.def("reserve", &Vector::reserve, "reserves storage"); + cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); + cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + if (v.size()) return v.front(); + else throw index_error(); + }, "access the first element"); + + cl.def("back", [](Vector &v) { + if (v.size()) return v.back(); + else throw index_error(); + }, "access the last element "); + +#endif + + return cl; +} + + + +// +// std::map, std::unordered_map +// + +NAMESPACE_BEGIN(detail) + +/* Fallback functions */ +template void map_if_insertion_operator(const Args &...) { } +template void map_assignment(const Args &...) { } + +// Map assignment when copy-assignable: just copy the value +template +void map_assignment(enable_if_t::value, Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + auto it = m.find(k); + if (it != m.end()) it->second = v; + else m.emplace(k, v); + } + ); +} + +// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting +template +void map_assignment(enable_if_t< + !std::is_copy_assignable::value && + is_copy_constructible::value, + Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + // We can't use m[k] = v; because value type might not be default constructable + auto r = m.emplace(k, v); + if (!r.second) { + // value type is not copy assignable so the only way to insert it is to erase it first... + m.erase(r.first); + m.emplace(k, v); + } + } + ); +} + + +template auto map_if_insertion_operator(Class_ &cl, std::string const &name) +-> decltype(std::declval() << std::declval() << std::declval(), void()) { + + cl.def("__repr__", + [name](Map &m) { + std::ostringstream s; + s << name << '{'; + bool f = false; + for (auto const &kv : m) { + if (f) + s << ", "; + s << kv.first << ": " << kv.second; + f = true; + } + s << '}'; + return s.str(); + }, + "Return the canonical string representation of this map." + ); +} + + +NAMESPACE_END(detail) + +template , typename... Args> +class_ bind_map(handle scope, const std::string &name, Args&&... args) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + using Class_ = class_; + + // If either type is a non-module-local bound type then make the map binding non-local as well; + // otherwise (e.g. both types are either module-local or converting) the map will be + // module-local. + auto tinfo = detail::get_type_info(typeid(MappedType)); + bool local = !tinfo || tinfo->module_local; + if (local) { + tinfo = detail::get_type_info(typeid(KeyType)); + local = !tinfo || tinfo->module_local; + } + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + cl.def(init<>()); + + // Register stream insertion operator (if possible) + detail::map_if_insertion_operator(cl, name); + + cl.def("__bool__", + [](const Map &m) -> bool { return !m.empty(); }, + "Check whether the map is nonempty" + ); + + cl.def("__iter__", + [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("items", + [](Map &m) { return make_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("__getitem__", + [](Map &m, const KeyType &k) -> MappedType & { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return it->second; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + // Assignment provided only if the type is copyable + detail::map_assignment(cl); + + cl.def("__delitem__", + [](Map &m, const KeyType &k) { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return m.erase(it); + } + ); + + cl.def("__len__", &Map::size); + + return cl; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/external/pybind11/pybind11/__init__.py b/external/pybind11/pybind11/__init__.py new file mode 100644 index 0000000..a765692 --- /dev/null +++ b/external/pybind11/pybind11/__init__.py @@ -0,0 +1,11 @@ +from ._version import version_info, __version__ # noqa: F401 imported but unused + + +def get_include(*args, **kwargs): + import os + try: + from pip import locations + return os.path.dirname( + locations.distutils_scheme('pybind11', *args, **kwargs)['headers']) + except ImportError: + return 'include' diff --git a/external/pybind11/pybind11/__main__.py b/external/pybind11/pybind11/__main__.py new file mode 100644 index 0000000..9ef8378 --- /dev/null +++ b/external/pybind11/pybind11/__main__.py @@ -0,0 +1,37 @@ +from __future__ import print_function + +import argparse +import sys +import sysconfig + +from . import get_include + + +def print_includes(): + dirs = [sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + get_include(), + get_include(True)] + + # Make unique but preserve order + unique_dirs = [] + for d in dirs: + if d not in unique_dirs: + unique_dirs.append(d) + + print(' '.join('-I' + d for d in unique_dirs)) + + +def main(): + parser = argparse.ArgumentParser(prog='python -m pybind11') + parser.add_argument('--includes', action='store_true', + help='Include flags for both pybind11 and Python headers.') + args = parser.parse_args() + if not sys.argv[1:]: + parser.print_help() + if args.includes: + print_includes() + + +if __name__ == '__main__': + main() diff --git a/external/pybind11/pybind11/_version.py b/external/pybind11/pybind11/_version.py new file mode 100644 index 0000000..9241150 --- /dev/null +++ b/external/pybind11/pybind11/_version.py @@ -0,0 +1,2 @@ +version_info = (2, 2, 1) +__version__ = '.'.join(map(str, version_info)) diff --git a/external/pybind11/setup.cfg b/external/pybind11/setup.cfg new file mode 100644 index 0000000..9e5e88d --- /dev/null +++ b/external/pybind11/setup.cfg @@ -0,0 +1,10 @@ +[bdist_wheel] +universal=1 + +[flake8] +max-line-length = 99 +show_source = True +exclude = .git, __pycache__, build, dist, docs, tools, venv +ignore = + # required for pretty matrix formating: multiple spaces after `,` and `[` + E201, E241 diff --git a/external/pybind11/setup.py b/external/pybind11/setup.py new file mode 100644 index 0000000..b761205 --- /dev/null +++ b/external/pybind11/setup.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python + +# Setup script for PyPI; use CMakeFile.txt to build extension modules + +from setuptools import setup +from distutils.command.install_headers import install_headers +from pybind11 import __version__ +import os + +# Prevent installation of pybind11 headers by setting +# PYBIND11_USE_CMAKE. +if os.environ.get('PYBIND11_USE_CMAKE'): + headers = [] +else: + headers = [ + 'include/pybind11/detail/class.h', + 'include/pybind11/detail/common.h', + 'include/pybind11/detail/descr.h', + 'include/pybind11/detail/init.h', + 'include/pybind11/detail/internals.h', + 'include/pybind11/detail/typeid.h', + 'include/pybind11/attr.h', + 'include/pybind11/buffer_info.h', + 'include/pybind11/cast.h', + 'include/pybind11/chrono.h', + 'include/pybind11/common.h', + 'include/pybind11/complex.h', + 'include/pybind11/eigen.h', + 'include/pybind11/embed.h', + 'include/pybind11/eval.h', + 'include/pybind11/functional.h', + 'include/pybind11/iostream.h', + 'include/pybind11/numpy.h', + 'include/pybind11/operators.h', + 'include/pybind11/options.h', + 'include/pybind11/pybind11.h', + 'include/pybind11/pytypes.h', + 'include/pybind11/stl.h', + 'include/pybind11/stl_bind.h', + ] + + +class InstallHeaders(install_headers): + """Use custom header installer because the default one flattens subdirectories""" + def run(self): + if not self.distribution.headers: + return + + for header in self.distribution.headers: + subdir = os.path.dirname(os.path.relpath(header, 'include/pybind11')) + install_dir = os.path.join(self.install_dir, subdir) + self.mkpath(install_dir) + + (out, _) = self.copy_file(header, install_dir) + self.outfiles.append(out) + + +setup( + name='pybind11', + version=__version__, + description='Seamless operability between C++11 and Python', + author='Wenzel Jakob', + author_email='wenzel.jakob@epfl.ch', + url='https://github.com/wjakob/pybind11', + download_url='https://github.com/wjakob/pybind11/tarball/v' + __version__, + packages=['pybind11'], + license='BSD', + headers=headers, + cmdclass=dict(install_headers=InstallHeaders), + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Utilities', + 'Programming Language :: C++', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'License :: OSI Approved :: BSD License' + ], + keywords='C++11, Python bindings', + long_description="""pybind11 is a lightweight header-only library that +exposes C++ types in Python and vice versa, mainly to create Python bindings of +existing C++ code. Its goals and syntax are similar to the excellent +Boost.Python by David Abrahams: to minimize boilerplate code in traditional +extension modules by inferring type information using compile-time +introspection. + +The main issue with Boost.Python-and the reason for creating such a similar +project-is Boost. Boost is an enormously large and complex suite of utility +libraries that works with almost every C++ compiler in existence. This +compatibility has its cost: arcane template tricks and workarounds are +necessary to support the oldest and buggiest of compiler specimens. Now that +C++11-compatible compilers are widely available, this heavy machinery has +become an excessively large and unnecessary dependency. + +Think of this library as a tiny self-contained version of Boost.Python with +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~4K lines of code and depend on +Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This +compact implementation was possible thanks to some of the new C++11 language +features (specifically: tuples, lambda functions and variadic templates). Since +its creation, this library has grown beyond Boost.Python in many ways, leading +to dramatically simpler binding code in many common situations.""") diff --git a/external/pybind11/tests/CMakeLists.txt b/external/pybind11/tests/CMakeLists.txt new file mode 100644 index 0000000..25e0666 --- /dev/null +++ b/external/pybind11/tests/CMakeLists.txt @@ -0,0 +1,236 @@ +# CMakeLists.txt -- Build system for the pybind11 test suite +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +option(PYBIND11_WERROR "Report all warnings as errors" OFF) + +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + # We're being loaded directly, i.e. not via add_subdirectory, so make this + # work as its own project and load the pybind11Config to get the tools we need + project(pybind11_tests CXX) + + find_package(pybind11 REQUIRED CONFIG) +endif() + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting tests build type to MinSizeRel as none was specified") + set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +# Full set of test files (you can override these; see below) +set(PYBIND11_TEST_FILES + test_buffers.cpp + test_builtin_casters.cpp + test_call_policies.cpp + test_callbacks.cpp + test_chrono.cpp + test_class.cpp + test_constants_and_functions.cpp + test_copy_move.cpp + test_docstring_options.cpp + test_eigen.cpp + test_enum.cpp + test_eval.cpp + test_exceptions.cpp + test_factory_constructors.cpp + test_iostream.cpp + test_kwargs_and_defaults.cpp + test_local_bindings.cpp + test_methods_and_attributes.cpp + test_modules.cpp + test_multiple_inheritance.cpp + test_numpy_array.cpp + test_numpy_dtypes.cpp + test_numpy_vectorize.cpp + test_opaque_types.cpp + test_operator_overloading.cpp + test_pickling.cpp + test_pytypes.cpp + test_sequences_and_iterators.cpp + test_smart_ptr.cpp + test_stl.cpp + test_stl_binders.cpp + test_virtual_functions.cpp +) + +# Invoking cmake with something like: +# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_picking.cpp" .. +# lets you override the tests that get compiled and run. You can restore to all tests with: +# cmake -DPYBIND11_TEST_OVERRIDE= .. +if (PYBIND11_TEST_OVERRIDE) + set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE}) +endif() + +string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") + +# Contains the set of test files that require pybind11_cross_module_tests to be +# built; if none of these are built (i.e. because TEST_OVERRIDE is used and +# doesn't include them) the second module doesn't get built. +set(PYBIND11_CROSS_MODULE_TESTS + test_exceptions.py + test_local_bindings.py + test_stl.py + test_stl_binders.py +) + +# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but +# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" +# skip message). +list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I) +if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) + # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). + # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also + # produces a fatal error if loaded from a pre-3.0 cmake. + if (NOT CMAKE_VERSION VERSION_LESS 3.0) + find_package(Eigen3 QUIET CONFIG) + if (EIGEN3_FOUND) + if (EIGEN3_VERSION_STRING AND NOT EIGEN3_VERSION_STRING VERSION_LESS 3.3.1) + set(PYBIND11_EIGEN_VIA_TARGET 1) + endif() + endif() + endif() + if (NOT EIGEN3_FOUND) + # Couldn't load via target, so fall back to allowing module mode finding, which will pick up + # tools/FindEigen3.cmake + find_package(Eigen3 QUIET) + endif() + + if(EIGEN3_FOUND) + # Eigen 3.3.1+ cmake sets EIGEN3_VERSION_STRING (and hard codes the version when installed + # rather than looking it up in the cmake script); older versions, and the + # tools/FindEigen3.cmake, set EIGEN3_VERSION instead. + if(NOT EIGEN3_VERSION AND EIGEN3_VERSION_STRING) + set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING}) + endif() + message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") + else() + list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) + message(STATUS "Building tests WITHOUT Eigen") + endif() +endif() + +# Optional dependency for some tests (boost::variant is only supported with version >= 1.56) +find_package(Boost 1.56) + +# Compile with compiler warnings turned on +function(pybind11_enable_warnings target_name) + if(MSVC) + target_compile_options(${target_name} PRIVATE /W4) + else() + target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual) + endif() + + if(PYBIND11_WERROR) + if(MSVC) + target_compile_options(${target_name} PRIVATE /WX) + else() + target_compile_options(${target_name} PRIVATE -Werror) + endif() + endif() +endfunction() + +set(test_targets pybind11_tests) + +# Build pybind11_cross_module_tests if any test_whatever.py are being built that require it +foreach(t ${PYBIND11_CROSS_MODULE_TESTS}) + list(FIND PYBIND11_PYTEST_FILES ${t} i) + if (i GREATER -1) + list(APPEND test_targets pybind11_cross_module_tests) + break() + endif() +endforeach() + +set(testdir ${CMAKE_CURRENT_SOURCE_DIR}) +foreach(target ${test_targets}) + set(test_files ${PYBIND11_TEST_FILES}) + if(NOT target STREQUAL "pybind11_tests") + set(test_files "") + endif() + + # Create the binding library + pybind11_add_module(${target} THIN_LTO ${target}.cpp ${test_files} ${PYBIND11_HEADERS}) + pybind11_enable_warnings(${target}) + + if(MSVC) + target_compile_options(${target} PRIVATE /utf-8) + endif() + + if(EIGEN3_FOUND) + if (PYBIND11_EIGEN_VIA_TARGET) + target_link_libraries(${target} PRIVATE Eigen3::Eigen) + else() + target_include_directories(${target} PRIVATE ${EIGEN3_INCLUDE_DIR}) + endif() + target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_EIGEN) + endif() + + if(Boost_FOUND) + target_include_directories(${target} PRIVATE ${Boost_INCLUDE_DIRS}) + target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST) + endif() + + # Always write the output file directly into the 'tests' directory (even on MSVC) + if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir}) + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir}) + endforeach() + endif() +endforeach() + +# Make sure pytest is found or produce a fatal error +if(NOT PYBIND11_PYTEST_FOUND) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pytest; print(pytest.__version__)" + RESULT_VARIABLE pytest_not_found OUTPUT_VARIABLE pytest_version ERROR_QUIET) + if(pytest_not_found) + message(FATAL_ERROR "Running the tests requires pytest. Please install it manually" + " (try: ${PYTHON_EXECUTABLE} -m pip install pytest)") + elseif(pytest_version VERSION_LESS 3.0) + message(FATAL_ERROR "Running the tests requires pytest >= 3.0. Found: ${pytest_version}" + "Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)") + endif() + set(PYBIND11_PYTEST_FOUND TRUE CACHE INTERNAL "") +endif() + +if(CMAKE_VERSION VERSION_LESS 3.2) + set(PYBIND11_USES_TERMINAL "") +else() + set(PYBIND11_USES_TERMINAL "USES_TERMINAL") +endif() + +# A single command to compile and run the tests +add_custom_target(pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_PYTEST_FILES} + DEPENDS ${test_targets} WORKING_DIRECTORY ${testdir} ${PYBIND11_USES_TERMINAL}) + +if(PYBIND11_TEST_OVERRIDE) + add_custom_command(TARGET pytest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") +endif() + +# Add a check target to run all the tests, starting with pytest (we add dependencies to this below) +add_custom_target(check DEPENDS pytest) + +# The remaining tests only apply when being built as part of the pybind11 project, but not if the +# tests are being built independently. +if (NOT PROJECT_NAME STREQUAL "pybind11") + return() +endif() + +# Add a post-build comment to show the primary test suite .so size and, if a previous size, compare it: +add_custom_command(TARGET pybind11_tests POST_BUILD + COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/libsize.py + $ ${CMAKE_CURRENT_BINARY_DIR}/sosize-$.txt) + +# Test embedding the interpreter. Provides the `cpptest` target. +add_subdirectory(test_embed) + +# Test CMake build using functions and targets from subdirectory or installed location +add_subdirectory(test_cmake_build) diff --git a/external/pybind11/tests/conftest.py b/external/pybind11/tests/conftest.py new file mode 100644 index 0000000..f4c2282 --- /dev/null +++ b/external/pybind11/tests/conftest.py @@ -0,0 +1,241 @@ +"""pytest configuration + +Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. +Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences. +""" + +import pytest +import textwrap +import difflib +import re +import sys +import contextlib +import platform +import gc + +_unicode_marker = re.compile(r'u(\'[^\']*\')') +_long_marker = re.compile(r'([0-9])L') +_hexadecimal = re.compile(r'0x[0-9a-fA-F]+') + + +def _strip_and_dedent(s): + """For triple-quote strings""" + return textwrap.dedent(s.lstrip('\n').rstrip()) + + +def _split_and_sort(s): + """For output which does not require specific line order""" + return sorted(_strip_and_dedent(s).splitlines()) + + +def _make_explanation(a, b): + """Explanation for a failed assert -- the a and b arguments are List[str]""" + return ["--- actual / +++ expected"] + [line.strip('\n') for line in difflib.ndiff(a, b)] + + +class Output(object): + """Basic output post-processing and comparison""" + def __init__(self, string): + self.string = string + self.explanation = [] + + def __str__(self): + return self.string + + def __eq__(self, other): + # Ignore constructor/destructor output which is prefixed with "###" + a = [line for line in self.string.strip().splitlines() if not line.startswith("###")] + b = _strip_and_dedent(other).splitlines() + if a == b: + return True + else: + self.explanation = _make_explanation(a, b) + return False + + +class Unordered(Output): + """Custom comparison for output without strict line ordering""" + def __eq__(self, other): + a = _split_and_sort(self.string) + b = _split_and_sort(other) + if a == b: + return True + else: + self.explanation = _make_explanation(a, b) + return False + + +class Capture(object): + def __init__(self, capfd): + self.capfd = capfd + self.out = "" + self.err = "" + + def __enter__(self): + self.capfd.readouterr() + return self + + def __exit__(self, *_): + self.out, self.err = self.capfd.readouterr() + + def __eq__(self, other): + a = Output(self.out) + b = other + if a == b: + return True + else: + self.explanation = a.explanation + return False + + def __str__(self): + return self.out + + def __contains__(self, item): + return item in self.out + + @property + def unordered(self): + return Unordered(self.out) + + @property + def stderr(self): + return Output(self.err) + + +@pytest.fixture +def capture(capsys): + """Extended `capsys` with context manager and custom equality operators""" + return Capture(capsys) + + +class SanitizedString(object): + def __init__(self, sanitizer): + self.sanitizer = sanitizer + self.string = "" + self.explanation = [] + + def __call__(self, thing): + self.string = self.sanitizer(thing) + return self + + def __eq__(self, other): + a = self.string + b = _strip_and_dedent(other) + if a == b: + return True + else: + self.explanation = _make_explanation(a.splitlines(), b.splitlines()) + return False + + +def _sanitize_general(s): + s = s.strip() + s = s.replace("pybind11_tests.", "m.") + s = s.replace("unicode", "str") + s = _long_marker.sub(r"\1", s) + s = _unicode_marker.sub(r"\1", s) + return s + + +def _sanitize_docstring(thing): + s = thing.__doc__ + s = _sanitize_general(s) + return s + + +@pytest.fixture +def doc(): + """Sanitize docstrings and add custom failure explanation""" + return SanitizedString(_sanitize_docstring) + + +def _sanitize_message(thing): + s = str(thing) + s = _sanitize_general(s) + s = _hexadecimal.sub("0", s) + return s + + +@pytest.fixture +def msg(): + """Sanitize messages and add custom failure explanation""" + return SanitizedString(_sanitize_message) + + +# noinspection PyUnusedLocal +def pytest_assertrepr_compare(op, left, right): + """Hook to insert custom failure explanation""" + if hasattr(left, 'explanation'): + return left.explanation + + +@contextlib.contextmanager +def suppress(exception): + """Suppress the desired exception""" + try: + yield + except exception: + pass + + +def gc_collect(): + ''' Run the garbage collector twice (needed when running + reference counting tests with PyPy) ''' + gc.collect() + gc.collect() + + +def pytest_namespace(): + """Add import suppression and test requirements to `pytest` namespace""" + try: + import numpy as np + except ImportError: + np = None + try: + import scipy + except ImportError: + scipy = None + try: + from pybind11_tests.eigen import have_eigen + except ImportError: + have_eigen = False + pypy = platform.python_implementation() == "PyPy" + + skipif = pytest.mark.skipif + return { + 'suppress': suppress, + 'requires_numpy': skipif(not np, reason="numpy is not installed"), + 'requires_scipy': skipif(not np, reason="scipy is not installed"), + 'requires_eigen_and_numpy': skipif(not have_eigen or not np, + reason="eigen and/or numpy are not installed"), + 'requires_eigen_and_scipy': skipif(not have_eigen or not scipy, + reason="eigen and/or scipy are not installed"), + 'unsupported_on_pypy': skipif(pypy, reason="unsupported on PyPy"), + 'unsupported_on_py2': skipif(sys.version_info.major < 3, + reason="unsupported on Python 2.x"), + 'gc_collect': gc_collect + } + + +def _test_import_pybind11(): + """Early diagnostic for test module initialization errors + + When there is an error during initialization, the first import will report the + real error while all subsequent imports will report nonsense. This import test + is done early (in the pytest configuration file, before any tests) in order to + avoid the noise of having all tests fail with identical error messages. + + Any possible exception is caught here and reported manually *without* the stack + trace. This further reduces noise since the trace would only show pytest internals + which are not useful for debugging pybind11 module issues. + """ + # noinspection PyBroadException + try: + import pybind11_tests # noqa: F401 imported but unused + except Exception as e: + print("Failed to import pybind11_tests from pytest:") + print(" {}: {}".format(type(e).__name__, e)) + sys.exit(1) + + +_test_import_pybind11() diff --git a/external/pybind11/tests/constructor_stats.h b/external/pybind11/tests/constructor_stats.h new file mode 100644 index 0000000..babded0 --- /dev/null +++ b/external/pybind11/tests/constructor_stats.h @@ -0,0 +1,276 @@ +#pragma once +/* + tests/constructor_stats.h -- framework for printing and tracking object + instance lifetimes in example/test code. + + Copyright (c) 2016 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. + +This header provides a few useful tools for writing examples or tests that want to check and/or +display object instance lifetimes. It requires that you include this header and add the following +function calls to constructors: + + class MyClass { + MyClass() { ...; print_default_created(this); } + ~MyClass() { ...; print_destroyed(this); } + MyClass(const MyClass &c) { ...; print_copy_created(this); } + MyClass(MyClass &&c) { ...; print_move_created(this); } + MyClass(int a, int b) { ...; print_created(this, a, b); } + MyClass &operator=(const MyClass &c) { ...; print_copy_assigned(this); } + MyClass &operator=(MyClass &&c) { ...; print_move_assigned(this); } + + ... + } + +You can find various examples of these in several of the existing testing .cpp files. (Of course +you don't need to add any of the above constructors/operators that you don't actually have, except +for the destructor). + +Each of these will print an appropriate message such as: + + ### MyClass @ 0x2801910 created via default constructor + ### MyClass @ 0x27fa780 created 100 200 + ### MyClass @ 0x2801910 destroyed + ### MyClass @ 0x27fa780 destroyed + +You can also include extra arguments (such as the 100, 200 in the output above, coming from the +value constructor) for all of the above methods which will be included in the output. + +For testing, each of these also keeps track the created instances and allows you to check how many +of the various constructors have been invoked from the Python side via code such as: + + from pybind11_tests import ConstructorStats + cstats = ConstructorStats.get(MyClass) + print(cstats.alive()) + print(cstats.default_constructions) + +Note that `.alive()` should usually be the first thing you call as it invokes Python's garbage +collector to actually destroy objects that aren't yet referenced. + +For everything except copy and move constructors and destructors, any extra values given to the +print_...() function is stored in a class-specific values list which you can retrieve and inspect +from the ConstructorStats instance `.values()` method. + +In some cases, when you need to track instances of a C++ class not registered with pybind11, you +need to add a function returning the ConstructorStats for the C++ class; this can be done with: + + m.def("get_special_cstats", &ConstructorStats::get, py::return_value_policy::reference) + +Finally, you can suppress the output messages, but keep the constructor tracking (for +inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g. +`track_copy_created(this)`). + +*/ + +#include "pybind11_tests.h" +#include +#include +#include +#include + +class ConstructorStats { +protected: + std::unordered_map _instances; // Need a map rather than set because members can shared address with parents + std::list _values; // Used to track values (e.g. of value constructors) +public: + int default_constructions = 0; + int copy_constructions = 0; + int move_constructions = 0; + int copy_assignments = 0; + int move_assignments = 0; + + void copy_created(void *inst) { + created(inst); + copy_constructions++; + } + + void move_created(void *inst) { + created(inst); + move_constructions++; + } + + void default_created(void *inst) { + created(inst); + default_constructions++; + } + + void created(void *inst) { + ++_instances[inst]; + } + + void destroyed(void *inst) { + if (--_instances[inst] < 0) + throw std::runtime_error("cstats.destroyed() called with unknown " + "instance; potential double-destruction " + "or a missing cstats.created()"); + } + + static void gc() { + // Force garbage collection to ensure any pending destructors are invoked: +#if defined(PYPY_VERSION) + PyObject *globals = PyEval_GetGlobals(); + PyObject *result = PyRun_String( + "import gc\n" + "for i in range(2):" + " gc.collect()\n", + Py_file_input, globals, globals); + if (result == nullptr) + throw py::error_already_set(); + Py_DECREF(result); +#else + py::module::import("gc").attr("collect")(); +#endif + } + + int alive() { + gc(); + int total = 0; + for (const auto &p : _instances) + if (p.second > 0) + total += p.second; + return total; + } + + void value() {} // Recursion terminator + // Takes one or more values, converts them to strings, then stores them. + template void value(const T &v, Tmore &&...args) { + std::ostringstream oss; + oss << v; + _values.push_back(oss.str()); + value(std::forward(args)...); + } + + // Move out stored values + py::list values() { + py::list l; + for (const auto &v : _values) l.append(py::cast(v)); + _values.clear(); + return l; + } + + // Gets constructor stats from a C++ type index + static ConstructorStats& get(std::type_index type) { + static std::unordered_map all_cstats; + return all_cstats[type]; + } + + // Gets constructor stats from a C++ type + template static ConstructorStats& get() { +#if defined(PYPY_VERSION) + gc(); +#endif + return get(typeid(T)); + } + + // Gets constructor stats from a Python class + static ConstructorStats& get(py::object class_) { + auto &internals = py::detail::get_internals(); + const std::type_index *t1 = nullptr, *t2 = nullptr; + try { + auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0); + for (auto &p : internals.registered_types_cpp) { + if (p.second == type_info) { + if (t1) { + t2 = &p.first; + break; + } + t1 = &p.first; + } + } + } + catch (std::out_of_range) {} + if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); + auto &cs1 = get(*t1); + // If we have both a t1 and t2 match, one is probably the trampoline class; return whichever + // has more constructions (typically one or the other will be 0) + if (t2) { + auto &cs2 = get(*t2); + int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size(); + int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size(); + if (cs2_total > cs1_total) return cs2; + } + return cs1; + } +}; + +// To track construction/destruction, you need to call these methods from the various +// constructors/operators. The ones that take extra values record the given values in the +// constructor stats values for later inspection. +template void track_copy_created(T *inst) { ConstructorStats::get().copy_created(inst); } +template void track_move_created(T *inst) { ConstructorStats::get().move_created(inst); } +template void track_copy_assigned(T *, Values &&...values) { + auto &cst = ConstructorStats::get(); + cst.copy_assignments++; + cst.value(std::forward(values)...); +} +template void track_move_assigned(T *, Values &&...values) { + auto &cst = ConstructorStats::get(); + cst.move_assignments++; + cst.value(std::forward(values)...); +} +template void track_default_created(T *inst, Values &&...values) { + auto &cst = ConstructorStats::get(); + cst.default_created(inst); + cst.value(std::forward(values)...); +} +template void track_created(T *inst, Values &&...values) { + auto &cst = ConstructorStats::get(); + cst.created(inst); + cst.value(std::forward(values)...); +} +template void track_destroyed(T *inst) { + ConstructorStats::get().destroyed(inst); +} +template void track_values(T *, Values &&...values) { + ConstructorStats::get().value(std::forward(values)...); +} + +/// Don't cast pointers to Python, print them as strings +inline const char *format_ptrs(const char *p) { return p; } +template +py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast(p)); } +template +auto format_ptrs(T &&x) -> decltype(std::forward(x)) { return std::forward(x); } + +template +void print_constr_details(T *inst, const std::string &action, Output &&...output) { + py::print("###", py::type_id(), "@", format_ptrs(inst), action, + format_ptrs(std::forward(output))...); +} + +// Verbose versions of the above: +template void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values + print_constr_details(inst, "created via copy constructor", values...); + track_copy_created(inst); +} +template void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values + print_constr_details(inst, "created via move constructor", values...); + track_move_created(inst); +} +template void print_copy_assigned(T *inst, Values &&...values) { + print_constr_details(inst, "assigned via copy assignment", values...); + track_copy_assigned(inst, values...); +} +template void print_move_assigned(T *inst, Values &&...values) { + print_constr_details(inst, "assigned via move assignment", values...); + track_move_assigned(inst, values...); +} +template void print_default_created(T *inst, Values &&...values) { + print_constr_details(inst, "created via default constructor", values...); + track_default_created(inst, values...); +} +template void print_created(T *inst, Values &&...values) { + print_constr_details(inst, "created", values...); + track_created(inst, values...); +} +template void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values + print_constr_details(inst, "destroyed", values...); + track_destroyed(inst); +} +template void print_values(T *inst, Values &&...values) { + print_constr_details(inst, ":", values...); + track_values(inst, values...); +} + diff --git a/external/pybind11/tests/local_bindings.h b/external/pybind11/tests/local_bindings.h new file mode 100644 index 0000000..b6afb80 --- /dev/null +++ b/external/pybind11/tests/local_bindings.h @@ -0,0 +1,64 @@ +#pragma once +#include "pybind11_tests.h" + +/// Simple class used to test py::local: +template class LocalBase { +public: + LocalBase(int i) : i(i) { } + int i = -1; +}; + +/// Registered with py::module_local in both main and secondary modules: +using LocalType = LocalBase<0>; +/// Registered without py::module_local in both modules: +using NonLocalType = LocalBase<1>; +/// A second non-local type (for stl_bind tests): +using NonLocal2 = LocalBase<2>; +/// Tests within-module, different-compilation-unit local definition conflict: +using LocalExternal = LocalBase<3>; +/// Mixed: registered local first, then global +using MixedLocalGlobal = LocalBase<4>; +/// Mixed: global first, then local +using MixedGlobalLocal = LocalBase<5>; + +/// Registered with py::module_local only in the secondary module: +using ExternalType1 = LocalBase<6>; +using ExternalType2 = LocalBase<7>; + +using LocalVec = std::vector; +using LocalVec2 = std::vector; +using LocalMap = std::unordered_map; +using NonLocalVec = std::vector; +using NonLocalVec2 = std::vector; +using NonLocalMap = std::unordered_map; +using NonLocalMap2 = std::unordered_map; + +PYBIND11_MAKE_OPAQUE(LocalVec); +PYBIND11_MAKE_OPAQUE(LocalVec2); +PYBIND11_MAKE_OPAQUE(LocalMap); +PYBIND11_MAKE_OPAQUE(NonLocalVec); +//PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 +PYBIND11_MAKE_OPAQUE(NonLocalMap); +PYBIND11_MAKE_OPAQUE(NonLocalMap2); + + +// Simple bindings (used with the above): +template +py::class_ bind_local(Args && ...args) { + return py::class_(std::forward(args)...) + .def(py::init()) + .def("get", [](T &i) { return i.i + Adjust; }); +}; + +// Simulate a foreign library base class (to match the example in the docs): +namespace pets { +class Pet { +public: + Pet(std::string name) : name_(name) {} + std::string name_; + const std::string &name() { return name_; } +}; +} + +struct MixGL { int i; MixGL(int i) : i{i} {} }; +struct MixGL2 { int i; MixGL2(int i) : i{i} {} }; diff --git a/external/pybind11/tests/object.h b/external/pybind11/tests/object.h new file mode 100644 index 0000000..9235f19 --- /dev/null +++ b/external/pybind11/tests/object.h @@ -0,0 +1,175 @@ +#if !defined(__OBJECT_H) +#define __OBJECT_H + +#include +#include "constructor_stats.h" + +/// Reference counted object base class +class Object { +public: + /// Default constructor + Object() { print_default_created(this); } + + /// Copy constructor + Object(const Object &) : m_refCount(0) { print_copy_created(this); } + + /// Return the current reference count + int getRefCount() const { return m_refCount; }; + + /// Increase the object's reference count by one + void incRef() const { ++m_refCount; } + + /** \brief Decrease the reference count of + * the object and possibly deallocate it. + * + * The object will automatically be deallocated once + * the reference count reaches zero. + */ + void decRef(bool dealloc = true) const { + --m_refCount; + if (m_refCount == 0 && dealloc) + delete this; + else if (m_refCount < 0) + throw std::runtime_error("Internal error: reference count < 0!"); + } + + virtual std::string toString() const = 0; +protected: + /** \brief Virtual protected deconstructor. + * (Will only be called by \ref ref) + */ + virtual ~Object() { print_destroyed(this); } +private: + mutable std::atomic m_refCount { 0 }; +}; + +// Tag class used to track constructions of ref objects. When we track constructors, below, we +// track and print out the actual class (e.g. ref), and *also* add a fake tracker for +// ref_tag. This lets us check that the total number of ref constructors/destructors is +// correct without having to check each individual ref type individually. +class ref_tag {}; + +/** + * \brief Reference counting helper + * + * The \a ref refeference template is a simple wrapper to store a + * pointer to an object. It takes care of increasing and decreasing + * the reference count of the object. When the last reference goes + * out of scope, the associated object will be deallocated. + * + * \ingroup libcore + */ +template class ref { +public: + /// Create a nullptr reference + ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); } + + /// Construct a reference from a pointer + ref(T *ptr) : m_ptr(ptr) { + if (m_ptr) ((Object *) m_ptr)->incRef(); + + print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer"); + + } + + /// Copy constructor + ref(const ref &r) : m_ptr(r.m_ptr) { + if (m_ptr) + ((Object *) m_ptr)->incRef(); + + print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this); + } + + /// Move constructor + ref(ref &&r) : m_ptr(r.m_ptr) { + r.m_ptr = nullptr; + + print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this); + } + + /// Destroy this reference + ~ref() { + if (m_ptr) + ((Object *) m_ptr)->decRef(); + + print_destroyed(this); track_destroyed((ref_tag*) this); + } + + /// Move another reference into the current one + ref& operator=(ref&& r) { + print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this); + + if (*this == r) + return *this; + if (m_ptr) + ((Object *) m_ptr)->decRef(); + m_ptr = r.m_ptr; + r.m_ptr = nullptr; + return *this; + } + + /// Overwrite this reference with another reference + ref& operator=(const ref& r) { + print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this); + + if (m_ptr == r.m_ptr) + return *this; + if (m_ptr) + ((Object *) m_ptr)->decRef(); + m_ptr = r.m_ptr; + if (m_ptr) + ((Object *) m_ptr)->incRef(); + return *this; + } + + /// Overwrite this reference with a pointer to another object + ref& operator=(T *ptr) { + print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer"); + + if (m_ptr == ptr) + return *this; + if (m_ptr) + ((Object *) m_ptr)->decRef(); + m_ptr = ptr; + if (m_ptr) + ((Object *) m_ptr)->incRef(); + return *this; + } + + /// Compare this reference with another reference + bool operator==(const ref &r) const { return m_ptr == r.m_ptr; } + + /// Compare this reference with another reference + bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; } + + /// Compare this reference with a pointer + bool operator==(const T* ptr) const { return m_ptr == ptr; } + + /// Compare this reference with a pointer + bool operator!=(const T* ptr) const { return m_ptr != ptr; } + + /// Access the object referenced by this reference + T* operator->() { return m_ptr; } + + /// Access the object referenced by this reference + const T* operator->() const { return m_ptr; } + + /// Return a C++ reference to the referenced object + T& operator*() { return *m_ptr; } + + /// Return a const C++ reference to the referenced object + const T& operator*() const { return *m_ptr; } + + /// Return a pointer to the referenced object + operator T* () { return m_ptr; } + + /// Return a const pointer to the referenced object + T* get_ptr() { return m_ptr; } + + /// Return a pointer to the referenced object + const T* get_ptr() const { return m_ptr; } +private: + T *m_ptr; +}; + +#endif /* __OBJECT_H */ diff --git a/external/pybind11/tests/pybind11_cross_module_tests.cpp b/external/pybind11/tests/pybind11_cross_module_tests.cpp new file mode 100644 index 0000000..f705e31 --- /dev/null +++ b/external/pybind11/tests/pybind11_cross_module_tests.cpp @@ -0,0 +1,123 @@ +/* + tests/pybind11_cross_module_tests.cpp -- contains tests that require multiple modules + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "local_bindings.h" +#include +#include + +PYBIND11_MODULE(pybind11_cross_module_tests, m) { + m.doc() = "pybind11 cross-module test module"; + + // test_local_bindings.py tests: + // + // Definitions here are tested by importing both this module and the + // relevant pybind11_tests submodule from a test_whatever.py + + // test_load_external + bind_local(m, "ExternalType1", py::module_local()); + bind_local(m, "ExternalType2", py::module_local()); + + // test_exceptions.py + m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); }); + m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); }); + m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); }); + m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); }); + m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); + + // test_local_bindings.py + // Local to both: + bind_local(m, "LocalType", py::module_local()) + .def("get2", [](LocalType &t) { return t.i + 2; }) + ; + + // Can only be called with our python type: + m.def("local_value", [](LocalType &l) { return l.i; }); + + // test_nonlocal_failure + // This registration will fail (global registration when LocalFail is already registered + // globally in the main test module): + m.def("register_nonlocal", [m]() { + bind_local(m, "NonLocalType"); + }); + + // test_stl_bind_local + // stl_bind.h binders defaults to py::module_local if the types are local or converting: + py::bind_vector(m, "LocalVec"); + py::bind_map(m, "LocalMap"); + + // test_stl_bind_global + // and global if the type (or one of the types, for the map) is global (so these will fail, + // assuming pybind11_tests is already loaded): + m.def("register_nonlocal_vec", [m]() { + py::bind_vector(m, "NonLocalVec"); + }); + m.def("register_nonlocal_map", [m]() { + py::bind_map(m, "NonLocalMap"); + }); + // The default can, however, be overridden to global using `py::module_local()` or + // `py::module_local(false)`. + // Explicitly made local: + py::bind_vector(m, "NonLocalVec2", py::module_local()); + // Explicitly made global (and so will fail to bind): + m.def("register_nonlocal_map2", [m]() { + py::bind_map(m, "NonLocalMap2", py::module_local(false)); + }); + + // test_mixed_local_global + // We try this both with the global type registered first and vice versa (the order shouldn't + // matter). + m.def("register_mixed_global_local", [m]() { + bind_local(m, "MixedGlobalLocal", py::module_local()); + }); + m.def("register_mixed_local_global", [m]() { + bind_local(m, "MixedLocalGlobal", py::module_local(false)); + }); + m.def("get_mixed_gl", [](int i) { return MixedGlobalLocal(i); }); + m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); + + // test_internal_locals_differ + m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); }); + + // test_stl_caster_vs_stl_bind + py::bind_vector>(m, "VectorInt"); + + m.def("load_vector_via_binding", [](std::vector &v) { + return std::accumulate(v.begin(), v.end(), 0); + }); + + // test_cross_module_calls + m.def("return_self", [](LocalVec *v) { return v; }); + m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); }); + + class Dog : public pets::Pet { public: Dog(std::string name) : Pet(name) {}; }; + py::class_(m, "Pet", py::module_local()) + .def("name", &pets::Pet::name); + // Binding for local extending class: + py::class_(m, "Dog") + .def(py::init()); + m.def("pet_name", [](pets::Pet &p) { return p.name(); }); + + py::class_(m, "MixGL", py::module_local()).def(py::init()); + m.def("get_gl_value", [](MixGL &o) { return o.i + 100; }); + + py::class_(m, "MixGL2", py::module_local()).def(py::init()); + + // test_vector_bool + // We can't test both stl.h and stl_bind.h conversions of `std::vector` within + // the same module (it would be an ODR violation). Therefore `bind_vector` of `bool` + // is defined here and tested in `test_stl_binders.py`. + py::bind_vector>(m, "VectorBool"); + + // test_missing_header_message + // The main module already includes stl.h, but we need to test the error message + // which appears when this header is missing. + m.def("missing_header_arg", [](std::vector) { }); + m.def("missing_header_return", []() { return std::vector(); }); +} diff --git a/external/pybind11/tests/pybind11_tests.cpp b/external/pybind11/tests/pybind11_tests.cpp new file mode 100644 index 0000000..bc7d2c3 --- /dev/null +++ b/external/pybind11/tests/pybind11_tests.cpp @@ -0,0 +1,93 @@ +/* + tests/pybind11_tests.cpp -- pybind example plugin + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" + +#include +#include + +/* +For testing purposes, we define a static global variable here in a function that each individual +test .cpp calls with its initialization lambda. It's convenient here because we can just not +compile some test files to disable/ignore some of the test code. + +It is NOT recommended as a way to use pybind11 in practice, however: the initialization order will +be essentially random, which is okay for our test scripts (there are no dependencies between the +individual pybind11 test .cpp files), but most likely not what you want when using pybind11 +productively. + +Instead, see the "How can I reduce the build time?" question in the "Frequently asked questions" +section of the documentation for good practice on splitting binding code over multiple files. +*/ +std::list> &initializers() { + static std::list> inits; + return inits; +} + +test_initializer::test_initializer(Initializer init) { + initializers().push_back(init); +} + +test_initializer::test_initializer(const char *submodule_name, Initializer init) { + initializers().push_back([=](py::module &parent) { + auto m = parent.def_submodule(submodule_name); + init(m); + }); +} + +void bind_ConstructorStats(py::module &m) { + py::class_(m, "ConstructorStats") + .def("alive", &ConstructorStats::alive) + .def("values", &ConstructorStats::values) + .def_readwrite("default_constructions", &ConstructorStats::default_constructions) + .def_readwrite("copy_assignments", &ConstructorStats::copy_assignments) + .def_readwrite("move_assignments", &ConstructorStats::move_assignments) + .def_readwrite("copy_constructions", &ConstructorStats::copy_constructions) + .def_readwrite("move_constructions", &ConstructorStats::move_constructions) + .def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal) + + // Not exactly ConstructorStats, but related: expose the internal pybind number of registered instances + // to allow instance cleanup checks (invokes a GC first) + .def_static("detail_reg_inst", []() { + ConstructorStats::gc(); + return py::detail::get_internals().registered_instances.size(); + }) + ; +} + +PYBIND11_MODULE(pybind11_tests, m) { + m.doc() = "pybind11 test module"; + + bind_ConstructorStats(m); + +#if !defined(NDEBUG) + m.attr("debug_enabled") = true; +#else + m.attr("debug_enabled") = false; +#endif + + py::class_(m, "UserType", "A `py::class_` type for testing") + .def(py::init<>()) + .def(py::init()) + .def("get_value", &UserType::value, "Get value using a method") + .def("set_value", &UserType::set, "Set value using a method") + .def_property("value", &UserType::value, &UserType::set, "Get/set value using a property") + .def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); }); + + py::class_(m, "IncType") + .def(py::init<>()) + .def(py::init()) + .def("__repr__", [](const IncType& u) { return "IncType({})"_s.format(u.value()); }); + + for (const auto &initializer : initializers()) + initializer(m); + + if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false; +} diff --git a/external/pybind11/tests/pybind11_tests.h b/external/pybind11/tests/pybind11_tests.h new file mode 100644 index 0000000..90963a5 --- /dev/null +++ b/external/pybind11/tests/pybind11_tests.h @@ -0,0 +1,65 @@ +#pragma once +#include + +#if defined(_MSC_VER) && _MSC_VER < 1910 +// We get some really long type names here which causes MSVC 2015 to emit warnings +# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated +#endif + +namespace py = pybind11; +using namespace pybind11::literals; + +class test_initializer { + using Initializer = void (*)(py::module &); + +public: + test_initializer(Initializer init); + test_initializer(const char *submodule_name, Initializer init); +}; + +#define TEST_SUBMODULE(name, variable) \ + void test_submodule_##name(py::module &); \ + test_initializer name(#name, test_submodule_##name); \ + void test_submodule_##name(py::module &variable) + + +/// Dummy type which is not exported anywhere -- something to trigger a conversion error +struct UnregisteredType { }; + +/// A user-defined type which is exported and can be used by any test +class UserType { +public: + UserType() = default; + UserType(int i) : i(i) { } + + int value() const { return i; } + void set(int set) { i = set; } + +private: + int i = -1; +}; + +/// Like UserType, but increments `value` on copy for quick reference vs. copy tests +class IncType : public UserType { +public: + using UserType::UserType; + IncType() = default; + IncType(const IncType &other) : IncType(other.value() + 1) { } + IncType(IncType &&) = delete; + IncType &operator=(const IncType &) = delete; + IncType &operator=(IncType &&) = delete; +}; + +/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context. +/// Used to test recursive casters (e.g. std::tuple, stl containers). +struct RValueCaster {}; +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) +template<> class type_caster { +public: + PYBIND11_TYPE_CASTER(RValueCaster, _("RValueCaster")); + static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); } + static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); } +}; +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/external/pybind11/tests/pytest.ini b/external/pybind11/tests/pytest.ini new file mode 100644 index 0000000..1e44f0a --- /dev/null +++ b/external/pybind11/tests/pytest.ini @@ -0,0 +1,15 @@ +[pytest] +minversion = 3.0 +norecursedirs = test_cmake_build test_embed +addopts = + # show summary of skipped tests + -rs + # capture only Python print and C++ py::print, but not C output (low-level Python errors) + --capture=sys +filterwarnings = + # make warnings into errors but ignore certain third-party extension issues + error + # importing scipy submodules on some version of Python + ignore::ImportWarning + # bogus numpy ABI warning (see numpy/#432) + ignore:.*numpy.dtype size changed.*:RuntimeWarning diff --git a/external/pybind11/tests/test_buffers.cpp b/external/pybind11/tests/test_buffers.cpp new file mode 100644 index 0000000..5be7177 --- /dev/null +++ b/external/pybind11/tests/test_buffers.cpp @@ -0,0 +1,169 @@ +/* + tests/test_buffers.cpp -- supporting Pythons' buffer protocol + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" + +TEST_SUBMODULE(buffers, m) { + // test_from_python / test_to_python: + class Matrix { + public: + Matrix(ssize_t rows, ssize_t cols) : m_rows(rows), m_cols(cols) { + print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + m_data = new float[(size_t) (rows*cols)]; + memset(m_data, 0, sizeof(float) * (size_t) (rows * cols)); + } + + Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) { + print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + m_data = new float[(size_t) (m_rows * m_cols)]; + memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); + } + + Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) { + print_move_created(this); + s.m_rows = 0; + s.m_cols = 0; + s.m_data = nullptr; + } + + ~Matrix() { + print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + delete[] m_data; + } + + Matrix &operator=(const Matrix &s) { + print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + delete[] m_data; + m_rows = s.m_rows; + m_cols = s.m_cols; + m_data = new float[(size_t) (m_rows * m_cols)]; + memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); + return *this; + } + + Matrix &operator=(Matrix &&s) { + print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); + if (&s != this) { + delete[] m_data; + m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data; + s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr; + } + return *this; + } + + float operator()(ssize_t i, ssize_t j) const { + return m_data[(size_t) (i*m_cols + j)]; + } + + float &operator()(ssize_t i, ssize_t j) { + return m_data[(size_t) (i*m_cols + j)]; + } + + float *data() { return m_data; } + + ssize_t rows() const { return m_rows; } + ssize_t cols() const { return m_cols; } + private: + ssize_t m_rows; + ssize_t m_cols; + float *m_data; + }; + py::class_(m, "Matrix", py::buffer_protocol()) + .def(py::init()) + /// Construct from a buffer + .def(py::init([](py::buffer b) { + py::buffer_info info = b.request(); + if (info.format != py::format_descriptor::format() || info.ndim != 2) + throw std::runtime_error("Incompatible buffer format!"); + + auto v = new Matrix(info.shape[0], info.shape[1]); + memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols())); + return v; + })) + + .def("rows", &Matrix::rows) + .def("cols", &Matrix::cols) + + /// Bare bones interface + .def("__getitem__", [](const Matrix &m, std::pair i) { + if (i.first >= m.rows() || i.second >= m.cols()) + throw py::index_error(); + return m(i.first, i.second); + }) + .def("__setitem__", [](Matrix &m, std::pair i, float v) { + if (i.first >= m.rows() || i.second >= m.cols()) + throw py::index_error(); + m(i.first, i.second) = v; + }) + /// Provide buffer access + .def_buffer([](Matrix &m) -> py::buffer_info { + return py::buffer_info( + m.data(), /* Pointer to buffer */ + { m.rows(), m.cols() }, /* Buffer dimensions */ + { sizeof(float) * size_t(m.rows()), /* Strides (in bytes) for each index */ + sizeof(float) } + ); + }) + ; + + + // test_inherited_protocol + class SquareMatrix : public Matrix { + public: + SquareMatrix(ssize_t n) : Matrix(n, n) { } + }; + // Derived classes inherit the buffer protocol and the buffer access function + py::class_(m, "SquareMatrix") + .def(py::init()); + + + // test_pointer_to_member_fn + // Tests that passing a pointer to member to the base class works in + // the derived class. + struct Buffer { + int32_t value = 0; + + py::buffer_info get_buffer_info() { + return py::buffer_info(&value, sizeof(value), + py::format_descriptor::format(), 1); + } + }; + py::class_(m, "Buffer", py::buffer_protocol()) + .def(py::init<>()) + .def_readwrite("value", &Buffer::value) + .def_buffer(&Buffer::get_buffer_info); + + + class ConstBuffer { + std::unique_ptr value; + + public: + int32_t get_value() const { return *value; } + void set_value(int32_t v) { *value = v; } + + py::buffer_info get_buffer_info() const { + return py::buffer_info(value.get(), sizeof(*value), + py::format_descriptor::format(), 1); + } + + ConstBuffer() : value(new int32_t{0}) { }; + }; + py::class_(m, "ConstBuffer", py::buffer_protocol()) + .def(py::init<>()) + .def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value) + .def_buffer(&ConstBuffer::get_buffer_info); + + struct DerivedBuffer : public Buffer { }; + py::class_(m, "DerivedBuffer", py::buffer_protocol()) + .def(py::init<>()) + .def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value) + .def_buffer(&DerivedBuffer::get_buffer_info); + +} diff --git a/external/pybind11/tests/test_buffers.py b/external/pybind11/tests/test_buffers.py new file mode 100644 index 0000000..c348be5 --- /dev/null +++ b/external/pybind11/tests/test_buffers.py @@ -0,0 +1,83 @@ +import struct +import pytest +from pybind11_tests import buffers as m +from pybind11_tests import ConstructorStats + +pytestmark = pytest.requires_numpy + +with pytest.suppress(ImportError): + import numpy as np + + +def test_from_python(): + with pytest.raises(RuntimeError) as excinfo: + m.Matrix(np.array([1, 2, 3])) # trying to assign a 1D array + assert str(excinfo.value) == "Incompatible buffer format!" + + m3 = np.array([[1, 2, 3], [4, 5, 6]]).astype(np.float32) + m4 = m.Matrix(m3) + + for i in range(m4.rows()): + for j in range(m4.cols()): + assert m3[i, j] == m4[i, j] + + cstats = ConstructorStats.get(m.Matrix) + assert cstats.alive() == 1 + del m3, m4 + assert cstats.alive() == 0 + assert cstats.values() == ["2x3 matrix"] + assert cstats.copy_constructions == 0 + # assert cstats.move_constructions >= 0 # Don't invoke any + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + +# PyPy: Memory leak in the "np.array(m, copy=False)" call +# https://bitbucket.org/pypy/pypy/issues/2444 +@pytest.unsupported_on_pypy +def test_to_python(): + mat = m.Matrix(5, 5) + assert memoryview(mat).shape == (5, 5) + + assert mat[2, 3] == 0 + mat[2, 3] = 4 + assert mat[2, 3] == 4 + + mat2 = np.array(mat, copy=False) + assert mat2.shape == (5, 5) + assert abs(mat2).sum() == 4 + assert mat2[2, 3] == 4 + mat2[2, 3] = 5 + assert mat2[2, 3] == 5 + + cstats = ConstructorStats.get(m.Matrix) + assert cstats.alive() == 1 + del mat + pytest.gc_collect() + assert cstats.alive() == 1 + del mat2 # holds a mat reference + pytest.gc_collect() + assert cstats.alive() == 0 + assert cstats.values() == ["5x5 matrix"] + assert cstats.copy_constructions == 0 + # assert cstats.move_constructions >= 0 # Don't invoke any + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + +@pytest.unsupported_on_pypy +def test_inherited_protocol(): + """SquareMatrix is derived from Matrix and inherits the buffer protocol""" + + matrix = m.SquareMatrix(5) + assert memoryview(matrix).shape == (5, 5) + assert np.asarray(matrix).shape == (5, 5) + + +@pytest.unsupported_on_pypy +def test_pointer_to_member_fn(): + for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: + buf = cls() + buf.value = 0x12345678 + value = struct.unpack('i', bytearray(buf))[0] + assert value == 0x12345678 diff --git a/external/pybind11/tests/test_builtin_casters.cpp b/external/pybind11/tests/test_builtin_casters.cpp new file mode 100644 index 0000000..b73e96e --- /dev/null +++ b/external/pybind11/tests/test_builtin_casters.cpp @@ -0,0 +1,156 @@ +/* + tests/test_builtin_casters.cpp -- Casters available without any additional headers + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +TEST_SUBMODULE(builtin_casters, m) { + // test_simple_string + m.def("string_roundtrip", [](const char *s) { return s; }); + + // test_unicode_conversion + // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte + char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/; + char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; + std::wstring wstr; + wstr.push_back(0x61); // a + wstr.push_back(0x2e18); // ⸘ + if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 + else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 + wstr.push_back(0x7a); // z + + m.def("good_utf8_string", []() { return std::string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀 + m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z + m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z + m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z + m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); + m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); + // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError + if (PY_MAJOR_VERSION >= 3) + m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); }); + if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2) + m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); + m.def("u8_Z", []() -> char { return 'Z'; }); + m.def("u8_eacute", []() -> char { return '\xe9'; }); + m.def("u16_ibang", [=]() -> char16_t { return ib16; }); + m.def("u32_mathbfA", [=]() -> char32_t { return mathbfA32; }); + m.def("wchar_heart", []() -> wchar_t { return 0x2665; }); + + // test_single_char_arguments + m.attr("wchar_size") = py::cast(sizeof(wchar_t)); + m.def("ord_char", [](char c) -> int { return static_cast(c); }); + m.def("ord_char16", [](char16_t c) -> uint16_t { return c; }); + m.def("ord_char32", [](char32_t c) -> uint32_t { return c; }); + m.def("ord_wchar", [](wchar_t c) -> int { return c; }); + + // test_bytes_to_string + m.def("strlen", [](char *s) { return strlen(s); }); + m.def("string_length", [](std::string s) { return s.length(); }); + + // test_string_view +#ifdef PYBIND11_HAS_STRING_VIEW + m.attr("has_string_view") = true; + m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); + m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); }); + m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); }); + m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); + m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); + m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); + m.def("string_view_return", []() { return std::string_view(u8"utf8 secret \U0001f382"); }); + m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); + m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); +#endif + + // test_integer_casting + m.def("i32_str", [](std::int32_t v) { return std::to_string(v); }); + m.def("u32_str", [](std::uint32_t v) { return std::to_string(v); }); + m.def("i64_str", [](std::int64_t v) { return std::to_string(v); }); + m.def("u64_str", [](std::uint64_t v) { return std::to_string(v); }); + + // test_tuple + m.def("pair_passthrough", [](std::pair input) { + return std::make_pair(input.second, input.first); + }, "Return a pair in reversed order"); + m.def("tuple_passthrough", [](std::tuple input) { + return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); + }, "Return a triple in reversed order"); + m.def("empty_tuple", []() { return std::tuple<>(); }); + static std::pair lvpair; + static std::tuple lvtuple; + static std::pair>> lvnested; + m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); }); + m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; }); + m.def("rvalue_tuple", []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); }); + m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; }); + m.def("rvalue_nested", []() { + return std::make_pair(RValueCaster{}, std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); }); + m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; }); + + // test_builtins_cast_return_none + m.def("return_none_string", []() -> std::string * { return nullptr; }); + m.def("return_none_char", []() -> const char * { return nullptr; }); + m.def("return_none_bool", []() -> bool * { return nullptr; }); + m.def("return_none_int", []() -> int * { return nullptr; }); + m.def("return_none_float", []() -> float * { return nullptr; }); + + // test_none_deferred + m.def("defer_none_cstring", [](char *) { return false; }); + m.def("defer_none_cstring", [](py::none) { return true; }); + m.def("defer_none_custom", [](UserType *) { return false; }); + m.def("defer_none_custom", [](py::none) { return true; }); + m.def("nodefer_none_void", [](void *) { return true; }); + m.def("nodefer_none_void", [](py::none) { return false; }); + + // test_void_caster + m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile + m.def("cast_nullptr_t", []() { return std::nullptr_t{}; }); + + // test_bool_caster + m.def("bool_passthrough", [](bool arg) { return arg; }); + m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg().noconvert()); + + // test_reference_wrapper + m.def("refwrap_builtin", [](std::reference_wrapper p) { return 10 * p.get(); }); + m.def("refwrap_usertype", [](std::reference_wrapper p) { return p.get().value(); }); + // Not currently supported (std::pair caster has return-by-value cast operator); + // triggers static_assert failure. + //m.def("refwrap_pair", [](std::reference_wrapper>) { }); + + m.def("refwrap_list", [](bool copy) { + static IncType x1(1), x2(2); + py::list l; + for (auto &f : {std::ref(x1), std::ref(x2)}) { + l.append(py::cast(f, copy ? py::return_value_policy::copy + : py::return_value_policy::reference)); + } + return l; + }, "copy"_a); + + m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); + m.def("refwrap_call_iiw", [](IncType &w, py::function f) { + py::list l; + l.append(f(std::ref(w))); + l.append(f(std::cref(w))); + IncType x(w.value()); + l.append(f(std::ref(x))); + IncType y(w.value()); + auto r3 = std::ref(y); + l.append(f(r3)); + return l; + }); + + // test_complex + m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); + m.def("complex_cast", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); +} diff --git a/external/pybind11/tests/test_builtin_casters.py b/external/pybind11/tests/test_builtin_casters.py new file mode 100644 index 0000000..bc094a3 --- /dev/null +++ b/external/pybind11/tests/test_builtin_casters.py @@ -0,0 +1,322 @@ +# Python < 3 needs this: coding=utf-8 +import pytest + +from pybind11_tests import builtin_casters as m +from pybind11_tests import UserType, IncType + + +def test_simple_string(): + assert m.string_roundtrip("const char *") == "const char *" + + +def test_unicode_conversion(): + """Tests unicode conversion and error reporting.""" + assert m.good_utf8_string() == u"Say utf8‽ 🎂 𝐀" + assert m.good_utf16_string() == u"b‽🎂𝐀z" + assert m.good_utf32_string() == u"a𝐀🎂‽z" + assert m.good_wchar_string() == u"a⸘𝐀z" + + with pytest.raises(UnicodeDecodeError): + m.bad_utf8_string() + + with pytest.raises(UnicodeDecodeError): + m.bad_utf16_string() + + # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7) + if hasattr(m, "bad_utf32_string"): + with pytest.raises(UnicodeDecodeError): + m.bad_utf32_string() + if hasattr(m, "bad_wchar_string"): + with pytest.raises(UnicodeDecodeError): + m.bad_wchar_string() + + assert m.u8_Z() == 'Z' + assert m.u8_eacute() == u'é' + assert m.u16_ibang() == u'‽' + assert m.u32_mathbfA() == u'𝐀' + assert m.wchar_heart() == u'♥' + + +def test_single_char_arguments(): + """Tests failures for passing invalid inputs to char-accepting functions""" + def toobig_message(r): + return "Character code point not in range({0:#x})".format(r) + toolong_message = "Expected a character, but multi-character string found" + + assert m.ord_char(u'a') == 0x61 # simple ASCII + assert m.ord_char(u'é') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char + with pytest.raises(ValueError) as excinfo: + assert m.ord_char(u'Ā') == 0x100 # requires 2 bytes, doesn't fit in a char + assert str(excinfo.value) == toobig_message(0x100) + with pytest.raises(ValueError) as excinfo: + assert m.ord_char(u'ab') + assert str(excinfo.value) == toolong_message + + assert m.ord_char16(u'a') == 0x61 + assert m.ord_char16(u'é') == 0xE9 + assert m.ord_char16(u'Ā') == 0x100 + assert m.ord_char16(u'‽') == 0x203d + assert m.ord_char16(u'♥') == 0x2665 + with pytest.raises(ValueError) as excinfo: + assert m.ord_char16(u'🎂') == 0x1F382 # requires surrogate pair + assert str(excinfo.value) == toobig_message(0x10000) + with pytest.raises(ValueError) as excinfo: + assert m.ord_char16(u'aa') + assert str(excinfo.value) == toolong_message + + assert m.ord_char32(u'a') == 0x61 + assert m.ord_char32(u'é') == 0xE9 + assert m.ord_char32(u'Ā') == 0x100 + assert m.ord_char32(u'‽') == 0x203d + assert m.ord_char32(u'♥') == 0x2665 + assert m.ord_char32(u'🎂') == 0x1F382 + with pytest.raises(ValueError) as excinfo: + assert m.ord_char32(u'aa') + assert str(excinfo.value) == toolong_message + + assert m.ord_wchar(u'a') == 0x61 + assert m.ord_wchar(u'é') == 0xE9 + assert m.ord_wchar(u'Ā') == 0x100 + assert m.ord_wchar(u'‽') == 0x203d + assert m.ord_wchar(u'♥') == 0x2665 + if m.wchar_size == 2: + with pytest.raises(ValueError) as excinfo: + assert m.ord_wchar(u'🎂') == 0x1F382 # requires surrogate pair + assert str(excinfo.value) == toobig_message(0x10000) + else: + assert m.ord_wchar(u'🎂') == 0x1F382 + with pytest.raises(ValueError) as excinfo: + assert m.ord_wchar(u'aa') + assert str(excinfo.value) == toolong_message + + +def test_bytes_to_string(): + """Tests the ability to pass bytes to C++ string-accepting functions. Note that this is + one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" + # Issue #816 + import sys + byte = bytes if sys.version_info[0] < 3 else str + + assert m.strlen(byte("hi")) == 2 + assert m.string_length(byte("world")) == 5 + assert m.string_length(byte("a\x00b")) == 3 + assert m.strlen(byte("a\x00b")) == 1 # C-string limitation + + # passing in a utf8 encoded string should work + assert m.string_length(u'💩'.encode("utf8")) == 4 + + +@pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no ") +def test_string_view(capture): + """Tests support for C++17 string_view arguments and return values""" + assert m.string_view_chars("Hi") == [72, 105] + assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] + assert m.string_view16_chars("Hi 🎂") == [72, 105, 32, 0xd83c, 0xdf82] + assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874] + + assert m.string_view_return() == "utf8 secret 🎂" + assert m.string_view16_return() == "utf16 secret 🎂" + assert m.string_view32_return() == "utf32 secret 🎂" + + with capture: + m.string_view_print("Hi") + m.string_view_print("utf8 🎂") + m.string_view16_print("utf16 🎂") + m.string_view32_print("utf32 🎂") + assert capture == """ + Hi 2 + utf8 🎂 9 + utf16 🎂 8 + utf32 🎂 7 + """ + + with capture: + m.string_view_print("Hi, ascii") + m.string_view_print("Hi, utf8 🎂") + m.string_view16_print("Hi, utf16 🎂") + m.string_view32_print("Hi, utf32 🎂") + assert capture == """ + Hi, ascii 9 + Hi, utf8 🎂 13 + Hi, utf16 🎂 12 + Hi, utf32 🎂 11 + """ + + +def test_integer_casting(): + """Issue #929 - out-of-range integer values shouldn't be accepted""" + import sys + assert m.i32_str(-1) == "-1" + assert m.i64_str(-1) == "-1" + assert m.i32_str(2000000000) == "2000000000" + assert m.u32_str(2000000000) == "2000000000" + if sys.version_info < (3,): + assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' + assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' + assert m.i64_str(long(-999999999999)) == "-999999999999" # noqa: F821 undefined name + assert m.u64_str(long(999999999999)) == "999999999999" # noqa: F821 undefined name 'long' + else: + assert m.i64_str(-999999999999) == "-999999999999" + assert m.u64_str(999999999999) == "999999999999" + + with pytest.raises(TypeError) as excinfo: + m.u32_str(-1) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.u64_str(-1) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.i32_str(-3000000000) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.i32_str(3000000000) + assert "incompatible function arguments" in str(excinfo.value) + + if sys.version_info < (3,): + with pytest.raises(TypeError) as excinfo: + m.u32_str(long(-1)) # noqa: F821 undefined name 'long' + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.u64_str(long(-1)) # noqa: F821 undefined name 'long' + assert "incompatible function arguments" in str(excinfo.value) + + +def test_tuple(doc): + """std::pair <-> tuple & std::tuple <-> tuple""" + assert m.pair_passthrough((True, "test")) == ("test", True) + assert m.tuple_passthrough((True, "test", 5)) == (5, "test", True) + # Any sequence can be cast to a std::pair or std::tuple + assert m.pair_passthrough([True, "test"]) == ("test", True) + assert m.tuple_passthrough([True, "test", 5]) == (5, "test", True) + assert m.empty_tuple() == () + + assert doc(m.pair_passthrough) == """ + pair_passthrough(arg0: Tuple[bool, str]) -> Tuple[str, bool] + + Return a pair in reversed order + """ + assert doc(m.tuple_passthrough) == """ + tuple_passthrough(arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool] + + Return a triple in reversed order + """ + + assert m.rvalue_pair() == ("rvalue", "rvalue") + assert m.lvalue_pair() == ("lvalue", "lvalue") + assert m.rvalue_tuple() == ("rvalue", "rvalue", "rvalue") + assert m.lvalue_tuple() == ("lvalue", "lvalue", "lvalue") + assert m.rvalue_nested() == ("rvalue", ("rvalue", ("rvalue", "rvalue"))) + assert m.lvalue_nested() == ("lvalue", ("lvalue", ("lvalue", "lvalue"))) + + +def test_builtins_cast_return_none(): + """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None""" + assert m.return_none_string() is None + assert m.return_none_char() is None + assert m.return_none_bool() is None + assert m.return_none_int() is None + assert m.return_none_float() is None + + +def test_none_deferred(): + """None passed as various argument types should defer to other overloads""" + assert not m.defer_none_cstring("abc") + assert m.defer_none_cstring(None) + assert not m.defer_none_custom(UserType()) + assert m.defer_none_custom(None) + assert m.nodefer_none_void(None) + + +def test_void_caster(): + assert m.load_nullptr_t(None) is None + assert m.cast_nullptr_t() is None + + +def test_reference_wrapper(): + """std::reference_wrapper for builtin and user types""" + assert m.refwrap_builtin(42) == 420 + assert m.refwrap_usertype(UserType(42)) == 42 + + with pytest.raises(TypeError) as excinfo: + m.refwrap_builtin(None) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.refwrap_usertype(None) + assert "incompatible function arguments" in str(excinfo.value) + + a1 = m.refwrap_list(copy=True) + a2 = m.refwrap_list(copy=True) + assert [x.value for x in a1] == [2, 3] + assert [x.value for x in a2] == [2, 3] + assert not a1[0] is a2[0] and not a1[1] is a2[1] + + b1 = m.refwrap_list(copy=False) + b2 = m.refwrap_list(copy=False) + assert [x.value for x in b1] == [1, 2] + assert [x.value for x in b2] == [1, 2] + assert b1[0] is b2[0] and b1[1] is b2[1] + + assert m.refwrap_iiw(IncType(5)) == 5 + assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10] + + +def test_complex_cast(): + """std::complex casts""" + assert m.complex_cast(1) == "1.0" + assert m.complex_cast(2j) == "(0.0, 2.0)" + + +def test_bool_caster(): + """Test bool caster implicit conversions.""" + convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert + + def require_implicit(v): + pytest.raises(TypeError, noconvert, v) + + def cant_convert(v): + pytest.raises(TypeError, convert, v) + + # straight up bool + assert convert(True) is True + assert convert(False) is False + assert noconvert(True) is True + assert noconvert(False) is False + + # None requires implicit conversion + require_implicit(None) + assert convert(None) is False + + class A(object): + def __init__(self, x): + self.x = x + + def __nonzero__(self): + return self.x + + def __bool__(self): + return self.x + + class B(object): + pass + + # Arbitrary objects are not accepted + cant_convert(object()) + cant_convert(B()) + + # Objects with __nonzero__ / __bool__ defined can be converted + require_implicit(A(True)) + assert convert(A(True)) is True + assert convert(A(False)) is False + + +@pytest.requires_numpy +def test_numpy_bool(): + import numpy as np + convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert + + # np.bool_ is not considered implicit + assert convert(np.bool_(True)) is True + assert convert(np.bool_(False)) is False + assert noconvert(np.bool_(True)) is True + assert noconvert(np.bool_(False)) is False diff --git a/external/pybind11/tests/test_call_policies.cpp b/external/pybind11/tests/test_call_policies.cpp new file mode 100644 index 0000000..8642188 --- /dev/null +++ b/external/pybind11/tests/test_call_policies.cpp @@ -0,0 +1,98 @@ +/* + tests/test_call_policies.cpp -- keep_alive and call_guard + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +struct CustomGuard { + static bool enabled; + + CustomGuard() { enabled = true; } + ~CustomGuard() { enabled = false; } + + static const char *report_status() { return enabled ? "guarded" : "unguarded"; } +}; +bool CustomGuard::enabled = false; + +struct DependentGuard { + static bool enabled; + + DependentGuard() { enabled = CustomGuard::enabled; } + ~DependentGuard() { enabled = false; } + + static const char *report_status() { return enabled ? "guarded" : "unguarded"; } +}; +bool DependentGuard::enabled = false; + +TEST_SUBMODULE(call_policies, m) { + // Parent/Child are used in: + // test_keep_alive_argument, test_keep_alive_return_value, test_alive_gc_derived, + // test_alive_gc_multi_derived, test_return_none, test_keep_alive_constructor + class Child { + public: + Child() { py::print("Allocating child."); } + ~Child() { py::print("Releasing child."); } + }; + py::class_(m, "Child") + .def(py::init<>()); + + class Parent { + public: + Parent() { py::print("Allocating parent."); } + ~Parent() { py::print("Releasing parent."); } + void addChild(Child *) { } + Child *returnChild() { return new Child(); } + Child *returnNullChild() { return nullptr; } + }; + py::class_(m, "Parent") + .def(py::init<>()) + .def(py::init([](Child *) { return new Parent(); }), py::keep_alive<1, 2>()) + .def("addChild", &Parent::addChild) + .def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>()) + .def("returnChild", &Parent::returnChild) + .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) + .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) + .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()); + +#if !defined(PYPY_VERSION) + // test_alive_gc + class ParentGC : public Parent { + public: + using Parent::Parent; + }; + py::class_(m, "ParentGC", py::dynamic_attr()) + .def(py::init<>()); +#endif + + // test_call_guard + m.def("unguarded_call", &CustomGuard::report_status); + m.def("guarded_call", &CustomGuard::report_status, py::call_guard()); + + m.def("multiple_guards_correct_order", []() { + return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status(); + }, py::call_guard()); + + m.def("multiple_guards_wrong_order", []() { + return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status(); + }, py::call_guard()); + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + // `py::call_guard()` should work in PyPy as well, + // but it's unclear how to test it without `PyGILState_GetThisThreadState`. + auto report_gil_status = []() { + auto is_gil_held = false; + if (auto tstate = py::detail::get_thread_state_unchecked()) + is_gil_held = (tstate == PyGILState_GetThisThreadState()); + + return is_gil_held ? "GIL held" : "GIL released"; + }; + + m.def("with_gil", report_gil_status); + m.def("without_gil", report_gil_status, py::call_guard()); +#endif +} diff --git a/external/pybind11/tests/test_call_policies.py b/external/pybind11/tests/test_call_policies.py new file mode 100644 index 0000000..7c83559 --- /dev/null +++ b/external/pybind11/tests/test_call_policies.py @@ -0,0 +1,187 @@ +import pytest +from pybind11_tests import call_policies as m +from pybind11_tests import ConstructorStats + + +def test_keep_alive_argument(capture): + n_inst = ConstructorStats.detail_reg_inst() + with capture: + p = m.Parent() + assert capture == "Allocating parent." + with capture: + p.addChild(m.Child()) + assert ConstructorStats.detail_reg_inst() == n_inst + 1 + assert capture == """ + Allocating child. + Releasing child. + """ + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == "Releasing parent." + + with capture: + p = m.Parent() + assert capture == "Allocating parent." + with capture: + p.addChildKeepAlive(m.Child()) + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + assert capture == "Allocating child." + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == """ + Releasing parent. + Releasing child. + """ + + +def test_keep_alive_return_value(capture): + n_inst = ConstructorStats.detail_reg_inst() + with capture: + p = m.Parent() + assert capture == "Allocating parent." + with capture: + p.returnChild() + assert ConstructorStats.detail_reg_inst() == n_inst + 1 + assert capture == """ + Allocating child. + Releasing child. + """ + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == "Releasing parent." + + with capture: + p = m.Parent() + assert capture == "Allocating parent." + with capture: + p.returnChildKeepAlive() + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + assert capture == "Allocating child." + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == """ + Releasing parent. + Releasing child. + """ + + +# https://bitbucket.org/pypy/pypy/issues/2447 +@pytest.unsupported_on_pypy +def test_alive_gc(capture): + n_inst = ConstructorStats.detail_reg_inst() + p = m.ParentGC() + p.addChildKeepAlive(m.Child()) + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + lst = [p] + lst.append(lst) # creates a circular reference + with capture: + del p, lst + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == """ + Releasing parent. + Releasing child. + """ + + +def test_alive_gc_derived(capture): + class Derived(m.Parent): + pass + + n_inst = ConstructorStats.detail_reg_inst() + p = Derived() + p.addChildKeepAlive(m.Child()) + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + lst = [p] + lst.append(lst) # creates a circular reference + with capture: + del p, lst + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == """ + Releasing parent. + Releasing child. + """ + + +def test_alive_gc_multi_derived(capture): + class Derived(m.Parent, m.Child): + def __init__(self): + m.Parent.__init__(self) + m.Child.__init__(self) + + n_inst = ConstructorStats.detail_reg_inst() + p = Derived() + p.addChildKeepAlive(m.Child()) + # +3 rather than +2 because Derived corresponds to two registered instances + assert ConstructorStats.detail_reg_inst() == n_inst + 3 + lst = [p] + lst.append(lst) # creates a circular reference + with capture: + del p, lst + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == """ + Releasing parent. + Releasing child. + Releasing child. + """ + + +def test_return_none(capture): + n_inst = ConstructorStats.detail_reg_inst() + with capture: + p = m.Parent() + assert capture == "Allocating parent." + with capture: + p.returnNullChildKeepAliveChild() + assert ConstructorStats.detail_reg_inst() == n_inst + 1 + assert capture == "" + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == "Releasing parent." + + with capture: + p = m.Parent() + assert capture == "Allocating parent." + with capture: + p.returnNullChildKeepAliveParent() + assert ConstructorStats.detail_reg_inst() == n_inst + 1 + assert capture == "" + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == "Releasing parent." + + +def test_keep_alive_constructor(capture): + n_inst = ConstructorStats.detail_reg_inst() + + with capture: + p = m.Parent(m.Child()) + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + assert capture == """ + Allocating child. + Allocating parent. + """ + with capture: + del p + assert ConstructorStats.detail_reg_inst() == n_inst + assert capture == """ + Releasing parent. + Releasing child. + """ + + +def test_call_guard(): + assert m.unguarded_call() == "unguarded" + assert m.guarded_call() == "guarded" + + assert m.multiple_guards_correct_order() == "guarded & guarded" + assert m.multiple_guards_wrong_order() == "unguarded & guarded" + + if hasattr(m, "with_gil"): + assert m.with_gil() == "GIL held" + assert m.without_gil() == "GIL released" diff --git a/external/pybind11/tests/test_callbacks.cpp b/external/pybind11/tests/test_callbacks.cpp new file mode 100644 index 0000000..273eacc --- /dev/null +++ b/external/pybind11/tests/test_callbacks.cpp @@ -0,0 +1,149 @@ +/* + tests/test_callbacks.cpp -- callbacks + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include + + +int dummy_function(int i) { return i + 1; } + +TEST_SUBMODULE(callbacks, m) { + // test_callbacks, test_function_signatures + m.def("test_callback1", [](py::object func) { return func(); }); + m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); }); + m.def("test_callback3", [](const std::function &func) { + return "func(43) = " + std::to_string(func(43)); }); + m.def("test_callback4", []() -> std::function { return [](int i) { return i+1; }; }); + m.def("test_callback5", []() { + return py::cpp_function([](int i) { return i+1; }, py::arg("number")); + }); + + // test_keyword_args_and_generalized_unpacking + m.def("test_tuple_unpacking", [](py::function f) { + auto t1 = py::make_tuple(2, 3); + auto t2 = py::make_tuple(5, 6); + return f("positional", 1, *t1, 4, *t2); + }); + + m.def("test_dict_unpacking", [](py::function f) { + auto d1 = py::dict("key"_a="value", "a"_a=1); + auto d2 = py::dict(); + auto d3 = py::dict("b"_a=2); + return f("positional", 1, **d1, **d2, **d3); + }); + + m.def("test_keyword_args", [](py::function f) { + return f("x"_a=10, "y"_a=20); + }); + + m.def("test_unpacking_and_keywords1", [](py::function f) { + auto args = py::make_tuple(2); + auto kwargs = py::dict("d"_a=4); + return f(1, *args, "c"_a=3, **kwargs); + }); + + m.def("test_unpacking_and_keywords2", [](py::function f) { + auto kwargs1 = py::dict("a"_a=1); + auto kwargs2 = py::dict("c"_a=3, "d"_a=4); + return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5, + "key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5); + }); + + m.def("test_unpacking_error1", [](py::function f) { + auto kwargs = py::dict("x"_a=3); + return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword + }); + + m.def("test_unpacking_error2", [](py::function f) { + auto kwargs = py::dict("x"_a=3); + return f(**kwargs, "x"_a=1); // duplicate keyword after ** + }); + + m.def("test_arg_conversion_error1", [](py::function f) { + f(234, UnregisteredType(), "kw"_a=567); + }); + + m.def("test_arg_conversion_error2", [](py::function f) { + f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567); + }); + + // test_lambda_closure_cleanup + struct Payload { + Payload() { print_default_created(this); } + ~Payload() { print_destroyed(this); } + Payload(const Payload &) { print_copy_created(this); } + Payload(Payload &&) { print_move_created(this); } + }; + // Export the payload constructor statistics for testing purposes: + m.def("payload_cstats", &ConstructorStats::get); + /* Test cleanup of lambda closure */ + m.def("test_cleanup", []() -> std::function { + Payload p; + + return [p]() { + /* p should be cleaned up when the returned function is garbage collected */ + (void) p; + }; + }); + + // test_cpp_function_roundtrip + /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */ + m.def("dummy_function", &dummy_function); + m.def("dummy_function2", [](int i, int j) { return i + j; }); + m.def("roundtrip", [](std::function f, bool expect_none = false) { + if (expect_none && f) + throw std::runtime_error("Expected None to be converted to empty std::function"); + return f; + }, py::arg("f"), py::arg("expect_none")=false); + m.def("test_dummy_function", [](const std::function &f) -> std::string { + using fn_type = int (*)(int); + auto result = f.target(); + if (!result) { + auto r = f(1); + return "can't convert to function pointer: eval(1) = " + std::to_string(r); + } else if (*result == dummy_function) { + auto r = (*result)(1); + return "matches dummy_function: eval(1) = " + std::to_string(r); + } else { + return "argument does NOT match dummy_function. This should never happen!"; + } + }); + + class AbstractBase { public: virtual unsigned int func() = 0; }; + m.def("func_accepting_func_accepting_base", [](std::function) { }); + + struct MovableObject { + bool valid = true; + + MovableObject() = default; + MovableObject(const MovableObject &) = default; + MovableObject &operator=(const MovableObject &) = default; + MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; } + MovableObject &operator=(MovableObject &&o) { + valid = o.valid; + o.valid = false; + return *this; + } + }; + py::class_(m, "MovableObject"); + + // test_movable_object + m.def("callback_with_movable", [](std::function f) { + auto x = MovableObject(); + f(x); // lvalue reference shouldn't move out object + return x.valid; // must still return `true` + }); + + // test_bound_method_callback + struct CppBoundMethodTest {}; + py::class_(m, "CppBoundMethodTest") + .def(py::init<>()) + .def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; }); +} diff --git a/external/pybind11/tests/test_callbacks.py b/external/pybind11/tests/test_callbacks.py new file mode 100644 index 0000000..93c42c2 --- /dev/null +++ b/external/pybind11/tests/test_callbacks.py @@ -0,0 +1,107 @@ +import pytest +from pybind11_tests import callbacks as m + + +def test_callbacks(): + from functools import partial + + def func1(): + return "func1" + + def func2(a, b, c, d): + return "func2", a, b, c, d + + def func3(a): + return "func3({})".format(a) + + assert m.test_callback1(func1) == "func1" + assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5) + assert m.test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4) + assert m.test_callback1(partial(func3, "partial")) == "func3(partial)" + assert m.test_callback3(lambda i: i + 1) == "func(43) = 44" + + f = m.test_callback4() + assert f(43) == 44 + f = m.test_callback5() + assert f(number=43) == 44 + + +def test_bound_method_callback(): + # Bound Python method: + class MyClass: + def double(self, val): + return 2 * val + + z = MyClass() + assert m.test_callback3(z.double) == "func(43) = 86" + + z = m.CppBoundMethodTest() + assert m.test_callback3(z.triple) == "func(43) = 129" + + +def test_keyword_args_and_generalized_unpacking(): + + def f(*args, **kwargs): + return args, kwargs + + assert m.test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {}) + assert m.test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2}) + assert m.test_keyword_args(f) == ((), {"x": 10, "y": 20}) + assert m.test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4}) + assert m.test_unpacking_and_keywords2(f) == ( + ("positional", 1, 2, 3, 4, 5), + {"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5} + ) + + with pytest.raises(TypeError) as excinfo: + m.test_unpacking_error1(f) + assert "Got multiple values for keyword argument" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.test_unpacking_error2(f) + assert "Got multiple values for keyword argument" in str(excinfo.value) + + with pytest.raises(RuntimeError) as excinfo: + m.test_arg_conversion_error1(f) + assert "Unable to convert call argument" in str(excinfo.value) + + with pytest.raises(RuntimeError) as excinfo: + m.test_arg_conversion_error2(f) + assert "Unable to convert call argument" in str(excinfo.value) + + +def test_lambda_closure_cleanup(): + m.test_cleanup() + cstats = m.payload_cstats() + assert cstats.alive() == 0 + assert cstats.copy_constructions == 1 + assert cstats.move_constructions >= 1 + + +def test_cpp_function_roundtrip(): + """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" + + assert m.test_dummy_function(m.dummy_function) == "matches dummy_function: eval(1) = 2" + assert (m.test_dummy_function(m.roundtrip(m.dummy_function)) == + "matches dummy_function: eval(1) = 2") + assert m.roundtrip(None, expect_none=True) is None + assert (m.test_dummy_function(lambda x: x + 2) == + "can't convert to function pointer: eval(1) = 3") + + with pytest.raises(TypeError) as excinfo: + m.test_dummy_function(m.dummy_function2) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.test_dummy_function(lambda x, y: x + y) + assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument", + "takes exactly 2 arguments")) + + +def test_function_signatures(doc): + assert doc(m.test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str" + assert doc(m.test_callback4) == "test_callback4() -> Callable[[int], int]" + + +def test_movable_object(): + assert m.callback_with_movable(lambda _: None) is True diff --git a/external/pybind11/tests/test_chrono.cpp b/external/pybind11/tests/test_chrono.cpp new file mode 100644 index 0000000..195a93b --- /dev/null +++ b/external/pybind11/tests/test_chrono.cpp @@ -0,0 +1,47 @@ +/* + tests/test_chrono.cpp -- test conversions to/from std::chrono types + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +TEST_SUBMODULE(chrono, m) { + using system_time = std::chrono::system_clock::time_point; + using steady_time = std::chrono::steady_clock::time_point; + // test_chrono_system_clock + // Return the current time off the wall clock + m.def("test_chrono1", []() { return std::chrono::system_clock::now(); }); + + // test_chrono_system_clock_roundtrip + // Round trip the passed in system clock time + m.def("test_chrono2", [](system_time t) { return t; }); + + // test_chrono_duration_roundtrip + // Round trip the passed in duration + m.def("test_chrono3", [](std::chrono::system_clock::duration d) { return d; }); + + // test_chrono_duration_subtraction_equivalence + // Difference between two passed in time_points + m.def("test_chrono4", [](system_time a, system_time b) { return a - b; }); + + // test_chrono_steady_clock + // Return the current time off the steady_clock + m.def("test_chrono5", []() { return std::chrono::steady_clock::now(); }); + + // test_chrono_steady_clock_roundtrip + // Round trip a steady clock timepoint + m.def("test_chrono6", [](steady_time t) { return t; }); + + // test_floating_point_duration + // Roundtrip a duration in microseconds from a float argument + m.def("test_chrono7", [](std::chrono::microseconds t) { return t; }); + // Float durations (issue #719) + m.def("test_chrono_float_diff", [](std::chrono::duration a, std::chrono::duration b) { + return a - b; }); +} diff --git a/external/pybind11/tests/test_chrono.py b/external/pybind11/tests/test_chrono.py new file mode 100644 index 0000000..2b75bd1 --- /dev/null +++ b/external/pybind11/tests/test_chrono.py @@ -0,0 +1,101 @@ +from pybind11_tests import chrono as m +import datetime + + +def test_chrono_system_clock(): + + # Get the time from both c++ and datetime + date1 = m.test_chrono1() + date2 = datetime.datetime.today() + + # The returned value should be a datetime + assert isinstance(date1, datetime.datetime) + + # The numbers should vary by a very small amount (time it took to execute) + diff = abs(date1 - date2) + + # There should never be a days/seconds difference + assert diff.days == 0 + assert diff.seconds == 0 + + # We test that no more than about 0.5 seconds passes here + # This makes sure that the dates created are very close to the same + # but if the testing system is incredibly overloaded this should still pass + assert diff.microseconds < 500000 + + +def test_chrono_system_clock_roundtrip(): + date1 = datetime.datetime.today() + + # Roundtrip the time + date2 = m.test_chrono2(date1) + + # The returned value should be a datetime + assert isinstance(date2, datetime.datetime) + + # They should be identical (no information lost on roundtrip) + diff = abs(date1 - date2) + assert diff.days == 0 + assert diff.seconds == 0 + assert diff.microseconds == 0 + + +def test_chrono_duration_roundtrip(): + + # Get the difference between two times (a timedelta) + date1 = datetime.datetime.today() + date2 = datetime.datetime.today() + diff = date2 - date1 + + # Make sure this is a timedelta + assert isinstance(diff, datetime.timedelta) + + cpp_diff = m.test_chrono3(diff) + + assert cpp_diff.days == diff.days + assert cpp_diff.seconds == diff.seconds + assert cpp_diff.microseconds == diff.microseconds + + +def test_chrono_duration_subtraction_equivalence(): + + date1 = datetime.datetime.today() + date2 = datetime.datetime.today() + + diff = date2 - date1 + cpp_diff = m.test_chrono4(date2, date1) + + assert cpp_diff.days == diff.days + assert cpp_diff.seconds == diff.seconds + assert cpp_diff.microseconds == diff.microseconds + + +def test_chrono_steady_clock(): + time1 = m.test_chrono5() + assert isinstance(time1, datetime.timedelta) + + +def test_chrono_steady_clock_roundtrip(): + time1 = datetime.timedelta(days=10, seconds=10, microseconds=100) + time2 = m.test_chrono6(time1) + + assert isinstance(time2, datetime.timedelta) + + # They should be identical (no information lost on roundtrip) + assert time1.days == time2.days + assert time1.seconds == time2.seconds + assert time1.microseconds == time2.microseconds + + +def test_floating_point_duration(): + # Test using a floating point number in seconds + time = m.test_chrono7(35.525123) + + assert isinstance(time, datetime.timedelta) + + assert time.seconds == 35 + assert 525122 <= time.microseconds <= 525123 + + diff = m.test_chrono_float_diff(43.789012, 1.123456) + assert diff.seconds == 42 + assert 665556 <= diff.microseconds <= 665557 diff --git a/external/pybind11/tests/test_class.cpp b/external/pybind11/tests/test_class.cpp new file mode 100644 index 0000000..2221906 --- /dev/null +++ b/external/pybind11/tests/test_class.cpp @@ -0,0 +1,357 @@ +/* + tests/test_class.cpp -- test py::class_ definitions and basic functionality + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include "local_bindings.h" + +TEST_SUBMODULE(class_, m) { + // test_instance + struct NoConstructor { + static NoConstructor *new_instance() { + auto *ptr = new NoConstructor(); + print_created(ptr, "via new_instance"); + return ptr; + } + ~NoConstructor() { print_destroyed(this); } + }; + + py::class_(m, "NoConstructor") + .def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); + + // test_inheritance + class Pet { + public: + Pet(const std::string &name, const std::string &species) + : m_name(name), m_species(species) {} + std::string name() const { return m_name; } + std::string species() const { return m_species; } + private: + std::string m_name; + std::string m_species; + }; + + class Dog : public Pet { + public: + Dog(const std::string &name) : Pet(name, "dog") {} + std::string bark() const { return "Woof!"; } + }; + + class Rabbit : public Pet { + public: + Rabbit(const std::string &name) : Pet(name, "parrot") {} + }; + + class Hamster : public Pet { + public: + Hamster(const std::string &name) : Pet(name, "rodent") {} + }; + + class Chimera : public Pet { + Chimera() : Pet("Kimmy", "chimera") {} + }; + + py::class_ pet_class(m, "Pet"); + pet_class + .def(py::init()) + .def("name", &Pet::name) + .def("species", &Pet::species); + + /* One way of declaring a subclass relationship: reference parent's class_ object */ + py::class_(m, "Dog", pet_class) + .def(py::init()); + + /* Another way of declaring a subclass relationship: reference parent's C++ type */ + py::class_(m, "Rabbit") + .def(py::init()); + + /* And another: list parent in class template arguments */ + py::class_(m, "Hamster") + .def(py::init()); + + /* Constructors are not inherited by default */ + py::class_(m, "Chimera"); + + m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); }); + m.def("dog_bark", [](const Dog &dog) { return dog.bark(); }); + + // test_automatic_upcasting + struct BaseClass { virtual ~BaseClass() {} }; + struct DerivedClass1 : BaseClass { }; + struct DerivedClass2 : BaseClass { }; + + py::class_(m, "BaseClass").def(py::init<>()); + py::class_(m, "DerivedClass1").def(py::init<>()); + py::class_(m, "DerivedClass2").def(py::init<>()); + + m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); }); + m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); }); + m.def("return_class_n", [](int n) -> BaseClass* { + if (n == 1) return new DerivedClass1(); + if (n == 2) return new DerivedClass2(); + return new BaseClass(); + }); + m.def("return_none", []() -> BaseClass* { return nullptr; }); + + // test_isinstance + m.def("check_instances", [](py::list l) { + return py::make_tuple( + py::isinstance(l[0]), + py::isinstance(l[1]), + py::isinstance(l[2]), + py::isinstance(l[3]), + py::isinstance(l[4]), + py::isinstance(l[5]), + py::isinstance(l[6]) + ); + }); + + // test_mismatched_holder + struct MismatchBase1 { }; + struct MismatchDerived1 : MismatchBase1 { }; + + struct MismatchBase2 { }; + struct MismatchDerived2 : MismatchBase2 { }; + + m.def("mismatched_holder_1", []() { + auto mod = py::module::import("__main__"); + py::class_>(mod, "MismatchBase1"); + py::class_(mod, "MismatchDerived1"); + }); + m.def("mismatched_holder_2", []() { + auto mod = py::module::import("__main__"); + py::class_(mod, "MismatchBase2"); + py::class_, + MismatchBase2>(mod, "MismatchDerived2"); + }); + + // test_override_static + // #511: problem with inheritance + overwritten def_static + struct MyBase { + static std::unique_ptr make() { + return std::unique_ptr(new MyBase()); + } + }; + + struct MyDerived : MyBase { + static std::unique_ptr make() { + return std::unique_ptr(new MyDerived()); + } + }; + + py::class_(m, "MyBase") + .def_static("make", &MyBase::make); + + py::class_(m, "MyDerived") + .def_static("make", &MyDerived::make) + .def_static("make2", &MyDerived::make); + + // test_implicit_conversion_life_support + struct ConvertibleFromUserType { + int i; + + ConvertibleFromUserType(UserType u) : i(u.value()) { } + }; + + py::class_(m, "AcceptsUserType") + .def(py::init()); + py::implicitly_convertible(); + + m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; }); + m.def("implicitly_convert_variable", [](py::object o) { + // `o` is `UserType` and `r` is a reference to a temporary created by implicit + // conversion. This is valid when called inside a bound function because the temp + // object is attached to the same life support system as the arguments. + const auto &r = o.cast(); + return r.i; + }); + m.add_object("implicitly_convert_variable_fail", [&] { + auto f = [](PyObject *, PyObject *args) -> PyObject * { + auto o = py::reinterpret_borrow(args)[0]; + try { // It should fail here because there is no life support. + o.cast(); + } catch (const py::cast_error &e) { + return py::str(e.what()).release().ptr(); + } + return py::str().release().ptr(); + }; + + auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; + return py::reinterpret_steal(PyCFunction_NewEx(def, nullptr, m.ptr())); + }()); + + // test_operator_new_delete + struct HasOpNewDel { + std::uint64_t i; + static void *operator new(size_t s) { py::print("A new", s); return ::operator new(s); } + static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; } + static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); } + }; + struct HasOpNewDelSize { + std::uint32_t i; + static void *operator new(size_t s) { py::print("B new", s); return ::operator new(s); } + static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; } + static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); } + }; + struct AliasedHasOpNewDelSize { + std::uint64_t i; + static void *operator new(size_t s) { py::print("C new", s); return ::operator new(s); } + static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; } + static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); } + virtual ~AliasedHasOpNewDelSize() = default; + }; + struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { + PyAliasedHasOpNewDelSize() = default; + PyAliasedHasOpNewDelSize(int) { } + std::uint64_t j; + }; + struct HasOpNewDelBoth { + std::uint32_t i[8]; + static void *operator new(size_t s) { py::print("D new", s); return ::operator new(s); } + static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; } + static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); } + static void operator delete(void *p, size_t s) { py::print("D wrong delete", s); return ::operator delete(p); } + }; + py::class_(m, "HasOpNewDel").def(py::init<>()); + py::class_(m, "HasOpNewDelSize").def(py::init<>()); + py::class_(m, "HasOpNewDelBoth").def(py::init<>()); + py::class_ aliased(m, "AliasedHasOpNewDelSize"); + aliased.def(py::init<>()); + aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize)); + aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize)); + + // This test is actually part of test_local_bindings (test_duplicate_local), but we need a + // definition in a different compilation unit within the same module: + bind_local(m, "LocalExternal", py::module_local()); + + // test_bind_protected_functions + class ProtectedA { + protected: + int foo() const { return value; } + + private: + int value = 42; + }; + + class PublicistA : public ProtectedA { + public: + using ProtectedA::foo; + }; + + py::class_(m, "ProtectedA") + .def(py::init<>()) +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + .def("foo", &PublicistA::foo); +#else + .def("foo", static_cast(&PublicistA::foo)); +#endif + + class ProtectedB { + public: + virtual ~ProtectedB() = default; + + protected: + virtual int foo() const { return value; } + + private: + int value = 42; + }; + + class TrampolineB : public ProtectedB { + public: + int foo() const override { PYBIND11_OVERLOAD(int, ProtectedB, foo, ); } + }; + + class PublicistB : public ProtectedB { + public: + using ProtectedB::foo; + }; + + py::class_(m, "ProtectedB") + .def(py::init<>()) +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + .def("foo", &PublicistB::foo); +#else + .def("foo", static_cast(&PublicistB::foo)); +#endif + + // test_brace_initialization + struct BraceInitialization { + int field1; + std::string field2; + }; + + py::class_(m, "BraceInitialization") + .def(py::init()) + .def_readwrite("field1", &BraceInitialization::field1) + .def_readwrite("field2", &BraceInitialization::field2); + + // test_reentrant_implicit_conversion_failure + // #1035: issue with runaway reentrant implicit conversion + struct BogusImplicitConversion { + BogusImplicitConversion(const BogusImplicitConversion &) { } + }; + + py::class_(m, "BogusImplicitConversion") + .def(py::init()); + + py::implicitly_convertible(); +} + +template class BreaksBase { public: virtual ~BreaksBase() = default; }; +template class BreaksTramp : public BreaksBase {}; +// These should all compile just fine: +typedef py::class_, std::unique_ptr>, BreaksTramp<1>> DoesntBreak1; +typedef py::class_, BreaksTramp<2>, std::unique_ptr>> DoesntBreak2; +typedef py::class_, std::unique_ptr>> DoesntBreak3; +typedef py::class_, BreaksTramp<4>> DoesntBreak4; +typedef py::class_> DoesntBreak5; +typedef py::class_, std::shared_ptr>, BreaksTramp<6>> DoesntBreak6; +typedef py::class_, BreaksTramp<7>, std::shared_ptr>> DoesntBreak7; +typedef py::class_, std::shared_ptr>> DoesntBreak8; +#define CHECK_BASE(N) static_assert(std::is_same>::value, \ + "DoesntBreak" #N " has wrong type!") +CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8); +#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same>::value, \ + "DoesntBreak" #N " has wrong type_alias!") +#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void::value, \ + "DoesntBreak" #N " has type alias, but shouldn't!") +CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8); +#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same>>::value, \ + "DoesntBreak" #N " has wrong holder_type!") +CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique); +CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); + +// There's no nice way to test that these fail because they fail to compile; leave them here, +// though, so that they can be manually tested by uncommenting them (and seeing that compilation +// failures occurs). + +// We have to actually look into the type: the typedef alone isn't enough to instantiate the type: +#define CHECK_BROKEN(N) static_assert(std::is_same>::value, \ + "Breaks1 has wrong type!"); + +//// Two holder classes: +//typedef py::class_, std::unique_ptr>, std::unique_ptr>> Breaks1; +//CHECK_BROKEN(1); +//// Two aliases: +//typedef py::class_, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2; +//CHECK_BROKEN(2); +//// Holder + 2 aliases +//typedef py::class_, std::unique_ptr>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3; +//CHECK_BROKEN(3); +//// Alias + 2 holders +//typedef py::class_, std::unique_ptr>, BreaksTramp<-4>, std::shared_ptr>> Breaks4; +//CHECK_BROKEN(4); +//// Invalid option (not a subclass or holder) +//typedef py::class_, BreaksTramp<-4>> Breaks5; +//CHECK_BROKEN(5); +//// Invalid option: multiple inheritance not supported: +//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {}; +//typedef py::class_, BreaksBase<-6>, BreaksBase<-7>> Breaks8; +//CHECK_BROKEN(8); diff --git a/external/pybind11/tests/test_class.py b/external/pybind11/tests/test_class.py new file mode 100644 index 0000000..412d679 --- /dev/null +++ b/external/pybind11/tests/test_class.py @@ -0,0 +1,235 @@ +import pytest + +from pybind11_tests import class_ as m +from pybind11_tests import UserType, ConstructorStats + + +def test_repr(): + # In Python 3.3+, repr() accesses __qualname__ + assert "pybind11_type" in repr(type(UserType)) + assert "UserType" in repr(UserType) + + +def test_instance(msg): + with pytest.raises(TypeError) as excinfo: + m.NoConstructor() + assert msg(excinfo.value) == "m.class_.NoConstructor: No constructor defined!" + + instance = m.NoConstructor.new_instance() + + cstats = ConstructorStats.get(m.NoConstructor) + assert cstats.alive() == 1 + del instance + assert cstats.alive() == 0 + + +def test_docstrings(doc): + assert doc(UserType) == "A `py::class_` type for testing" + assert UserType.__name__ == "UserType" + assert UserType.__module__ == "pybind11_tests" + assert UserType.get_value.__name__ == "get_value" + assert UserType.get_value.__module__ == "pybind11_tests" + + assert doc(UserType.get_value) == """ + get_value(self: m.UserType) -> int + + Get value using a method + """ + assert doc(UserType.value) == "Get/set value using a property" + + assert doc(m.NoConstructor.new_instance) == """ + new_instance() -> m.class_.NoConstructor + + Return an instance + """ + + +def test_inheritance(msg): + roger = m.Rabbit('Rabbit') + assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot" + assert m.pet_name_species(roger) == "Rabbit is a parrot" + + polly = m.Pet('Polly', 'parrot') + assert polly.name() + " is a " + polly.species() == "Polly is a parrot" + assert m.pet_name_species(polly) == "Polly is a parrot" + + molly = m.Dog('Molly') + assert molly.name() + " is a " + molly.species() == "Molly is a dog" + assert m.pet_name_species(molly) == "Molly is a dog" + + fred = m.Hamster('Fred') + assert fred.name() + " is a " + fred.species() == "Fred is a rodent" + + assert m.dog_bark(molly) == "Woof!" + + with pytest.raises(TypeError) as excinfo: + m.dog_bark(polly) + assert msg(excinfo.value) == """ + dog_bark(): incompatible function arguments. The following argument types are supported: + 1. (arg0: m.class_.Dog) -> str + + Invoked with: + """ + + with pytest.raises(TypeError) as excinfo: + m.Chimera("lion", "goat") + assert "No constructor defined!" in str(excinfo.value) + + +def test_automatic_upcasting(): + assert type(m.return_class_1()).__name__ == "DerivedClass1" + assert type(m.return_class_2()).__name__ == "DerivedClass2" + assert type(m.return_none()).__name__ == "NoneType" + # Repeat these a few times in a random order to ensure no invalid caching is applied + assert type(m.return_class_n(1)).__name__ == "DerivedClass1" + assert type(m.return_class_n(2)).__name__ == "DerivedClass2" + assert type(m.return_class_n(0)).__name__ == "BaseClass" + assert type(m.return_class_n(2)).__name__ == "DerivedClass2" + assert type(m.return_class_n(2)).__name__ == "DerivedClass2" + assert type(m.return_class_n(0)).__name__ == "BaseClass" + assert type(m.return_class_n(1)).__name__ == "DerivedClass1" + + +def test_isinstance(): + objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4 + expected = (True, True, True, True, True, False, False) + assert m.check_instances(objects) == expected + + +def test_mismatched_holder(): + import re + + with pytest.raises(RuntimeError) as excinfo: + m.mismatched_holder_1() + assert re.match('generic_type: type ".*MismatchDerived1" does not have a non-default ' + 'holder type while its base ".*MismatchBase1" does', str(excinfo.value)) + + with pytest.raises(RuntimeError) as excinfo: + m.mismatched_holder_2() + assert re.match('generic_type: type ".*MismatchDerived2" has a non-default holder type ' + 'while its base ".*MismatchBase2" does not', str(excinfo.value)) + + +def test_override_static(): + """#511: problem with inheritance + overwritten def_static""" + b = m.MyBase.make() + d1 = m.MyDerived.make2() + d2 = m.MyDerived.make() + + assert isinstance(b, m.MyBase) + assert isinstance(d1, m.MyDerived) + assert isinstance(d2, m.MyDerived) + + +def test_implicit_conversion_life_support(): + """Ensure the lifetime of temporary objects created for implicit conversions""" + assert m.implicitly_convert_argument(UserType(5)) == 5 + assert m.implicitly_convert_variable(UserType(5)) == 5 + + assert "outside a bound function" in m.implicitly_convert_variable_fail(UserType(5)) + + +def test_operator_new_delete(capture): + """Tests that class-specific operator new/delete functions are invoked""" + + class SubAliased(m.AliasedHasOpNewDelSize): + pass + + with capture: + a = m.HasOpNewDel() + b = m.HasOpNewDelSize() + d = m.HasOpNewDelBoth() + assert capture == """ + A new 8 + B new 4 + D new 32 + """ + sz_alias = str(m.AliasedHasOpNewDelSize.size_alias) + sz_noalias = str(m.AliasedHasOpNewDelSize.size_noalias) + with capture: + c = m.AliasedHasOpNewDelSize() + c2 = SubAliased() + assert capture == ( + "C new " + sz_noalias + "\n" + + "C new " + sz_alias + "\n" + ) + + with capture: + del a + pytest.gc_collect() + del b + pytest.gc_collect() + del d + pytest.gc_collect() + assert capture == """ + A delete + B delete 4 + D delete + """ + + with capture: + del c + pytest.gc_collect() + del c2 + pytest.gc_collect() + assert capture == ( + "C delete " + sz_noalias + "\n" + + "C delete " + sz_alias + "\n" + ) + + +def test_bind_protected_functions(): + """Expose protected member functions to Python using a helper class""" + a = m.ProtectedA() + assert a.foo() == 42 + + b = m.ProtectedB() + assert b.foo() == 42 + + class C(m.ProtectedB): + def __init__(self): + m.ProtectedB.__init__(self) + + def foo(self): + return 0 + + c = C() + assert c.foo() == 0 + + +def test_brace_initialization(): + """ Tests that simple POD classes can be constructed using C++11 brace initialization """ + a = m.BraceInitialization(123, "test") + assert a.field1 == 123 + assert a.field2 == "test" + + +@pytest.unsupported_on_pypy +def test_class_refcount(): + """Instances must correctly increase/decrease the reference count of their types (#1029)""" + from sys import getrefcount + + class PyDog(m.Dog): + pass + + for cls in m.Dog, PyDog: + refcount_1 = getrefcount(cls) + molly = [cls("Molly") for _ in range(10)] + refcount_2 = getrefcount(cls) + + del molly + pytest.gc_collect() + refcount_3 = getrefcount(cls) + + assert refcount_1 == refcount_3 + assert refcount_2 > refcount_1 + + +def test_reentrant_implicit_conversion_failure(msg): + # ensure that there is no runaway reentrant implicit conversion (#1035) + with pytest.raises(TypeError) as excinfo: + m.BogusImplicitConversion(0) + assert msg(excinfo.value) == '''__init__(): incompatible constructor arguments. The following argument types are supported: + 1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion) + +Invoked with: 0''' diff --git a/external/pybind11/tests/test_cmake_build/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/CMakeLists.txt new file mode 100644 index 0000000..c9b5fcb --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/CMakeLists.txt @@ -0,0 +1,58 @@ +add_custom_target(test_cmake_build) + +if(CMAKE_VERSION VERSION_LESS 3.1) + # 3.0 needed for interface library for subdirectory_target/installed_target + # 3.1 needed for cmake -E env for testing + return() +endif() + +include(CMakeParseArguments) +function(pybind11_add_build_test name) + cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN}) + + set(build_options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/mock_install" + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}" + "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}") + if(NOT ARG_INSTALL) + list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${PROJECT_SOURCE_DIR}") + endif() + + add_custom_target(test_${name} ${CMAKE_CTEST_COMMAND} + --quiet --output-log ${name}.log + --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/${name}" + "${CMAKE_CURRENT_BINARY_DIR}/${name}" + --build-config Release + --build-noclean + --build-generator ${CMAKE_GENERATOR} + $<$:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target check + --build-options ${build_options} + ) + if(ARG_INSTALL) + add_dependencies(test_${name} mock_install) + endif() + add_dependencies(test_cmake_build test_${name}) +endfunction() + +pybind11_add_build_test(subdirectory_function) +pybind11_add_build_test(subdirectory_target) +if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") + pybind11_add_build_test(subdirectory_embed) +endif() + +if(PYBIND11_INSTALL) + add_custom_target(mock_install ${CMAKE_COMMAND} + "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/mock_install" + -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" + ) + + pybind11_add_build_test(installed_function INSTALL) + pybind11_add_build_test(installed_target INSTALL) + if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") + pybind11_add_build_test(installed_embed INSTALL) + endif() +endif() + +add_dependencies(check test_cmake_build) diff --git a/external/pybind11/tests/test_cmake_build/embed.cpp b/external/pybind11/tests/test_cmake_build/embed.cpp new file mode 100644 index 0000000..b9581d2 --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/embed.cpp @@ -0,0 +1,21 @@ +#include +namespace py = pybind11; + +PYBIND11_EMBEDDED_MODULE(test_cmake_build, m) { + m.def("add", [](int i, int j) { return i + j; }); +} + +int main(int argc, char *argv[]) { + if (argc != 2) + throw std::runtime_error("Expected test.py file as the first argument"); + auto test_py_file = argv[1]; + + py::scoped_interpreter guard{}; + + auto m = py::module::import("test_cmake_build"); + if (m.attr("add")(1, 2).cast() != 3) + throw std::runtime_error("embed.cpp failed"); + + py::module::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp"); + py::eval_file(test_py_file, py::globals()); +} diff --git a/external/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt new file mode 100644 index 0000000..f7fc09c --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.0) +project(test_installed_embed CXX) + +set(CMAKE_MODULE_PATH "") +find_package(pybind11 CONFIG REQUIRED) +message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + +add_executable(test_cmake_build ../embed.cpp) +target_link_libraries(test_cmake_build PRIVATE pybind11::embed) + +# Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::embed). +# This may be needed to resolve header conflicts, e.g. between Python release and debug headers. +set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + +add_custom_target(check $ ${PROJECT_SOURCE_DIR}/../test.py) diff --git a/external/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt new file mode 100644 index 0000000..e0c20a8 --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8.12) +project(test_installed_module CXX) + +set(CMAKE_MODULE_PATH "") + +find_package(pybind11 CONFIG REQUIRED) +message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + +pybind11_add_module(test_cmake_build SHARED NO_EXTRAS ../main.cpp) + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/external/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt new file mode 100644 index 0000000..cd3ae6f --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.0) +project(test_installed_target CXX) + +set(CMAKE_MODULE_PATH "") + +find_package(pybind11 CONFIG REQUIRED) +message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + +add_library(test_cmake_build MODULE ../main.cpp) + +target_link_libraries(test_cmake_build PRIVATE pybind11::module) + +# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib +set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") + +# Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::module). +# This may be needed to resolve header conflicts, e.g. between Python release and debug headers. +set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/external/pybind11/tests/test_cmake_build/main.cpp b/external/pybind11/tests/test_cmake_build/main.cpp new file mode 100644 index 0000000..e30f2c4 --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/main.cpp @@ -0,0 +1,6 @@ +#include +namespace py = pybind11; + +PYBIND11_MODULE(test_cmake_build, m) { + m.def("add", [](int i, int j) { return i + j; }); +} diff --git a/external/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt new file mode 100644 index 0000000..88ba60d --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.0) +project(test_subdirectory_embed CXX) + +set(PYBIND11_INSTALL ON CACHE BOOL "") +set(PYBIND11_EXPORT_NAME test_export) + +add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) + +# Test basic target functionality +add_executable(test_cmake_build ../embed.cpp) +target_link_libraries(test_cmake_build PRIVATE pybind11::embed) + +add_custom_target(check $ ${PROJECT_SOURCE_DIR}/../test.py) + +# Test custom export group -- PYBIND11_EXPORT_NAME +add_library(test_embed_lib ../embed.cpp) +target_link_libraries(test_embed_lib PRIVATE pybind11::embed) + +install(TARGETS test_embed_lib + EXPORT test_export + ARCHIVE DESTINATION bin + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib) +install(EXPORT test_export + DESTINATION lib/cmake/test_export/test_export-Targets.cmake) diff --git a/external/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt new file mode 100644 index 0000000..278007a --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.8.12) +project(test_subdirectory_module CXX) + +add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) +pybind11_add_module(test_cmake_build THIN_LTO ../main.cpp) + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/external/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/external/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt new file mode 100644 index 0000000..6b142d6 --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.0) +project(test_subdirectory_target CXX) + +add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) + +add_library(test_cmake_build MODULE ../main.cpp) + +target_link_libraries(test_cmake_build PRIVATE pybind11::module) + +# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib +set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/external/pybind11/tests/test_cmake_build/test.py b/external/pybind11/tests/test_cmake_build/test.py new file mode 100644 index 0000000..1467a61 --- /dev/null +++ b/external/pybind11/tests/test_cmake_build/test.py @@ -0,0 +1,5 @@ +import sys +import test_cmake_build + +assert test_cmake_build.add(1, 2) == 3 +print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) diff --git a/external/pybind11/tests/test_constants_and_functions.cpp b/external/pybind11/tests/test_constants_and_functions.cpp new file mode 100644 index 0000000..8c9ef7f --- /dev/null +++ b/external/pybind11/tests/test_constants_and_functions.cpp @@ -0,0 +1,113 @@ +/* + tests/test_constants_and_functions.cpp -- global constants and functions, enumerations, raw byte strings + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +enum MyEnum { EFirstEntry = 1, ESecondEntry }; + +std::string test_function1() { + return "test_function()"; +} + +std::string test_function2(MyEnum k) { + return "test_function(enum=" + std::to_string(k) + ")"; +} + +std::string test_function3(int i) { + return "test_function(" + std::to_string(i) + ")"; +} + +py::str test_function4() { return "test_function()"; } +py::str test_function4(char *) { return "test_function(char *)"; } +py::str test_function4(int, float) { return "test_function(int, float)"; } +py::str test_function4(float, int) { return "test_function(float, int)"; } + +py::bytes return_bytes() { + const char *data = "\x01\x00\x02\x00"; + return std::string(data, 4); +} + +std::string print_bytes(py::bytes bytes) { + std::string ret = "bytes["; + const auto value = static_cast(bytes); + for (size_t i = 0; i < value.length(); ++i) { + ret += std::to_string(static_cast(value[i])) + " "; + } + ret.back() = ']'; + return ret; +} + +// Test that we properly handle C++17 exception specifiers (which are part of the function signature +// in C++17). These should all still work before C++17, but don't affect the function signature. +namespace test_exc_sp { +int f1(int x) noexcept { return x+1; } +int f2(int x) noexcept(true) { return x+2; } +int f3(int x) noexcept(false) { return x+3; } +int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true) +struct C { + int m1(int x) noexcept { return x-1; } + int m2(int x) const noexcept { return x-2; } + int m3(int x) noexcept(true) { return x-3; } + int m4(int x) const noexcept(true) { return x-4; } + int m5(int x) noexcept(false) { return x-5; } + int m6(int x) const noexcept(false) { return x-6; } + int m7(int x) throw() { return x-7; } + int m8(int x) const throw() { return x-8; } +}; +} + + +TEST_SUBMODULE(constants_and_functions, m) { + // test_constants + m.attr("some_constant") = py::int_(14); + + // test_function_overloading + m.def("test_function", &test_function1); + m.def("test_function", &test_function2); + m.def("test_function", &test_function3); + +#if defined(PYBIND11_OVERLOAD_CAST) + m.def("test_function", py::overload_cast<>(&test_function4)); + m.def("test_function", py::overload_cast(&test_function4)); + m.def("test_function", py::overload_cast(&test_function4)); + m.def("test_function", py::overload_cast(&test_function4)); +#else + m.def("test_function", static_cast(&test_function4)); + m.def("test_function", static_cast(&test_function4)); + m.def("test_function", static_cast(&test_function4)); + m.def("test_function", static_cast(&test_function4)); +#endif + + py::enum_(m, "MyEnum") + .value("EFirstEntry", EFirstEntry) + .value("ESecondEntry", ESecondEntry) + .export_values(); + + // test_bytes + m.def("return_bytes", &return_bytes); + m.def("print_bytes", &print_bytes); + + // test_exception_specifiers + using namespace test_exc_sp; + py::class_(m, "C") + .def(py::init<>()) + .def("m1", &C::m1) + .def("m2", &C::m2) + .def("m3", &C::m3) + .def("m4", &C::m4) + .def("m5", &C::m5) + .def("m6", &C::m6) + .def("m7", &C::m7) + .def("m8", &C::m8) + ; + m.def("f1", f1); + m.def("f2", f2); + m.def("f3", f3); + m.def("f4", f4); +} diff --git a/external/pybind11/tests/test_constants_and_functions.py b/external/pybind11/tests/test_constants_and_functions.py new file mode 100644 index 0000000..472682d --- /dev/null +++ b/external/pybind11/tests/test_constants_and_functions.py @@ -0,0 +1,39 @@ +from pybind11_tests import constants_and_functions as m + + +def test_constants(): + assert m.some_constant == 14 + + +def test_function_overloading(): + assert m.test_function() == "test_function()" + assert m.test_function(7) == "test_function(7)" + assert m.test_function(m.MyEnum.EFirstEntry) == "test_function(enum=1)" + assert m.test_function(m.MyEnum.ESecondEntry) == "test_function(enum=2)" + + assert m.test_function() == "test_function()" + assert m.test_function("abcd") == "test_function(char *)" + assert m.test_function(1, 1.0) == "test_function(int, float)" + assert m.test_function(1, 1.0) == "test_function(int, float)" + assert m.test_function(2.0, 2) == "test_function(float, int)" + + +def test_bytes(): + assert m.print_bytes(m.return_bytes()) == "bytes[1 0 2 0]" + + +def test_exception_specifiers(): + c = m.C() + assert c.m1(2) == 1 + assert c.m2(3) == 1 + assert c.m3(5) == 2 + assert c.m4(7) == 3 + assert c.m5(10) == 5 + assert c.m6(14) == 8 + assert c.m7(20) == 13 + assert c.m8(29) == 21 + + assert m.f1(33) == 34 + assert m.f2(53) == 55 + assert m.f3(86) == 89 + assert m.f4(140) == 144 diff --git a/external/pybind11/tests/test_copy_move.cpp b/external/pybind11/tests/test_copy_move.cpp new file mode 100644 index 0000000..94113e3 --- /dev/null +++ b/external/pybind11/tests/test_copy_move.cpp @@ -0,0 +1,213 @@ +/* + tests/test_copy_move_policies.cpp -- 'copy' and 'move' return value policies + and related tests + + Copyright (c) 2016 Ben North + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include + +template +struct empty { + static const derived& get_one() { return instance_; } + static derived instance_; +}; + +struct lacking_copy_ctor : public empty { + lacking_copy_ctor() {} + lacking_copy_ctor(const lacking_copy_ctor& other) = delete; +}; + +template <> lacking_copy_ctor empty::instance_ = {}; + +struct lacking_move_ctor : public empty { + lacking_move_ctor() {} + lacking_move_ctor(const lacking_move_ctor& other) = delete; + lacking_move_ctor(lacking_move_ctor&& other) = delete; +}; + +template <> lacking_move_ctor empty::instance_ = {}; + +/* Custom type caster move/copy test classes */ +class MoveOnlyInt { +public: + MoveOnlyInt() { print_default_created(this); } + MoveOnlyInt(int v) : value{std::move(v)} { print_created(this, value); } + MoveOnlyInt(MoveOnlyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); } + MoveOnlyInt &operator=(MoveOnlyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; } + MoveOnlyInt(const MoveOnlyInt &) = delete; + MoveOnlyInt &operator=(const MoveOnlyInt &) = delete; + ~MoveOnlyInt() { print_destroyed(this); } + + int value; +}; +class MoveOrCopyInt { +public: + MoveOrCopyInt() { print_default_created(this); } + MoveOrCopyInt(int v) : value{std::move(v)} { print_created(this, value); } + MoveOrCopyInt(MoveOrCopyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); } + MoveOrCopyInt &operator=(MoveOrCopyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; } + MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; } + MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } + ~MoveOrCopyInt() { print_destroyed(this); } + + int value; +}; +class CopyOnlyInt { +public: + CopyOnlyInt() { print_default_created(this); } + CopyOnlyInt(int v) : value{std::move(v)} { print_created(this, value); } + CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; } + CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } + ~CopyOnlyInt() { print_destroyed(this); } + + int value; +}; +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) +template <> struct type_caster { + PYBIND11_TYPE_CASTER(MoveOnlyInt, _("MoveOnlyInt")); + bool load(handle src, bool) { value = MoveOnlyInt(src.cast()); return true; } + static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } +}; + +template <> struct type_caster { + PYBIND11_TYPE_CASTER(MoveOrCopyInt, _("MoveOrCopyInt")); + bool load(handle src, bool) { value = MoveOrCopyInt(src.cast()); return true; } + static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } +}; + +template <> struct type_caster { +protected: + CopyOnlyInt value; +public: + static PYBIND11_DESCR name() { return _("CopyOnlyInt"); } + bool load(handle src, bool) { value = CopyOnlyInt(src.cast()); return true; } + static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } + static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) { + if (!src) return none().release(); + return cast(*src, policy, parent); + } + operator CopyOnlyInt*() { return &value; } + operator CopyOnlyInt&() { return value; } + template using cast_op_type = pybind11::detail::cast_op_type; +}; +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) + +TEST_SUBMODULE(copy_move_policies, m) { + // test_lacking_copy_ctor + py::class_(m, "lacking_copy_ctor") + .def_static("get_one", &lacking_copy_ctor::get_one, + py::return_value_policy::copy); + // test_lacking_move_ctor + py::class_(m, "lacking_move_ctor") + .def_static("get_one", &lacking_move_ctor::get_one, + py::return_value_policy::move); + + // test_move_and_copy_casts + m.def("move_and_copy_casts", [](py::object o) { + int r = 0; + r += py::cast(o).value; /* moves */ + r += py::cast(o).value; /* moves */ + r += py::cast(o).value; /* copies */ + MoveOrCopyInt m1(py::cast(o)); /* moves */ + MoveOnlyInt m2(py::cast(o)); /* moves */ + CopyOnlyInt m3(py::cast(o)); /* copies */ + r += m1.value + m2.value + m3.value; + + return r; + }); + + // test_move_and_copy_loads + m.def("move_only", [](MoveOnlyInt m) { return m.value; }); + m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; }); + m.def("copy_only", [](CopyOnlyInt m) { return m.value; }); + m.def("move_pair", [](std::pair p) { + return p.first.value + p.second.value; + }); + m.def("move_tuple", [](std::tuple t) { + return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value; + }); + m.def("copy_tuple", [](std::tuple t) { + return std::get<0>(t).value + std::get<1>(t).value; + }); + m.def("move_copy_nested", [](std::pair>, MoveOrCopyInt>> x) { + return x.first.value + std::get<0>(x.second.first).value + std::get<1>(x.second.first).value + + std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value; + }); + m.def("move_and_copy_cstats", []() { + ConstructorStats::gc(); + // Reset counts to 0 so that previous tests don't affect later ones: + auto &mc = ConstructorStats::get(); + mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions = 0; + auto &mo = ConstructorStats::get(); + mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions = 0; + auto &co = ConstructorStats::get(); + co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions = 0; + py::dict d; + d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference); + d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference); + d["CopyOnlyInt"] = py::cast(co, py::return_value_policy::reference); + return d; + }); +#ifdef PYBIND11_HAS_OPTIONAL + // test_move_and_copy_load_optional + m.attr("has_optional") = true; + m.def("move_optional", [](std::optional o) { + return o->value; + }); + m.def("move_or_copy_optional", [](std::optional o) { + return o->value; + }); + m.def("copy_optional", [](std::optional o) { + return o->value; + }); + m.def("move_optional_tuple", [](std::optional> x) { + return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value; + }); +#else + m.attr("has_optional") = false; +#endif + + // #70 compilation issue if operator new is not public + struct PrivateOpNew { + int value = 1; + private: +#if defined(_MSC_VER) +# pragma warning(disable: 4822) // warning C4822: local class member function does not have a body +#endif + void *operator new(size_t bytes); + }; + py::class_(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value); + m.def("private_op_new_value", []() { return PrivateOpNew(); }); + m.def("private_op_new_reference", []() -> const PrivateOpNew & { + static PrivateOpNew x{}; + return x; + }, py::return_value_policy::reference); + + // test_move_fallback + // #389: rvp::move should fall-through to copy on non-movable objects + struct MoveIssue1 { + int v; + MoveIssue1(int v) : v{v} {} + MoveIssue1(const MoveIssue1 &c) = default; + MoveIssue1(MoveIssue1 &&) = delete; + }; + py::class_(m, "MoveIssue1").def(py::init()).def_readwrite("value", &MoveIssue1::v); + + struct MoveIssue2 { + int v; + MoveIssue2(int v) : v{v} {} + MoveIssue2(MoveIssue2 &&) = default; + }; + py::class_(m, "MoveIssue2").def(py::init()).def_readwrite("value", &MoveIssue2::v); + + m.def("get_moveissue1", [](int i) { return new MoveIssue1(i); }, py::return_value_policy::move); + m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); +} diff --git a/external/pybind11/tests/test_copy_move.py b/external/pybind11/tests/test_copy_move.py new file mode 100644 index 0000000..aff2d99 --- /dev/null +++ b/external/pybind11/tests/test_copy_move.py @@ -0,0 +1,112 @@ +import pytest +from pybind11_tests import copy_move_policies as m + + +def test_lacking_copy_ctor(): + with pytest.raises(RuntimeError) as excinfo: + m.lacking_copy_ctor.get_one() + assert "the object is non-copyable!" in str(excinfo.value) + + +def test_lacking_move_ctor(): + with pytest.raises(RuntimeError) as excinfo: + m.lacking_move_ctor.get_one() + assert "the object is neither movable nor copyable!" in str(excinfo.value) + + +def test_move_and_copy_casts(): + """Cast some values in C++ via custom type casters and count the number of moves/copies.""" + + cstats = m.move_and_copy_cstats() + c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"] + + # The type move constructions/assignments below each get incremented: the move assignment comes + # from the type_caster load; the move construction happens when extracting that via a cast or + # loading into an argument. + assert m.move_and_copy_casts(3) == 18 + assert c_m.copy_assignments + c_m.copy_constructions == 0 + assert c_m.move_assignments == 2 + assert c_m.move_constructions >= 2 + assert c_mc.alive() == 0 + assert c_mc.copy_assignments + c_mc.copy_constructions == 0 + assert c_mc.move_assignments == 2 + assert c_mc.move_constructions >= 2 + assert c_c.alive() == 0 + assert c_c.copy_assignments == 2 + assert c_c.copy_constructions >= 2 + assert c_m.alive() + c_mc.alive() + c_c.alive() == 0 + + +def test_move_and_copy_loads(): + """Call some functions that load arguments via custom type casters and count the number of + moves/copies.""" + + cstats = m.move_and_copy_cstats() + c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"] + + assert m.move_only(10) == 10 # 1 move, c_m + assert m.move_or_copy(11) == 11 # 1 move, c_mc + assert m.copy_only(12) == 12 # 1 copy, c_c + assert m.move_pair((13, 14)) == 27 # 1 c_m move, 1 c_mc move + assert m.move_tuple((15, 16, 17)) == 48 # 2 c_m moves, 1 c_mc move + assert m.copy_tuple((18, 19)) == 37 # 2 c_c copies + # Direct constructions: 2 c_m moves, 2 c_mc moves, 1 c_c copy + # Extra moves/copies when moving pairs/tuples: 3 c_m, 3 c_mc, 2 c_c + assert m.move_copy_nested((1, ((2, 3, (4,)), 5))) == 15 + + assert c_m.copy_assignments + c_m.copy_constructions == 0 + assert c_m.move_assignments == 6 + assert c_m.move_constructions == 9 + assert c_mc.copy_assignments + c_mc.copy_constructions == 0 + assert c_mc.move_assignments == 5 + assert c_mc.move_constructions == 8 + assert c_c.copy_assignments == 4 + assert c_c.copy_constructions == 6 + assert c_m.alive() + c_mc.alive() + c_c.alive() == 0 + + +@pytest.mark.skipif(not m.has_optional, reason='no ') +def test_move_and_copy_load_optional(): + """Tests move/copy loads of std::optional arguments""" + + cstats = m.move_and_copy_cstats() + c_m, c_mc, c_c = cstats["MoveOnlyInt"], cstats["MoveOrCopyInt"], cstats["CopyOnlyInt"] + + # The extra move/copy constructions below come from the std::optional move (which has to move + # its arguments): + assert m.move_optional(10) == 10 # c_m: 1 move assign, 2 move construct + assert m.move_or_copy_optional(11) == 11 # c_mc: 1 move assign, 2 move construct + assert m.copy_optional(12) == 12 # c_c: 1 copy assign, 2 copy construct + # 1 move assign + move construct moves each of c_m, c_mc, 1 c_c copy + # +1 move/copy construct each from moving the tuple + # +1 move/copy construct each from moving the optional (which moves the tuple again) + assert m.move_optional_tuple((3, 4, 5)) == 12 + + assert c_m.copy_assignments + c_m.copy_constructions == 0 + assert c_m.move_assignments == 2 + assert c_m.move_constructions == 5 + assert c_mc.copy_assignments + c_mc.copy_constructions == 0 + assert c_mc.move_assignments == 2 + assert c_mc.move_constructions == 5 + assert c_c.copy_assignments == 2 + assert c_c.copy_constructions == 5 + assert c_m.alive() + c_mc.alive() + c_c.alive() == 0 + + +def test_private_op_new(): + """An object with a private `operator new` cannot be returned by value""" + + with pytest.raises(RuntimeError) as excinfo: + m.private_op_new_value() + assert "the object is neither movable nor copyable" in str(excinfo.value) + + assert m.private_op_new_reference().value == 1 + + +def test_move_fallback(): + """#389: rvp::move should fall-through to copy on non-movable objects""" + + m2 = m.get_moveissue2(2) + assert m2.value == 2 + m1 = m.get_moveissue1(1) + assert m1.value == 1 diff --git a/external/pybind11/tests/test_docstring_options.cpp b/external/pybind11/tests/test_docstring_options.cpp new file mode 100644 index 0000000..8c8f79f --- /dev/null +++ b/external/pybind11/tests/test_docstring_options.cpp @@ -0,0 +1,61 @@ +/* + tests/test_docstring_options.cpp -- generation of docstrings and signatures + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +TEST_SUBMODULE(docstring_options, m) { + // test_docstring_options + { + py::options options; + options.disable_function_signatures(); + + m.def("test_function1", [](int, int) {}, py::arg("a"), py::arg("b")); + m.def("test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + + m.def("test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring"); + m.def("test_overloaded1", [](double) {}, py::arg("d")); + + m.def("test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1"); + m.def("test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2"); + + m.def("test_overloaded3", [](int) {}, py::arg("i")); + m.def("test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr"); + + options.enable_function_signatures(); + + m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b")); + m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + + options.disable_function_signatures().disable_user_defined_docstrings(); + + m.def("test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + + { + py::options nested_options; + nested_options.enable_user_defined_docstrings(); + m.def("test_function6", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + } + } + + m.def("test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); + + { + py::options options; + options.disable_user_defined_docstrings(); + + struct DocstringTestFoo { + int value; + void setValue(int v) { value = v; } + int getValue() const { return value; } + }; + py::class_(m, "DocstringTestFoo", "This is a class docstring") + .def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring") + ; + } +} diff --git a/external/pybind11/tests/test_docstring_options.py b/external/pybind11/tests/test_docstring_options.py new file mode 100644 index 0000000..0dbca60 --- /dev/null +++ b/external/pybind11/tests/test_docstring_options.py @@ -0,0 +1,38 @@ +from pybind11_tests import docstring_options as m + + +def test_docstring_options(): + # options.disable_function_signatures() + assert not m.test_function1.__doc__ + + assert m.test_function2.__doc__ == "A custom docstring" + + # docstring specified on just the first overload definition: + assert m.test_overloaded1.__doc__ == "Overload docstring" + + # docstring on both overloads: + assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2" + + # docstring on only second overload: + assert m.test_overloaded3.__doc__ == "Overload docstr" + + # options.enable_function_signatures() + assert m.test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None") + + assert m.test_function4.__doc__ .startswith("test_function4(a: int, b: int) -> None") + assert m.test_function4.__doc__ .endswith("A custom docstring\n") + + # options.disable_function_signatures() + # options.disable_user_defined_docstrings() + assert not m.test_function5.__doc__ + + # nested options.enable_user_defined_docstrings() + assert m.test_function6.__doc__ == "A custom docstring" + + # RAII destructor + assert m.test_function7.__doc__ .startswith("test_function7(a: int, b: int) -> None") + assert m.test_function7.__doc__ .endswith("A custom docstring\n") + + # Suppression of user-defined docstrings for non-function objects + assert not m.DocstringTestFoo.__doc__ + assert not m.DocstringTestFoo.value_prop.__doc__ diff --git a/external/pybind11/tests/test_eigen.cpp b/external/pybind11/tests/test_eigen.cpp new file mode 100644 index 0000000..17b156c --- /dev/null +++ b/external/pybind11/tests/test_eigen.cpp @@ -0,0 +1,317 @@ +/* + tests/eigen.cpp -- automatic conversion of Eigen types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include +#include +#include + +using MatrixXdR = Eigen::Matrix; + + + +// Sets/resets a testing reference matrix to have values of 10*r + c, where r and c are the +// (1-based) row/column number. +template void reset_ref(M &x) { + for (int i = 0; i < x.rows(); i++) for (int j = 0; j < x.cols(); j++) + x(i, j) = 11 + 10*i + j; +} + +// Returns a static, column-major matrix +Eigen::MatrixXd &get_cm() { + static Eigen::MatrixXd *x; + if (!x) { + x = new Eigen::MatrixXd(3, 3); + reset_ref(*x); + } + return *x; +} +// Likewise, but row-major +MatrixXdR &get_rm() { + static MatrixXdR *x; + if (!x) { + x = new MatrixXdR(3, 3); + reset_ref(*x); + } + return *x; +} +// Resets the values of the static matrices returned by get_cm()/get_rm() +void reset_refs() { + reset_ref(get_cm()); + reset_ref(get_rm()); +} + +// Returns element 2,1 from a matrix (used to test copy/nocopy) +double get_elem(Eigen::Ref m) { return m(2, 1); }; + + +// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix +// reference is referencing rows/columns correctly). +template Eigen::MatrixXd adjust_matrix(MatrixArgType m) { + Eigen::MatrixXd ret(m); + for (int c = 0; c < m.cols(); c++) for (int r = 0; r < m.rows(); r++) + ret(r, c) += 10*r + 100*c; + return ret; +} + +struct CustomOperatorNew { + CustomOperatorNew() = default; + + Eigen::Matrix4d a = Eigen::Matrix4d::Zero(); + Eigen::Matrix4d b = Eigen::Matrix4d::Identity(); + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW; +}; + +TEST_SUBMODULE(eigen, m) { + using FixedMatrixR = Eigen::Matrix; + using FixedMatrixC = Eigen::Matrix; + using DenseMatrixR = Eigen::Matrix; + using DenseMatrixC = Eigen::Matrix; + using FourRowMatrixC = Eigen::Matrix; + using FourColMatrixC = Eigen::Matrix; + using FourRowMatrixR = Eigen::Matrix; + using FourColMatrixR = Eigen::Matrix; + using SparseMatrixR = Eigen::SparseMatrix; + using SparseMatrixC = Eigen::SparseMatrix; + + m.attr("have_eigen") = true; + + // various tests + m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; }); + m.def("double_row", [](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; }); + m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; }); + m.def("double_threec", [](py::EigenDRef x) { x *= 2; }); + m.def("double_threer", [](py::EigenDRef x) { x *= 2; }); + m.def("double_mat_cm", [](Eigen::MatrixXf x) -> Eigen::MatrixXf { return 2.0f * x; }); + m.def("double_mat_rm", [](DenseMatrixR x) -> DenseMatrixR { return 2.0f * x; }); + + // test_eigen_ref_to_python + // Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended + m.def("cholesky1", [](Eigen::Ref x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + m.def("cholesky2", [](const Eigen::Ref &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + m.def("cholesky3", [](const Eigen::Ref &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + m.def("cholesky4", [](Eigen::Ref x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); + + // test_eigen_ref_mutators + // Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into + // the numpy array data and so the result should show up there. There are three versions: one that + // works on a contiguous-row matrix (numpy's default), one for a contiguous-column matrix, and one + // for any matrix. + auto add_rm = [](Eigen::Ref x, int r, int c, double v) { x(r,c) += v; }; + auto add_cm = [](Eigen::Ref x, int r, int c, double v) { x(r,c) += v; }; + + // Mutators (Eigen maps into numpy variables): + m.def("add_rm", add_rm); // Only takes row-contiguous + m.def("add_cm", add_cm); // Only takes column-contiguous + // Overloaded versions that will accept either row or column contiguous: + m.def("add1", add_rm); + m.def("add1", add_cm); + m.def("add2", add_cm); + m.def("add2", add_rm); + // This one accepts a matrix of any stride: + m.def("add_any", [](py::EigenDRef x, int r, int c, double v) { x(r,c) += v; }); + + // Return mutable references (numpy maps into eigen varibles) + m.def("get_cm_ref", []() { return Eigen::Ref(get_cm()); }); + m.def("get_rm_ref", []() { return Eigen::Ref(get_rm()); }); + // The same references, but non-mutable (numpy maps into eigen variables, but is !writeable) + m.def("get_cm_const_ref", []() { return Eigen::Ref(get_cm()); }); + m.def("get_rm_const_ref", []() { return Eigen::Ref(get_rm()); }); + + m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values + + // Increments and returns ref to (same) matrix + m.def("incr_matrix", [](Eigen::Ref m, double v) { + m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); + return m; + }, py::return_value_policy::reference); + + // Same, but accepts a matrix of any strides + m.def("incr_matrix_any", [](py::EigenDRef m, double v) { + m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); + return m; + }, py::return_value_policy::reference); + + // Returns an eigen slice of even rows + m.def("even_rows", [](py::EigenDRef m) { + return py::EigenDMap( + m.data(), (m.rows() + 1) / 2, m.cols(), + py::EigenDStride(m.outerStride(), 2 * m.innerStride())); + }, py::return_value_policy::reference); + + // Returns an eigen slice of even columns + m.def("even_cols", [](py::EigenDRef m) { + return py::EigenDMap( + m.data(), m.rows(), (m.cols() + 1) / 2, + py::EigenDStride(2 * m.outerStride(), m.innerStride())); + }, py::return_value_policy::reference); + + // Returns diagonals: a vector-like object with an inner stride != 1 + m.def("diagonal", [](const Eigen::Ref &x) { return x.diagonal(); }); + m.def("diagonal_1", [](const Eigen::Ref &x) { return x.diagonal<1>(); }); + m.def("diagonal_n", [](const Eigen::Ref &x, int index) { return x.diagonal(index); }); + + // Return a block of a matrix (gives non-standard strides) + m.def("block", [](const Eigen::Ref &x, int start_row, int start_col, int block_rows, int block_cols) { + return x.block(start_row, start_col, block_rows, block_cols); + }); + + // test_eigen_return_references, test_eigen_keepalive + // return value referencing/copying tests: + class ReturnTester { + Eigen::MatrixXd mat = create(); + public: + ReturnTester() { print_created(this); } + ~ReturnTester() { print_destroyed(this); } + static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); } + static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); } + Eigen::MatrixXd &get() { return mat; } + Eigen::MatrixXd *getPtr() { return &mat; } + const Eigen::MatrixXd &view() { return mat; } + const Eigen::MatrixXd *viewPtr() { return &mat; } + Eigen::Ref ref() { return mat; } + Eigen::Ref refConst() { return mat; } + Eigen::Block block(int r, int c, int nrow, int ncol) { return mat.block(r, c, nrow, ncol); } + Eigen::Block blockConst(int r, int c, int nrow, int ncol) const { return mat.block(r, c, nrow, ncol); } + py::EigenDMap corners() { return py::EigenDMap(mat.data(), + py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); } + py::EigenDMap cornersConst() const { return py::EigenDMap(mat.data(), + py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); } + }; + using rvp = py::return_value_policy; + py::class_(m, "ReturnTester") + .def(py::init<>()) + .def_static("create", &ReturnTester::create) + .def_static("create_const", &ReturnTester::createConst) + .def("get", &ReturnTester::get, rvp::reference_internal) + .def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal) + .def("view", &ReturnTester::view, rvp::reference_internal) + .def("view_ptr", &ReturnTester::view, rvp::reference_internal) + .def("copy_get", &ReturnTester::get) // Default rvp: copy + .def("copy_view", &ReturnTester::view) // " + .def("ref", &ReturnTester::ref) // Default for Ref is to reference + .def("ref_const", &ReturnTester::refConst) // Likewise, but const + .def("ref_safe", &ReturnTester::ref, rvp::reference_internal) + .def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal) + .def("copy_ref", &ReturnTester::ref, rvp::copy) + .def("copy_ref_const", &ReturnTester::refConst, rvp::copy) + .def("block", &ReturnTester::block) + .def("block_safe", &ReturnTester::block, rvp::reference_internal) + .def("block_const", &ReturnTester::blockConst, rvp::reference_internal) + .def("copy_block", &ReturnTester::block, rvp::copy) + .def("corners", &ReturnTester::corners, rvp::reference_internal) + .def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal) + ; + + // test_special_matrix_objects + // Returns a DiagonalMatrix with diagonal (1,2,3,...) + m.def("incr_diag", [](int k) { + Eigen::DiagonalMatrix m(k); + for (int i = 0; i < k; i++) m.diagonal()[i] = i+1; + return m; + }); + + // Returns a SelfAdjointView referencing the lower triangle of m + m.def("symmetric_lower", [](const Eigen::MatrixXi &m) { + return m.selfadjointView(); + }); + // Returns a SelfAdjointView referencing the lower triangle of m + m.def("symmetric_upper", [](const Eigen::MatrixXi &m) { + return m.selfadjointView(); + }); + + // Test matrix for various functions below. + Eigen::MatrixXf mat(5, 6); + mat << 0, 3, 0, 0, 0, 11, + 22, 0, 0, 0, 17, 11, + 7, 5, 0, 1, 0, 11, + 0, 0, 0, 0, 0, 11, + 0, 0, 14, 0, 8, 11; + + // test_fixed, and various other tests + m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); }); + m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); }); + m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); }); + m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; }); + m.def("fixed_copy_c", [](const FixedMatrixC &m) -> FixedMatrixC { return m; }); + // test_mutator_descriptors + m.def("fixed_mutator_r", [](Eigen::Ref) {}); + m.def("fixed_mutator_c", [](Eigen::Ref) {}); + m.def("fixed_mutator_a", [](py::EigenDRef) {}); + // test_dense + m.def("dense_r", [mat]() -> DenseMatrixR { return DenseMatrixR(mat); }); + m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); }); + m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; }); + m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; }); + // test_sparse, test_sparse_signature + m.def("sparse_r", [mat]() -> SparseMatrixR { return Eigen::SparseView(mat); }); + m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView(mat); }); + m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; }); + m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); + // test_partially_fixed + m.def("partial_copy_four_rm_r", [](const FourRowMatrixR &m) -> FourRowMatrixR { return m; }); + m.def("partial_copy_four_rm_c", [](const FourColMatrixR &m) -> FourColMatrixR { return m; }); + m.def("partial_copy_four_cm_r", [](const FourRowMatrixC &m) -> FourRowMatrixC { return m; }); + m.def("partial_copy_four_cm_c", [](const FourColMatrixC &m) -> FourColMatrixC { return m; }); + + // test_cpp_casting + // Test that we can cast a numpy object to a Eigen::MatrixXd explicitly + m.def("cpp_copy", [](py::handle m) { return m.cast()(1, 0); }); + m.def("cpp_ref_c", [](py::handle m) { return m.cast>()(1, 0); }); + m.def("cpp_ref_r", [](py::handle m) { return m.cast>()(1, 0); }); + m.def("cpp_ref_any", [](py::handle m) { return m.cast>()(1, 0); }); + + + // test_nocopy_wrapper + // Test that we can prevent copying into an argument that would normally copy: First a version + // that would allow copying (if types or strides don't match) for comparison: + m.def("get_elem", &get_elem); + // Now this alternative that calls the tells pybind to fail rather than copy: + m.def("get_elem_nocopy", [](Eigen::Ref m) -> double { return get_elem(m); }, + py::arg().noconvert()); + // Also test a row-major-only no-copy const ref: + m.def("get_elem_rm_nocopy", [](Eigen::Ref> &m) -> long { return m(2, 1); }, + py::arg().noconvert()); + + // test_issue738 + // Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an + // incompatible stride value on the length-1 dimension--but that should be allowed (without + // requiring a copy!) because the stride value can be safely ignored on a size-1 dimension. + m.def("iss738_f1", &adjust_matrix &>, py::arg().noconvert()); + m.def("iss738_f2", &adjust_matrix> &>, py::arg().noconvert()); + + // test_named_arguments + // Make sure named arguments are working properly: + m.def("matrix_multiply", [](const py::EigenDRef A, const py::EigenDRef B) + -> Eigen::MatrixXd { + if (A.cols() != B.rows()) throw std::domain_error("Nonconformable matrices!"); + return A * B; + }, py::arg("A"), py::arg("B")); + + // test_custom_operator_new + py::class_(m, "CustomOperatorNew") + .def(py::init<>()) + .def_readonly("a", &CustomOperatorNew::a) + .def_readonly("b", &CustomOperatorNew::b); + + // test_eigen_ref_life_support + // In case of a failure (the caster's temp array does not live long enough), creating + // a new array (np.ones(10)) increases the chances that the temp array will be garbage + // collected and/or that its memory will be overridden with different values. + m.def("get_elem_direct", [](Eigen::Ref v) { + py::module::import("numpy").attr("ones")(10); + return v(5); + }); + m.def("get_elem_indirect", [](std::vector> v) { + py::module::import("numpy").attr("ones")(10); + return v[0](5); + }); +} diff --git a/external/pybind11/tests/test_eigen.py b/external/pybind11/tests/test_eigen.py new file mode 100644 index 0000000..4ac8cbf --- /dev/null +++ b/external/pybind11/tests/test_eigen.py @@ -0,0 +1,681 @@ +import pytest +from pybind11_tests import ConstructorStats + +pytestmark = pytest.requires_eigen_and_numpy + +with pytest.suppress(ImportError): + from pybind11_tests import eigen as m + import numpy as np + + ref = np.array([[ 0., 3, 0, 0, 0, 11], + [22, 0, 0, 0, 17, 11], + [ 7, 5, 0, 1, 0, 11], + [ 0, 0, 0, 0, 0, 11], + [ 0, 0, 14, 0, 8, 11]]) + + +def assert_equal_ref(mat): + np.testing.assert_array_equal(mat, ref) + + +def assert_sparse_equal_ref(sparse_mat): + assert_equal_ref(sparse_mat.todense()) + + +def test_fixed(): + assert_equal_ref(m.fixed_c()) + assert_equal_ref(m.fixed_r()) + assert_equal_ref(m.fixed_copy_r(m.fixed_r())) + assert_equal_ref(m.fixed_copy_c(m.fixed_c())) + assert_equal_ref(m.fixed_copy_r(m.fixed_c())) + assert_equal_ref(m.fixed_copy_c(m.fixed_r())) + + +def test_dense(): + assert_equal_ref(m.dense_r()) + assert_equal_ref(m.dense_c()) + assert_equal_ref(m.dense_copy_r(m.dense_r())) + assert_equal_ref(m.dense_copy_c(m.dense_c())) + assert_equal_ref(m.dense_copy_r(m.dense_c())) + assert_equal_ref(m.dense_copy_c(m.dense_r())) + + +def test_partially_fixed(): + ref2 = np.array([[0., 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]) + np.testing.assert_array_equal(m.partial_copy_four_rm_r(ref2), ref2) + np.testing.assert_array_equal(m.partial_copy_four_rm_c(ref2), ref2) + np.testing.assert_array_equal(m.partial_copy_four_rm_r(ref2[:, 1]), ref2[:, [1]]) + np.testing.assert_array_equal(m.partial_copy_four_rm_c(ref2[0, :]), ref2[[0], :]) + np.testing.assert_array_equal(m.partial_copy_four_rm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)]) + np.testing.assert_array_equal( + m.partial_copy_four_rm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :]) + + np.testing.assert_array_equal(m.partial_copy_four_cm_r(ref2), ref2) + np.testing.assert_array_equal(m.partial_copy_four_cm_c(ref2), ref2) + np.testing.assert_array_equal(m.partial_copy_four_cm_r(ref2[:, 1]), ref2[:, [1]]) + np.testing.assert_array_equal(m.partial_copy_four_cm_c(ref2[0, :]), ref2[[0], :]) + np.testing.assert_array_equal(m.partial_copy_four_cm_r(ref2[:, (0, 2)]), ref2[:, (0, 2)]) + np.testing.assert_array_equal( + m.partial_copy_four_cm_c(ref2[(3, 1, 2), :]), ref2[(3, 1, 2), :]) + + # TypeError should be raise for a shape mismatch + functions = [m.partial_copy_four_rm_r, m.partial_copy_four_rm_c, + m.partial_copy_four_cm_r, m.partial_copy_four_cm_c] + matrix_with_wrong_shape = [[1, 2], + [3, 4]] + for f in functions: + with pytest.raises(TypeError) as excinfo: + f(matrix_with_wrong_shape) + assert "incompatible function arguments" in str(excinfo.value) + + +def test_mutator_descriptors(): + zr = np.arange(30, dtype='float32').reshape(5, 6) # row-major + zc = zr.reshape(6, 5).transpose() # column-major + + m.fixed_mutator_r(zr) + m.fixed_mutator_c(zc) + m.fixed_mutator_a(zr) + m.fixed_mutator_a(zc) + with pytest.raises(TypeError) as excinfo: + m.fixed_mutator_r(zc) + assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable, flags.c_contiguous]) -> None' + in str(excinfo.value)) + with pytest.raises(TypeError) as excinfo: + m.fixed_mutator_c(zr) + assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable, flags.f_contiguous]) -> None' + in str(excinfo.value)) + with pytest.raises(TypeError) as excinfo: + m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype='float32')) + assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable]) -> None' + in str(excinfo.value)) + zr.flags.writeable = False + with pytest.raises(TypeError): + m.fixed_mutator_r(zr) + with pytest.raises(TypeError): + m.fixed_mutator_a(zr) + + +def test_cpp_casting(): + assert m.cpp_copy(m.fixed_r()) == 22. + assert m.cpp_copy(m.fixed_c()) == 22. + z = np.array([[5., 6], [7, 8]]) + assert m.cpp_copy(z) == 7. + assert m.cpp_copy(m.get_cm_ref()) == 21. + assert m.cpp_copy(m.get_rm_ref()) == 21. + assert m.cpp_ref_c(m.get_cm_ref()) == 21. + assert m.cpp_ref_r(m.get_rm_ref()) == 21. + with pytest.raises(RuntimeError) as excinfo: + # Can't reference m.fixed_c: it contains floats, m.cpp_ref_any wants doubles + m.cpp_ref_any(m.fixed_c()) + assert 'Unable to cast Python instance' in str(excinfo.value) + with pytest.raises(RuntimeError) as excinfo: + # Can't reference m.fixed_r: it contains floats, m.cpp_ref_any wants doubles + m.cpp_ref_any(m.fixed_r()) + assert 'Unable to cast Python instance' in str(excinfo.value) + assert m.cpp_ref_any(m.ReturnTester.create()) == 1. + + assert m.cpp_ref_any(m.get_cm_ref()) == 21. + assert m.cpp_ref_any(m.get_cm_ref()) == 21. + + +def test_pass_readonly_array(): + z = np.full((5, 6), 42.0) + z.flags.writeable = False + np.testing.assert_array_equal(z, m.fixed_copy_r(z)) + np.testing.assert_array_equal(m.fixed_r_const(), m.fixed_r()) + assert not m.fixed_r_const().flags.writeable + np.testing.assert_array_equal(m.fixed_copy_r(m.fixed_r_const()), m.fixed_r_const()) + + +def test_nonunit_stride_from_python(): + counting_mat = np.arange(9.0, dtype=np.float32).reshape((3, 3)) + second_row = counting_mat[1, :] + second_col = counting_mat[:, 1] + np.testing.assert_array_equal(m.double_row(second_row), 2.0 * second_row) + np.testing.assert_array_equal(m.double_col(second_row), 2.0 * second_row) + np.testing.assert_array_equal(m.double_complex(second_row), 2.0 * second_row) + np.testing.assert_array_equal(m.double_row(second_col), 2.0 * second_col) + np.testing.assert_array_equal(m.double_col(second_col), 2.0 * second_col) + np.testing.assert_array_equal(m.double_complex(second_col), 2.0 * second_col) + + counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) + slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] + for slice_idx, ref_mat in enumerate(slices): + np.testing.assert_array_equal(m.double_mat_cm(ref_mat), 2.0 * ref_mat) + np.testing.assert_array_equal(m.double_mat_rm(ref_mat), 2.0 * ref_mat) + + # Mutator: + m.double_threer(second_row) + m.double_threec(second_col) + np.testing.assert_array_equal(counting_mat, [[0., 2, 2], [6, 16, 10], [6, 14, 8]]) + + +def test_negative_stride_from_python(msg): + """Eigen doesn't support (as of yet) negative strides. When a function takes an Eigen matrix by + copy or const reference, we can pass a numpy array that has negative strides. Otherwise, an + exception will be thrown as Eigen will not be able to map the numpy array.""" + + counting_mat = np.arange(9.0, dtype=np.float32).reshape((3, 3)) + counting_mat = counting_mat[::-1, ::-1] + second_row = counting_mat[1, :] + second_col = counting_mat[:, 1] + np.testing.assert_array_equal(m.double_row(second_row), 2.0 * second_row) + np.testing.assert_array_equal(m.double_col(second_row), 2.0 * second_row) + np.testing.assert_array_equal(m.double_complex(second_row), 2.0 * second_row) + np.testing.assert_array_equal(m.double_row(second_col), 2.0 * second_col) + np.testing.assert_array_equal(m.double_col(second_col), 2.0 * second_col) + np.testing.assert_array_equal(m.double_complex(second_col), 2.0 * second_col) + + counting_3d = np.arange(27.0, dtype=np.float32).reshape((3, 3, 3)) + counting_3d = counting_3d[::-1, ::-1, ::-1] + slices = [counting_3d[0, :, :], counting_3d[:, 0, :], counting_3d[:, :, 0]] + for slice_idx, ref_mat in enumerate(slices): + np.testing.assert_array_equal(m.double_mat_cm(ref_mat), 2.0 * ref_mat) + np.testing.assert_array_equal(m.double_mat_rm(ref_mat), 2.0 * ref_mat) + + # Mutator: + with pytest.raises(TypeError) as excinfo: + m.double_threer(second_row) + assert msg(excinfo.value) == """ + double_threer(): incompatible function arguments. The following argument types are supported: + 1. (arg0: numpy.ndarray[float32[1, 3], flags.writeable]) -> None + + Invoked with: array([ 5., 4., 3.], dtype=float32) + """ # noqa: E501 line too long + + with pytest.raises(TypeError) as excinfo: + m.double_threec(second_col) + assert msg(excinfo.value) == """ + double_threec(): incompatible function arguments. The following argument types are supported: + 1. (arg0: numpy.ndarray[float32[3, 1], flags.writeable]) -> None + + Invoked with: array([ 7., 4., 1.], dtype=float32) + """ # noqa: E501 line too long + + +def test_nonunit_stride_to_python(): + assert np.all(m.diagonal(ref) == ref.diagonal()) + assert np.all(m.diagonal_1(ref) == ref.diagonal(1)) + for i in range(-5, 7): + assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), "m.diagonal_n({})".format(i) + + assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4]) + assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:]) + assert np.all(m.block(ref, 1, 4, 3, 2) == ref[1:4, 4:]) + + +def test_eigen_ref_to_python(): + chols = [m.cholesky1, m.cholesky2, m.cholesky3, m.cholesky4] + for i, chol in enumerate(chols, start=1): + mymat = chol(np.array([[1., 2, 4], [2, 13, 23], [4, 23, 77]])) + assert np.all(mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]])), "cholesky{}".format(i) + + +def assign_both(a1, a2, r, c, v): + a1[r, c] = v + a2[r, c] = v + + +def array_copy_but_one(a, r, c, v): + z = np.array(a, copy=True) + z[r, c] = v + return z + + +def test_eigen_return_references(): + """Tests various ways of returning references and non-referencing copies""" + + master = np.ones((10, 10)) + a = m.ReturnTester() + a_get1 = a.get() + assert not a_get1.flags.owndata and a_get1.flags.writeable + assign_both(a_get1, master, 3, 3, 5) + a_get2 = a.get_ptr() + assert not a_get2.flags.owndata and a_get2.flags.writeable + assign_both(a_get1, master, 2, 3, 6) + + a_view1 = a.view() + assert not a_view1.flags.owndata and not a_view1.flags.writeable + with pytest.raises(ValueError): + a_view1[2, 3] = 4 + a_view2 = a.view_ptr() + assert not a_view2.flags.owndata and not a_view2.flags.writeable + with pytest.raises(ValueError): + a_view2[2, 3] = 4 + + a_copy1 = a.copy_get() + assert a_copy1.flags.owndata and a_copy1.flags.writeable + np.testing.assert_array_equal(a_copy1, master) + a_copy1[7, 7] = -44 # Shouldn't affect anything else + c1want = array_copy_but_one(master, 7, 7, -44) + a_copy2 = a.copy_view() + assert a_copy2.flags.owndata and a_copy2.flags.writeable + np.testing.assert_array_equal(a_copy2, master) + a_copy2[4, 4] = -22 # Shouldn't affect anything else + c2want = array_copy_but_one(master, 4, 4, -22) + + a_ref1 = a.ref() + assert not a_ref1.flags.owndata and a_ref1.flags.writeable + assign_both(a_ref1, master, 1, 1, 15) + a_ref2 = a.ref_const() + assert not a_ref2.flags.owndata and not a_ref2.flags.writeable + with pytest.raises(ValueError): + a_ref2[5, 5] = 33 + a_ref3 = a.ref_safe() + assert not a_ref3.flags.owndata and a_ref3.flags.writeable + assign_both(a_ref3, master, 0, 7, 99) + a_ref4 = a.ref_const_safe() + assert not a_ref4.flags.owndata and not a_ref4.flags.writeable + with pytest.raises(ValueError): + a_ref4[7, 0] = 987654321 + + a_copy3 = a.copy_ref() + assert a_copy3.flags.owndata and a_copy3.flags.writeable + np.testing.assert_array_equal(a_copy3, master) + a_copy3[8, 1] = 11 + c3want = array_copy_but_one(master, 8, 1, 11) + a_copy4 = a.copy_ref_const() + assert a_copy4.flags.owndata and a_copy4.flags.writeable + np.testing.assert_array_equal(a_copy4, master) + a_copy4[8, 4] = 88 + c4want = array_copy_but_one(master, 8, 4, 88) + + a_block1 = a.block(3, 3, 2, 2) + assert not a_block1.flags.owndata and a_block1.flags.writeable + a_block1[0, 0] = 55 + master[3, 3] = 55 + a_block2 = a.block_safe(2, 2, 3, 2) + assert not a_block2.flags.owndata and a_block2.flags.writeable + a_block2[2, 1] = -123 + master[4, 3] = -123 + a_block3 = a.block_const(6, 7, 4, 3) + assert not a_block3.flags.owndata and not a_block3.flags.writeable + with pytest.raises(ValueError): + a_block3[2, 2] = -44444 + + a_copy5 = a.copy_block(2, 2, 2, 3) + assert a_copy5.flags.owndata and a_copy5.flags.writeable + np.testing.assert_array_equal(a_copy5, master[2:4, 2:5]) + a_copy5[1, 1] = 777 + c5want = array_copy_but_one(master[2:4, 2:5], 1, 1, 777) + + a_corn1 = a.corners() + assert not a_corn1.flags.owndata and a_corn1.flags.writeable + a_corn1 *= 50 + a_corn1[1, 1] = 999 + master[0, 0] = 50 + master[0, 9] = 50 + master[9, 0] = 50 + master[9, 9] = 999 + a_corn2 = a.corners_const() + assert not a_corn2.flags.owndata and not a_corn2.flags.writeable + with pytest.raises(ValueError): + a_corn2[1, 0] = 51 + + # All of the changes made all the way along should be visible everywhere + # now (except for the copies, of course) + np.testing.assert_array_equal(a_get1, master) + np.testing.assert_array_equal(a_get2, master) + np.testing.assert_array_equal(a_view1, master) + np.testing.assert_array_equal(a_view2, master) + np.testing.assert_array_equal(a_ref1, master) + np.testing.assert_array_equal(a_ref2, master) + np.testing.assert_array_equal(a_ref3, master) + np.testing.assert_array_equal(a_ref4, master) + np.testing.assert_array_equal(a_block1, master[3:5, 3:5]) + np.testing.assert_array_equal(a_block2, master[2:5, 2:4]) + np.testing.assert_array_equal(a_block3, master[6:10, 7:10]) + np.testing.assert_array_equal(a_corn1, master[0::master.shape[0] - 1, 0::master.shape[1] - 1]) + np.testing.assert_array_equal(a_corn2, master[0::master.shape[0] - 1, 0::master.shape[1] - 1]) + + np.testing.assert_array_equal(a_copy1, c1want) + np.testing.assert_array_equal(a_copy2, c2want) + np.testing.assert_array_equal(a_copy3, c3want) + np.testing.assert_array_equal(a_copy4, c4want) + np.testing.assert_array_equal(a_copy5, c5want) + + +def assert_keeps_alive(cl, method, *args): + cstats = ConstructorStats.get(cl) + start_with = cstats.alive() + a = cl() + assert cstats.alive() == start_with + 1 + z = method(a, *args) + assert cstats.alive() == start_with + 1 + del a + # Here's the keep alive in action: + assert cstats.alive() == start_with + 1 + del z + # Keep alive should have expired: + assert cstats.alive() == start_with + + +def test_eigen_keepalive(): + a = m.ReturnTester() + cstats = ConstructorStats.get(m.ReturnTester) + assert cstats.alive() == 1 + unsafe = [a.ref(), a.ref_const(), a.block(1, 2, 3, 4)] + copies = [a.copy_get(), a.copy_view(), a.copy_ref(), a.copy_ref_const(), + a.copy_block(4, 3, 2, 1)] + del a + assert cstats.alive() == 0 + del unsafe + del copies + + for meth in [m.ReturnTester.get, m.ReturnTester.get_ptr, m.ReturnTester.view, + m.ReturnTester.view_ptr, m.ReturnTester.ref_safe, m.ReturnTester.ref_const_safe, + m.ReturnTester.corners, m.ReturnTester.corners_const]: + assert_keeps_alive(m.ReturnTester, meth) + + for meth in [m.ReturnTester.block_safe, m.ReturnTester.block_const]: + assert_keeps_alive(m.ReturnTester, meth, 4, 3, 2, 1) + + +def test_eigen_ref_mutators(): + """Tests Eigen's ability to mutate numpy values""" + + orig = np.array([[1., 2, 3], [4, 5, 6], [7, 8, 9]]) + zr = np.array(orig) + zc = np.array(orig, order='F') + m.add_rm(zr, 1, 0, 100) + assert np.all(zr == np.array([[1., 2, 3], [104, 5, 6], [7, 8, 9]])) + m.add_cm(zc, 1, 0, 200) + assert np.all(zc == np.array([[1., 2, 3], [204, 5, 6], [7, 8, 9]])) + + m.add_any(zr, 1, 0, 20) + assert np.all(zr == np.array([[1., 2, 3], [124, 5, 6], [7, 8, 9]])) + m.add_any(zc, 1, 0, 10) + assert np.all(zc == np.array([[1., 2, 3], [214, 5, 6], [7, 8, 9]])) + + # Can't reference a col-major array with a row-major Ref, and vice versa: + with pytest.raises(TypeError): + m.add_rm(zc, 1, 0, 1) + with pytest.raises(TypeError): + m.add_cm(zr, 1, 0, 1) + + # Overloads: + m.add1(zr, 1, 0, -100) + m.add2(zr, 1, 0, -20) + assert np.all(zr == orig) + m.add1(zc, 1, 0, -200) + m.add2(zc, 1, 0, -10) + assert np.all(zc == orig) + + # a non-contiguous slice (this won't work on either the row- or + # column-contiguous refs, but should work for the any) + cornersr = zr[0::2, 0::2] + cornersc = zc[0::2, 0::2] + + assert np.all(cornersr == np.array([[1., 3], [7, 9]])) + assert np.all(cornersc == np.array([[1., 3], [7, 9]])) + + with pytest.raises(TypeError): + m.add_rm(cornersr, 0, 1, 25) + with pytest.raises(TypeError): + m.add_cm(cornersr, 0, 1, 25) + with pytest.raises(TypeError): + m.add_rm(cornersc, 0, 1, 25) + with pytest.raises(TypeError): + m.add_cm(cornersc, 0, 1, 25) + m.add_any(cornersr, 0, 1, 25) + m.add_any(cornersc, 0, 1, 44) + assert np.all(zr == np.array([[1., 2, 28], [4, 5, 6], [7, 8, 9]])) + assert np.all(zc == np.array([[1., 2, 47], [4, 5, 6], [7, 8, 9]])) + + # You shouldn't be allowed to pass a non-writeable array to a mutating Eigen method: + zro = zr[0:4, 0:4] + zro.flags.writeable = False + with pytest.raises(TypeError): + m.add_rm(zro, 0, 0, 0) + with pytest.raises(TypeError): + m.add_any(zro, 0, 0, 0) + with pytest.raises(TypeError): + m.add1(zro, 0, 0, 0) + with pytest.raises(TypeError): + m.add2(zro, 0, 0, 0) + + # integer array shouldn't be passable to a double-matrix-accepting mutating func: + zi = np.array([[1, 2], [3, 4]]) + with pytest.raises(TypeError): + m.add_rm(zi) + + +def test_numpy_ref_mutators(): + """Tests numpy mutating Eigen matrices (for returned Eigen::Ref<...>s)""" + + m.reset_refs() # In case another test already changed it + + zc = m.get_cm_ref() + zcro = m.get_cm_const_ref() + zr = m.get_rm_ref() + zrro = m.get_rm_const_ref() + + assert [zc[1, 2], zcro[1, 2], zr[1, 2], zrro[1, 2]] == [23] * 4 + + assert not zc.flags.owndata and zc.flags.writeable + assert not zr.flags.owndata and zr.flags.writeable + assert not zcro.flags.owndata and not zcro.flags.writeable + assert not zrro.flags.owndata and not zrro.flags.writeable + + zc[1, 2] = 99 + expect = np.array([[11., 12, 13], [21, 22, 99], [31, 32, 33]]) + # We should have just changed zc, of course, but also zcro and the original eigen matrix + assert np.all(zc == expect) + assert np.all(zcro == expect) + assert np.all(m.get_cm_ref() == expect) + + zr[1, 2] = 99 + assert np.all(zr == expect) + assert np.all(zrro == expect) + assert np.all(m.get_rm_ref() == expect) + + # Make sure the readonly ones are numpy-readonly: + with pytest.raises(ValueError): + zcro[1, 2] = 6 + with pytest.raises(ValueError): + zrro[1, 2] = 6 + + # We should be able to explicitly copy like this (and since we're copying, + # the const should drop away) + y1 = np.array(m.get_cm_const_ref()) + + assert y1.flags.owndata and y1.flags.writeable + # We should get copies of the eigen data, which was modified above: + assert y1[1, 2] == 99 + y1[1, 2] += 12 + assert y1[1, 2] == 111 + assert zc[1, 2] == 99 # Make sure we aren't referencing the original + + +def test_both_ref_mutators(): + """Tests a complex chain of nested eigen/numpy references""" + + m.reset_refs() # In case another test already changed it + + z = m.get_cm_ref() # numpy -> eigen + z[0, 2] -= 3 + z2 = m.incr_matrix(z, 1) # numpy -> eigen -> numpy -> eigen + z2[1, 1] += 6 + z3 = m.incr_matrix(z, 2) # (numpy -> eigen)^3 + z3[2, 2] += -5 + z4 = m.incr_matrix(z, 3) # (numpy -> eigen)^4 + z4[1, 1] -= 1 + z5 = m.incr_matrix(z, 4) # (numpy -> eigen)^5 + z5[0, 0] = 0 + assert np.all(z == z2) + assert np.all(z == z3) + assert np.all(z == z4) + assert np.all(z == z5) + expect = np.array([[0., 22, 20], [31, 37, 33], [41, 42, 38]]) + assert np.all(z == expect) + + y = np.array(range(100), dtype='float64').reshape(10, 10) + y2 = m.incr_matrix_any(y, 10) # np -> eigen -> np + y3 = m.incr_matrix_any(y2[0::2, 0::2], -33) # np -> eigen -> np slice -> np -> eigen -> np + y4 = m.even_rows(y3) # numpy -> eigen slice -> (... y3) + y5 = m.even_cols(y4) # numpy -> eigen slice -> (... y4) + y6 = m.incr_matrix_any(y5, 1000) # numpy -> eigen -> (... y5) + + # Apply same mutations using just numpy: + yexpect = np.array(range(100), dtype='float64').reshape(10, 10) + yexpect += 10 + yexpect[0::2, 0::2] -= 33 + yexpect[0::4, 0::4] += 1000 + assert np.all(y6 == yexpect[0::4, 0::4]) + assert np.all(y5 == yexpect[0::4, 0::4]) + assert np.all(y4 == yexpect[0::4, 0::2]) + assert np.all(y3 == yexpect[0::2, 0::2]) + assert np.all(y2 == yexpect) + assert np.all(y == yexpect) + + +def test_nocopy_wrapper(): + # get_elem requires a column-contiguous matrix reference, but should be + # callable with other types of matrix (via copying): + int_matrix_colmajor = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], order='F') + dbl_matrix_colmajor = np.array(int_matrix_colmajor, dtype='double', order='F', copy=True) + int_matrix_rowmajor = np.array(int_matrix_colmajor, order='C', copy=True) + dbl_matrix_rowmajor = np.array(int_matrix_rowmajor, dtype='double', order='C', copy=True) + + # All should be callable via get_elem: + assert m.get_elem(int_matrix_colmajor) == 8 + assert m.get_elem(dbl_matrix_colmajor) == 8 + assert m.get_elem(int_matrix_rowmajor) == 8 + assert m.get_elem(dbl_matrix_rowmajor) == 8 + + # All but the second should fail with m.get_elem_nocopy: + with pytest.raises(TypeError) as excinfo: + m.get_elem_nocopy(int_matrix_colmajor) + assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and + ', flags.f_contiguous' in str(excinfo.value)) + assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8 + with pytest.raises(TypeError) as excinfo: + m.get_elem_nocopy(int_matrix_rowmajor) + assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and + ', flags.f_contiguous' in str(excinfo.value)) + with pytest.raises(TypeError) as excinfo: + m.get_elem_nocopy(dbl_matrix_rowmajor) + assert ('get_elem_nocopy(): incompatible function arguments.' in str(excinfo.value) and + ', flags.f_contiguous' in str(excinfo.value)) + + # For the row-major test, we take a long matrix in row-major, so only the third is allowed: + with pytest.raises(TypeError) as excinfo: + m.get_elem_rm_nocopy(int_matrix_colmajor) + assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and + ', flags.c_contiguous' in str(excinfo.value)) + with pytest.raises(TypeError) as excinfo: + m.get_elem_rm_nocopy(dbl_matrix_colmajor) + assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and + ', flags.c_contiguous' in str(excinfo.value)) + assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8 + with pytest.raises(TypeError) as excinfo: + m.get_elem_rm_nocopy(dbl_matrix_rowmajor) + assert ('get_elem_rm_nocopy(): incompatible function arguments.' in str(excinfo.value) and + ', flags.c_contiguous' in str(excinfo.value)) + + +def test_eigen_ref_life_support(): + """Ensure the lifetime of temporary arrays created by the `Ref` caster + + The `Ref` caster sometimes creates a copy which needs to stay alive. This needs to + happen both for directs casts (just the array) or indirectly (e.g. list of arrays). + """ + + a = np.full(shape=10, fill_value=8, dtype=np.int8) + assert m.get_elem_direct(a) == 8 + + list_of_a = [a] + assert m.get_elem_indirect(list_of_a) == 8 + + +def test_special_matrix_objects(): + assert np.all(m.incr_diag(7) == np.diag([1., 2, 3, 4, 5, 6, 7])) + + asymm = np.array([[ 1., 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12], + [13, 14, 15, 16]]) + symm_lower = np.array(asymm) + symm_upper = np.array(asymm) + for i in range(4): + for j in range(i + 1, 4): + symm_lower[i, j] = symm_lower[j, i] + symm_upper[j, i] = symm_upper[i, j] + + assert np.all(m.symmetric_lower(asymm) == symm_lower) + assert np.all(m.symmetric_upper(asymm) == symm_upper) + + +def test_dense_signature(doc): + assert doc(m.double_col) == """ + double_col(arg0: numpy.ndarray[float32[m, 1]]) -> numpy.ndarray[float32[m, 1]] + """ + assert doc(m.double_row) == """ + double_row(arg0: numpy.ndarray[float32[1, n]]) -> numpy.ndarray[float32[1, n]] + """ + assert doc(m.double_complex) == """ + double_complex(arg0: numpy.ndarray[complex64[m, 1]]) -> numpy.ndarray[complex64[m, 1]] + """ + assert doc(m.double_mat_rm) == """ + double_mat_rm(arg0: numpy.ndarray[float32[m, n]]) -> numpy.ndarray[float32[m, n]] + """ + + +def test_named_arguments(): + a = np.array([[1.0, 2], [3, 4], [5, 6]]) + b = np.ones((2, 1)) + + assert np.all(m.matrix_multiply(a, b) == np.array([[3.], [7], [11]])) + assert np.all(m.matrix_multiply(A=a, B=b) == np.array([[3.], [7], [11]])) + assert np.all(m.matrix_multiply(B=b, A=a) == np.array([[3.], [7], [11]])) + + with pytest.raises(ValueError) as excinfo: + m.matrix_multiply(b, a) + assert str(excinfo.value) == 'Nonconformable matrices!' + + with pytest.raises(ValueError) as excinfo: + m.matrix_multiply(A=b, B=a) + assert str(excinfo.value) == 'Nonconformable matrices!' + + with pytest.raises(ValueError) as excinfo: + m.matrix_multiply(B=a, A=b) + assert str(excinfo.value) == 'Nonconformable matrices!' + + +@pytest.requires_eigen_and_scipy +def test_sparse(): + assert_sparse_equal_ref(m.sparse_r()) + assert_sparse_equal_ref(m.sparse_c()) + assert_sparse_equal_ref(m.sparse_copy_r(m.sparse_r())) + assert_sparse_equal_ref(m.sparse_copy_c(m.sparse_c())) + assert_sparse_equal_ref(m.sparse_copy_r(m.sparse_c())) + assert_sparse_equal_ref(m.sparse_copy_c(m.sparse_r())) + + +@pytest.requires_eigen_and_scipy +def test_sparse_signature(doc): + assert doc(m.sparse_copy_r) == """ + sparse_copy_r(arg0: scipy.sparse.csr_matrix[float32]) -> scipy.sparse.csr_matrix[float32] + """ # noqa: E501 line too long + assert doc(m.sparse_copy_c) == """ + sparse_copy_c(arg0: scipy.sparse.csc_matrix[float32]) -> scipy.sparse.csc_matrix[float32] + """ # noqa: E501 line too long + + +def test_issue738(): + """Ignore strides on a length-1 dimension (even if they would be incompatible length > 1)""" + assert np.all(m.iss738_f1(np.array([[1., 2, 3]])) == np.array([[1., 102, 203]])) + assert np.all(m.iss738_f1(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]])) + + assert np.all(m.iss738_f2(np.array([[1., 2, 3]])) == np.array([[1., 102, 203]])) + assert np.all(m.iss738_f2(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]])) + + +def test_custom_operator_new(): + """Using Eigen types as member variables requires a class-specific + operator new with proper alignment""" + + o = m.CustomOperatorNew() + np.testing.assert_allclose(o.a, 0.0) + np.testing.assert_allclose(o.b.diagonal(), 1.0) diff --git a/external/pybind11/tests/test_embed/CMakeLists.txt b/external/pybind11/tests/test_embed/CMakeLists.txt new file mode 100644 index 0000000..0a43e0e --- /dev/null +++ b/external/pybind11/tests/test_embed/CMakeLists.txt @@ -0,0 +1,34 @@ +if(${PYTHON_MODULE_EXTENSION} MATCHES "pypy") + add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. + set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") + return() +endif() + +find_package(Catch 1.9.3) +if(NOT CATCH_FOUND) + message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers" + " manually or use `cmake -DDOWNLOAD_CATCH=1` to fetch them automatically.") + return() +endif() + +add_executable(test_embed + catch.cpp + test_interpreter.cpp +) +target_include_directories(test_embed PRIVATE ${CATCH_INCLUDE_DIR}) +pybind11_enable_warnings(test_embed) + +if(NOT CMAKE_VERSION VERSION_LESS 3.0) + target_link_libraries(test_embed PRIVATE pybind11::embed) +else() + target_include_directories(test_embed PRIVATE ${PYBIND11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}) + target_compile_options(test_embed PRIVATE ${PYBIND11_CPP_STANDARD}) + target_link_libraries(test_embed PRIVATE ${PYTHON_LIBRARIES}) +endif() + +find_package(Threads REQUIRED) +target_link_libraries(test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT}) + +add_custom_target(cpptest COMMAND $ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_dependencies(check cpptest) diff --git a/external/pybind11/tests/test_embed/catch.cpp b/external/pybind11/tests/test_embed/catch.cpp new file mode 100644 index 0000000..cface48 --- /dev/null +++ b/external/pybind11/tests/test_embed/catch.cpp @@ -0,0 +1,16 @@ +// The Catch implementation is compiled here. This is a standalone +// translation unit to avoid recompiling it for every test change. + +#include + +#define CATCH_CONFIG_RUNNER +#include + +namespace py = pybind11; + +int main(int argc, const char *argv[]) { + py::scoped_interpreter guard{}; + auto result = Catch::Session().run(argc, argv); + + return result < 0xff ? result : 0xff; +} diff --git a/external/pybind11/tests/test_embed/test_interpreter.cpp b/external/pybind11/tests/test_embed/test_interpreter.cpp new file mode 100644 index 0000000..6b5f051 --- /dev/null +++ b/external/pybind11/tests/test_embed/test_interpreter.cpp @@ -0,0 +1,269 @@ +#include +#include + +#include +#include +#include + +namespace py = pybind11; +using namespace py::literals; + +class Widget { +public: + Widget(std::string message) : message(message) { } + virtual ~Widget() = default; + + std::string the_message() const { return message; } + virtual int the_answer() const = 0; + +private: + std::string message; +}; + +class PyWidget final : public Widget { + using Widget::Widget; + + int the_answer() const override { PYBIND11_OVERLOAD_PURE(int, Widget, the_answer); } +}; + +PYBIND11_EMBEDDED_MODULE(widget_module, m) { + py::class_(m, "Widget") + .def(py::init()) + .def_property_readonly("the_message", &Widget::the_message); + + m.def("add", [](int i, int j) { return i + j; }); +} + +PYBIND11_EMBEDDED_MODULE(throw_exception, ) { + throw std::runtime_error("C++ Error"); +} + +PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) { + auto d = py::dict(); + d["missing"].cast(); +} + +TEST_CASE("Pass classes and data between modules defined in C++ and Python") { + auto module = py::module::import("test_interpreter"); + REQUIRE(py::hasattr(module, "DerivedWidget")); + + auto locals = py::dict("hello"_a="Hello, World!", "x"_a=5, **module.attr("__dict__")); + py::exec(R"( + widget = DerivedWidget("{} - {}".format(hello, x)) + message = widget.the_message + )", py::globals(), locals); + REQUIRE(locals["message"].cast() == "Hello, World! - 5"); + + auto py_widget = module.attr("DerivedWidget")("The question"); + auto message = py_widget.attr("the_message"); + REQUIRE(message.cast() == "The question"); + + const auto &cpp_widget = py_widget.cast(); + REQUIRE(cpp_widget.the_answer() == 42); +} + +TEST_CASE("Import error handling") { + REQUIRE_NOTHROW(py::module::import("widget_module")); + REQUIRE_THROWS_WITH(py::module::import("throw_exception"), + "ImportError: C++ Error"); + REQUIRE_THROWS_WITH(py::module::import("throw_error_already_set"), + Catch::Contains("ImportError: KeyError")); +} + +TEST_CASE("There can be only one interpreter") { + static_assert(std::is_move_constructible::value, ""); + static_assert(!std::is_move_assignable::value, ""); + static_assert(!std::is_copy_constructible::value, ""); + static_assert(!std::is_copy_assignable::value, ""); + + REQUIRE_THROWS_WITH(py::initialize_interpreter(), "The interpreter is already running"); + REQUIRE_THROWS_WITH(py::scoped_interpreter(), "The interpreter is already running"); + + py::finalize_interpreter(); + REQUIRE_NOTHROW(py::scoped_interpreter()); + { + auto pyi1 = py::scoped_interpreter(); + auto pyi2 = std::move(pyi1); + } + py::initialize_interpreter(); +} + +bool has_pybind11_internals_builtin() { + auto builtins = py::handle(PyEval_GetBuiltins()); + return builtins.contains(PYBIND11_INTERNALS_ID); +}; + +bool has_pybind11_internals_static() { + return py::detail::get_internals_ptr() != nullptr; +} + +TEST_CASE("Restart the interpreter") { + // Verify pre-restart state. + REQUIRE(py::module::import("widget_module").attr("add")(1, 2).cast() == 3); + REQUIRE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_static()); + + // Restart the interpreter. + py::finalize_interpreter(); + REQUIRE(Py_IsInitialized() == 0); + + py::initialize_interpreter(); + REQUIRE(Py_IsInitialized() == 1); + + // Internals are deleted after a restart. + REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_static()); + pybind11::detail::get_internals(); + REQUIRE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_static()); + + // Make sure that an interpreter with no get_internals() created until finalize still gets the + // internals destroyed + py::finalize_interpreter(); + py::initialize_interpreter(); + bool ran = false; + py::module::import("__main__").attr("internals_destroy_test") = + py::capsule(&ran, [](void *ran) { py::detail::get_internals(); *static_cast(ran) = true; }); + REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_static()); + REQUIRE_FALSE(ran); + py::finalize_interpreter(); + REQUIRE(ran); + py::initialize_interpreter(); + REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_static()); + + // C++ modules can be reloaded. + auto cpp_module = py::module::import("widget_module"); + REQUIRE(cpp_module.attr("add")(1, 2).cast() == 3); + + // C++ type information is reloaded and can be used in python modules. + auto py_module = py::module::import("test_interpreter"); + auto py_widget = py_module.attr("DerivedWidget")("Hello after restart"); + REQUIRE(py_widget.attr("the_message").cast() == "Hello after restart"); +} + +TEST_CASE("Subinterpreter") { + // Add tags to the modules in the main interpreter and test the basics. + py::module::import("__main__").attr("main_tag") = "main interpreter"; + { + auto m = py::module::import("widget_module"); + m.attr("extension_module_tag") = "added to module in main interpreter"; + + REQUIRE(m.attr("add")(1, 2).cast() == 3); + } + REQUIRE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_static()); + + /// Create and switch to a subinterpreter. + auto main_tstate = PyThreadState_Get(); + auto sub_tstate = Py_NewInterpreter(); + + // Subinterpreters get their own copy of builtins. detail::get_internals() still + // works by returning from the static variable, i.e. all interpreters share a single + // global pybind11::internals; + REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_static()); + + // Modules tags should be gone. + REQUIRE_FALSE(py::hasattr(py::module::import("__main__"), "tag")); + { + auto m = py::module::import("widget_module"); + REQUIRE_FALSE(py::hasattr(m, "extension_module_tag")); + + // Function bindings should still work. + REQUIRE(m.attr("add")(1, 2).cast() == 3); + } + + // Restore main interpreter. + Py_EndInterpreter(sub_tstate); + PyThreadState_Swap(main_tstate); + + REQUIRE(py::hasattr(py::module::import("__main__"), "main_tag")); + REQUIRE(py::hasattr(py::module::import("widget_module"), "extension_module_tag")); +} + +TEST_CASE("Execution frame") { + // When the interpreter is embedded, there is no execution frame, but `py::exec` + // should still function by using reasonable globals: `__main__.__dict__`. + py::exec("var = dict(number=42)"); + REQUIRE(py::globals()["var"]["number"].cast() == 42); +} + +TEST_CASE("Threads") { + // Restart interpreter to ensure threads are not initialized + py::finalize_interpreter(); + py::initialize_interpreter(); + REQUIRE_FALSE(has_pybind11_internals_static()); + + constexpr auto num_threads = 10; + auto locals = py::dict("count"_a=0); + + { + py::gil_scoped_release gil_release{}; + REQUIRE(has_pybind11_internals_static()); + + auto threads = std::vector(); + for (auto i = 0; i < num_threads; ++i) { + threads.emplace_back([&]() { + py::gil_scoped_acquire gil{}; + locals["count"] = locals["count"].cast() + 1; + }); + } + + for (auto &thread : threads) { + thread.join(); + } + } + + REQUIRE(locals["count"].cast() == num_threads); +} + +// Scope exit utility https://stackoverflow.com/a/36644501/7255855 +struct scope_exit { + std::function f_; + explicit scope_exit(std::function f) noexcept : f_(std::move(f)) {} + ~scope_exit() { if (f_) f_(); } +}; + +TEST_CASE("Reload module from file") { + // Disable generation of cached bytecode (.pyc files) for this test, otherwise + // Python might pick up an old version from the cache instead of the new versions + // of the .py files generated below + auto sys = py::module::import("sys"); + bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast(); + sys.attr("dont_write_bytecode") = true; + // Reset the value at scope exit + scope_exit reset_dont_write_bytecode([&]() { + sys.attr("dont_write_bytecode") = dont_write_bytecode; + }); + + std::string module_name = "test_module_reload"; + std::string module_file = module_name + ".py"; + + // Create the module .py file + std::ofstream test_module(module_file); + test_module << "def test():\n"; + test_module << " return 1\n"; + test_module.close(); + // Delete the file at scope exit + scope_exit delete_module_file([&]() { + std::remove(module_file.c_str()); + }); + + // Import the module from file + auto module = py::module::import(module_name.c_str()); + int result = module.attr("test")().cast(); + REQUIRE(result == 1); + + // Update the module .py file with a small change + test_module.open(module_file); + test_module << "def test():\n"; + test_module << " return 2\n"; + test_module.close(); + + // Reload the module + module.reload(); + result = module.attr("test")().cast(); + REQUIRE(result == 2); +} diff --git a/external/pybind11/tests/test_embed/test_interpreter.py b/external/pybind11/tests/test_embed/test_interpreter.py new file mode 100644 index 0000000..26a0479 --- /dev/null +++ b/external/pybind11/tests/test_embed/test_interpreter.py @@ -0,0 +1,9 @@ +from widget_module import Widget + + +class DerivedWidget(Widget): + def __init__(self, message): + super(DerivedWidget, self).__init__(message) + + def the_answer(self): + return 42 diff --git a/external/pybind11/tests/test_enum.cpp b/external/pybind11/tests/test_enum.cpp new file mode 100644 index 0000000..49f31ba --- /dev/null +++ b/external/pybind11/tests/test_enum.cpp @@ -0,0 +1,71 @@ +/* + tests/test_enums.cpp -- enumerations + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +TEST_SUBMODULE(enums, m) { + // test_unscoped_enum + enum UnscopedEnum { + EOne = 1, + ETwo + }; + py::enum_(m, "UnscopedEnum", py::arithmetic()) + .value("EOne", EOne) + .value("ETwo", ETwo) + .export_values(); + + // test_scoped_enum + enum class ScopedEnum { + Two = 2, + Three + }; + py::enum_(m, "ScopedEnum", py::arithmetic()) + .value("Two", ScopedEnum::Two) + .value("Three", ScopedEnum::Three); + + m.def("test_scoped_enum", [](ScopedEnum z) { + return "ScopedEnum::" + std::string(z == ScopedEnum::Two ? "Two" : "Three"); + }); + + // test_binary_operators + enum Flags { + Read = 4, + Write = 2, + Execute = 1 + }; + py::enum_(m, "Flags", py::arithmetic()) + .value("Read", Flags::Read) + .value("Write", Flags::Write) + .value("Execute", Flags::Execute) + .export_values(); + + // test_implicit_conversion + class ClassWithUnscopedEnum { + public: + enum EMode { + EFirstMode = 1, + ESecondMode + }; + + static EMode test_function(EMode mode) { + return mode; + } + }; + py::class_ exenum_class(m, "ClassWithUnscopedEnum"); + exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function); + py::enum_(exenum_class, "EMode") + .value("EFirstMode", ClassWithUnscopedEnum::EFirstMode) + .value("ESecondMode", ClassWithUnscopedEnum::ESecondMode) + .export_values(); + + // test_enum_to_int + m.def("test_enum_to_int", [](int) { }); + m.def("test_enum_to_uint", [](uint32_t) { }); + m.def("test_enum_to_long_long", [](long long) { }); +} diff --git a/external/pybind11/tests/test_enum.py b/external/pybind11/tests/test_enum.py new file mode 100644 index 0000000..d8eff52 --- /dev/null +++ b/external/pybind11/tests/test_enum.py @@ -0,0 +1,121 @@ +import pytest +from pybind11_tests import enums as m + + +def test_unscoped_enum(): + assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne" + assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo" + assert str(m.EOne) == "UnscopedEnum.EOne" + # __members__ property + assert m.UnscopedEnum.__members__ == \ + {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo} + # __members__ readonly + with pytest.raises(AttributeError): + m.UnscopedEnum.__members__ = {} + # __members__ returns a copy + foo = m.UnscopedEnum.__members__ + foo["bar"] = "baz" + assert m.UnscopedEnum.__members__ == \ + {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo} + + # no TypeError exception for unscoped enum ==/!= int comparisons + y = m.UnscopedEnum.ETwo + assert y == 2 + assert y != 3 + + assert int(m.UnscopedEnum.ETwo) == 2 + assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo" + + # order + assert m.UnscopedEnum.EOne < m.UnscopedEnum.ETwo + assert m.UnscopedEnum.EOne < 2 + assert m.UnscopedEnum.ETwo > m.UnscopedEnum.EOne + assert m.UnscopedEnum.ETwo > 1 + assert m.UnscopedEnum.ETwo <= 2 + assert m.UnscopedEnum.ETwo >= 2 + assert m.UnscopedEnum.EOne <= m.UnscopedEnum.ETwo + assert m.UnscopedEnum.EOne <= 2 + assert m.UnscopedEnum.ETwo >= m.UnscopedEnum.EOne + assert m.UnscopedEnum.ETwo >= 1 + assert not (m.UnscopedEnum.ETwo < m.UnscopedEnum.EOne) + assert not (2 < m.UnscopedEnum.EOne) + + +def test_scoped_enum(): + assert m.test_scoped_enum(m.ScopedEnum.Three) == "ScopedEnum::Three" + z = m.ScopedEnum.Two + assert m.test_scoped_enum(z) == "ScopedEnum::Two" + + # expected TypeError exceptions for scoped enum ==/!= int comparisons + with pytest.raises(TypeError): + assert z == 2 + with pytest.raises(TypeError): + assert z != 3 + + # order + assert m.ScopedEnum.Two < m.ScopedEnum.Three + assert m.ScopedEnum.Three > m.ScopedEnum.Two + assert m.ScopedEnum.Two <= m.ScopedEnum.Three + assert m.ScopedEnum.Two <= m.ScopedEnum.Two + assert m.ScopedEnum.Two >= m.ScopedEnum.Two + assert m.ScopedEnum.Three >= m.ScopedEnum.Two + + +def test_implicit_conversion(): + assert str(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "EMode.EFirstMode" + assert str(m.ClassWithUnscopedEnum.EFirstMode) == "EMode.EFirstMode" + + f = m.ClassWithUnscopedEnum.test_function + first = m.ClassWithUnscopedEnum.EFirstMode + second = m.ClassWithUnscopedEnum.ESecondMode + + assert f(first) == 1 + + assert f(first) == f(first) + assert not f(first) != f(first) + + assert f(first) != f(second) + assert not f(first) == f(second) + + assert f(first) == int(f(first)) + assert not f(first) != int(f(first)) + + assert f(first) != int(f(second)) + assert not f(first) == int(f(second)) + + # noinspection PyDictCreation + x = {f(first): 1, f(second): 2} + x[f(first)] = 3 + x[f(second)] = 4 + # Hashing test + assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}" + + +def test_binary_operators(): + assert int(m.Flags.Read) == 4 + assert int(m.Flags.Write) == 2 + assert int(m.Flags.Execute) == 1 + assert int(m.Flags.Read | m.Flags.Write | m.Flags.Execute) == 7 + assert int(m.Flags.Read | m.Flags.Write) == 6 + assert int(m.Flags.Read | m.Flags.Execute) == 5 + assert int(m.Flags.Write | m.Flags.Execute) == 3 + assert int(m.Flags.Write | 1) == 3 + + state = m.Flags.Read | m.Flags.Write + assert (state & m.Flags.Read) != 0 + assert (state & m.Flags.Write) != 0 + assert (state & m.Flags.Execute) == 0 + assert (state & 1) == 0 + + state2 = ~state + assert state2 == -7 + assert int(state ^ state2) == -1 + + +def test_enum_to_int(): + m.test_enum_to_int(m.Flags.Read) + m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_uint(m.Flags.Read) + m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_long_long(m.Flags.Read) + m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode) diff --git a/external/pybind11/tests/test_eval.cpp b/external/pybind11/tests/test_eval.cpp new file mode 100644 index 0000000..e094821 --- /dev/null +++ b/external/pybind11/tests/test_eval.cpp @@ -0,0 +1,91 @@ +/* + tests/test_eval.cpp -- Usage of eval() and eval_file() + + Copyright (c) 2016 Klemens D. Morgenstern + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + + +#include +#include "pybind11_tests.h" + +TEST_SUBMODULE(eval_, m) { + // test_evals + + auto global = py::dict(py::module::import("__main__").attr("__dict__")); + + m.def("test_eval_statements", [global]() { + auto local = py::dict(); + local["call_test"] = py::cpp_function([&]() -> int { + return 42; + }); + + // Regular string literal + py::exec( + "message = 'Hello World!'\n" + "x = call_test()", + global, local + ); + + // Multi-line raw string literal + py::exec(R"( + if x == 42: + print(message) + else: + raise RuntimeError + )", global, local + ); + auto x = local["x"].cast(); + + return x == 42; + }); + + m.def("test_eval", [global]() { + auto local = py::dict(); + local["x"] = py::int_(42); + auto x = py::eval("x", global, local); + return x.cast() == 42; + }); + + m.def("test_eval_single_statement", []() { + auto local = py::dict(); + local["call_test"] = py::cpp_function([&]() -> int { + return 42; + }); + + auto result = py::eval("x = call_test()", py::dict(), local); + auto x = local["x"].cast(); + return result.is_none() && x == 42; + }); + + m.def("test_eval_file", [global](py::str filename) { + auto local = py::dict(); + local["y"] = py::int_(43); + + int val_out; + local["call_test2"] = py::cpp_function([&](int value) { val_out = value; }); + + auto result = py::eval_file(filename, global, local); + return val_out == 43 && result.is_none(); + }); + + m.def("test_eval_failure", []() { + try { + py::eval("nonsense code ..."); + } catch (py::error_already_set &) { + return true; + } + return false; + }); + + m.def("test_eval_file_failure", []() { + try { + py::eval_file("non-existing file"); + } catch (std::exception &) { + return true; + } + return false; + }); +} diff --git a/external/pybind11/tests/test_eval.py b/external/pybind11/tests/test_eval.py new file mode 100644 index 0000000..bda4ef6 --- /dev/null +++ b/external/pybind11/tests/test_eval.py @@ -0,0 +1,17 @@ +import os +from pybind11_tests import eval_ as m + + +def test_evals(capture): + with capture: + assert m.test_eval_statements() + assert capture == "Hello World!" + + assert m.test_eval() + assert m.test_eval_single_statement() + + filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") + assert m.test_eval_file(filename) + + assert m.test_eval_failure() + assert m.test_eval_file_failure() diff --git a/external/pybind11/tests/test_eval_call.py b/external/pybind11/tests/test_eval_call.py new file mode 100644 index 0000000..53c7e72 --- /dev/null +++ b/external/pybind11/tests/test_eval_call.py @@ -0,0 +1,4 @@ +# This file is called from 'test_eval.py' + +if 'call_test2' in locals(): + call_test2(y) # noqa: F821 undefined name diff --git a/external/pybind11/tests/test_exceptions.cpp b/external/pybind11/tests/test_exceptions.cpp new file mode 100644 index 0000000..ae28abb --- /dev/null +++ b/external/pybind11/tests/test_exceptions.cpp @@ -0,0 +1,168 @@ +/* + tests/test_custom-exceptions.cpp -- exception translation + + Copyright (c) 2016 Pim Schellart + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +// A type that should be raised as an exeption in Python +class MyException : public std::exception { +public: + explicit MyException(const char * m) : message{m} {} + virtual const char * what() const noexcept override {return message.c_str();} +private: + std::string message = ""; +}; + +// A type that should be translated to a standard Python exception +class MyException2 : public std::exception { +public: + explicit MyException2(const char * m) : message{m} {} + virtual const char * what() const noexcept override {return message.c_str();} +private: + std::string message = ""; +}; + +// A type that is not derived from std::exception (and is thus unknown) +class MyException3 { +public: + explicit MyException3(const char * m) : message{m} {} + virtual const char * what() const noexcept {return message.c_str();} +private: + std::string message = ""; +}; + +// A type that should be translated to MyException +// and delegated to its exception translator +class MyException4 : public std::exception { +public: + explicit MyException4(const char * m) : message{m} {} + virtual const char * what() const noexcept override {return message.c_str();} +private: + std::string message = ""; +}; + + +// Like the above, but declared via the helper function +class MyException5 : public std::logic_error { +public: + explicit MyException5(const std::string &what) : std::logic_error(what) {} +}; + +// Inherits from MyException5 +class MyException5_1 : public MyException5 { + using MyException5::MyException5; +}; + +struct PythonCallInDestructor { + PythonCallInDestructor(const py::dict &d) : d(d) {} + ~PythonCallInDestructor() { d["good"] = true; } + + py::dict d; +}; + +TEST_SUBMODULE(exceptions, m) { + m.def("throw_std_exception", []() { + throw std::runtime_error("This exception was intentionally thrown."); + }); + + // make a new custom exception and use it as a translation target + static py::exception ex(m, "MyException"); + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const MyException &e) { + // Set MyException as the active python error + ex(e.what()); + } + }); + + // register new translator for MyException2 + // no need to store anything here because this type will + // never by visible from Python + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const MyException2 &e) { + // Translate this exception to a standard RuntimeError + PyErr_SetString(PyExc_RuntimeError, e.what()); + } + }); + + // register new translator for MyException4 + // which will catch it and delegate to the previously registered + // translator for MyException by throwing a new exception + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const MyException4 &e) { + throw MyException(e.what()); + } + }); + + // A simple exception translation: + auto ex5 = py::register_exception(m, "MyException5"); + // A slightly more complicated one that declares MyException5_1 as a subclass of MyException5 + py::register_exception(m, "MyException5_1", ex5.ptr()); + + m.def("throws1", []() { throw MyException("this error should go to a custom type"); }); + m.def("throws2", []() { throw MyException2("this error should go to a standard Python exception"); }); + m.def("throws3", []() { throw MyException3("this error cannot be translated"); }); + m.def("throws4", []() { throw MyException4("this error is rethrown"); }); + m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); }); + m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); }); + m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); }); + m.def("exception_matches", []() { + py::dict foo; + try { foo["bar"]; } + catch (py::error_already_set& ex) { + if (!ex.matches(PyExc_KeyError)) throw; + } + }); + + m.def("throw_already_set", [](bool err) { + if (err) + PyErr_SetString(PyExc_ValueError, "foo"); + try { + throw py::error_already_set(); + } catch (const std::runtime_error& e) { + if ((err && e.what() != std::string("ValueError: foo")) || + (!err && e.what() != std::string("Unknown internal error occurred"))) + { + PyErr_Clear(); + throw std::runtime_error("error message mismatch"); + } + } + PyErr_Clear(); + if (err) + PyErr_SetString(PyExc_ValueError, "foo"); + throw py::error_already_set(); + }); + + m.def("python_call_in_destructor", [](py::dict d) { + try { + PythonCallInDestructor set_dict_in_destructor(d); + PyErr_SetString(PyExc_ValueError, "foo"); + throw py::error_already_set(); + } catch (const py::error_already_set&) { + return true; + } + return false; + }); + + // test_nested_throws + m.def("try_catch", [m](py::object exc_type, py::function f, py::args args) { + try { f(*args); } + catch (py::error_already_set &ex) { + if (ex.matches(exc_type)) + py::print(ex.what()); + else + throw; + } + }); + +} diff --git a/external/pybind11/tests/test_exceptions.py b/external/pybind11/tests/test_exceptions.py new file mode 100644 index 0000000..8d37c09 --- /dev/null +++ b/external/pybind11/tests/test_exceptions.py @@ -0,0 +1,144 @@ +import pytest + +from pybind11_tests import exceptions as m +import pybind11_cross_module_tests as cm + + +def test_std_exception(msg): + with pytest.raises(RuntimeError) as excinfo: + m.throw_std_exception() + assert msg(excinfo.value) == "This exception was intentionally thrown." + + +def test_error_already_set(msg): + with pytest.raises(RuntimeError) as excinfo: + m.throw_already_set(False) + assert msg(excinfo.value) == "Unknown internal error occurred" + + with pytest.raises(ValueError) as excinfo: + m.throw_already_set(True) + assert msg(excinfo.value) == "foo" + + +def test_cross_module_exceptions(): + with pytest.raises(RuntimeError) as excinfo: + cm.raise_runtime_error() + assert str(excinfo.value) == "My runtime error" + + with pytest.raises(ValueError) as excinfo: + cm.raise_value_error() + assert str(excinfo.value) == "My value error" + + with pytest.raises(ValueError) as excinfo: + cm.throw_pybind_value_error() + assert str(excinfo.value) == "pybind11 value error" + + with pytest.raises(TypeError) as excinfo: + cm.throw_pybind_type_error() + assert str(excinfo.value) == "pybind11 type error" + + with pytest.raises(StopIteration) as excinfo: + cm.throw_stop_iteration() + + +def test_python_call_in_catch(): + d = {} + assert m.python_call_in_destructor(d) is True + assert d["good"] is True + + +def test_exception_matches(): + m.exception_matches() + + +def test_custom(msg): + # Can we catch a MyException? + with pytest.raises(m.MyException) as excinfo: + m.throws1() + assert msg(excinfo.value) == "this error should go to a custom type" + + # Can we translate to standard Python exceptions? + with pytest.raises(RuntimeError) as excinfo: + m.throws2() + assert msg(excinfo.value) == "this error should go to a standard Python exception" + + # Can we handle unknown exceptions? + with pytest.raises(RuntimeError) as excinfo: + m.throws3() + assert msg(excinfo.value) == "Caught an unknown exception!" + + # Can we delegate to another handler by rethrowing? + with pytest.raises(m.MyException) as excinfo: + m.throws4() + assert msg(excinfo.value) == "this error is rethrown" + + # Can we fall-through to the default handler? + with pytest.raises(RuntimeError) as excinfo: + m.throws_logic_error() + assert msg(excinfo.value) == "this error should fall through to the standard handler" + + # Can we handle a helper-declared exception? + with pytest.raises(m.MyException5) as excinfo: + m.throws5() + assert msg(excinfo.value) == "this is a helper-defined translated exception" + + # Exception subclassing: + with pytest.raises(m.MyException5) as excinfo: + m.throws5_1() + assert msg(excinfo.value) == "MyException5 subclass" + assert isinstance(excinfo.value, m.MyException5_1) + + with pytest.raises(m.MyException5_1) as excinfo: + m.throws5_1() + assert msg(excinfo.value) == "MyException5 subclass" + + with pytest.raises(m.MyException5) as excinfo: + try: + m.throws5() + except m.MyException5_1: + raise RuntimeError("Exception error: caught child from parent") + assert msg(excinfo.value) == "this is a helper-defined translated exception" + + +def test_nested_throws(capture): + """Tests nested (e.g. C++ -> Python -> C++) exception handling""" + + def throw_myex(): + raise m.MyException("nested error") + + def throw_myex5(): + raise m.MyException5("nested error 5") + + # In the comments below, the exception is caught in the first step, thrown in the last step + + # C++ -> Python + with capture: + m.try_catch(m.MyException5, throw_myex5) + assert str(capture).startswith("MyException5: nested error 5") + + # Python -> C++ -> Python + with pytest.raises(m.MyException) as excinfo: + m.try_catch(m.MyException5, throw_myex) + assert str(excinfo.value) == "nested error" + + def pycatch(exctype, f, *args): + try: + f(*args) + except m.MyException as e: + print(e) + + # C++ -> Python -> C++ -> Python + with capture: + m.try_catch( + m.MyException5, pycatch, m.MyException, m.try_catch, m.MyException, throw_myex5) + assert str(capture).startswith("MyException5: nested error 5") + + # C++ -> Python -> C++ + with capture: + m.try_catch(m.MyException, pycatch, m.MyException5, m.throws4) + assert capture == "this error is rethrown" + + # Python -> C++ -> Python -> C++ + with pytest.raises(m.MyException5) as excinfo: + m.try_catch(m.MyException, pycatch, m.MyException, m.throws5) + assert str(excinfo.value) == "this is a helper-defined translated exception" diff --git a/external/pybind11/tests/test_factory_constructors.cpp b/external/pybind11/tests/test_factory_constructors.cpp new file mode 100644 index 0000000..fb33377 --- /dev/null +++ b/external/pybind11/tests/test_factory_constructors.cpp @@ -0,0 +1,337 @@ +/* + tests/test_factory_constructors.cpp -- tests construction from a factory function + via py::init_factory() + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include + +// Classes for testing python construction via C++ factory function: +// Not publically constructible, copyable, or movable: +class TestFactory1 { + friend class TestFactoryHelper; + TestFactory1() : value("(empty)") { print_default_created(this); } + TestFactory1(int v) : value(std::to_string(v)) { print_created(this, value); } + TestFactory1(std::string v) : value(std::move(v)) { print_created(this, value); } + TestFactory1(TestFactory1 &&) = delete; + TestFactory1(const TestFactory1 &) = delete; + TestFactory1 &operator=(TestFactory1 &&) = delete; + TestFactory1 &operator=(const TestFactory1 &) = delete; +public: + std::string value; + ~TestFactory1() { print_destroyed(this); } +}; +// Non-public construction, but moveable: +class TestFactory2 { + friend class TestFactoryHelper; + TestFactory2() : value("(empty2)") { print_default_created(this); } + TestFactory2(int v) : value(std::to_string(v)) { print_created(this, value); } + TestFactory2(std::string v) : value(std::move(v)) { print_created(this, value); } +public: + TestFactory2(TestFactory2 &&m) { value = std::move(m.value); print_move_created(this); } + TestFactory2 &operator=(TestFactory2 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } + std::string value; + ~TestFactory2() { print_destroyed(this); } +}; +// Mixed direct/factory construction: +class TestFactory3 { +protected: + friend class TestFactoryHelper; + TestFactory3() : value("(empty3)") { print_default_created(this); } + TestFactory3(int v) : value(std::to_string(v)) { print_created(this, value); } +public: + TestFactory3(std::string v) : value(std::move(v)) { print_created(this, value); } + TestFactory3(TestFactory3 &&m) { value = std::move(m.value); print_move_created(this); } + TestFactory3 &operator=(TestFactory3 &&m) { value = std::move(m.value); print_move_assigned(this); return *this; } + std::string value; + virtual ~TestFactory3() { print_destroyed(this); } +}; +// Inheritance test +class TestFactory4 : public TestFactory3 { +public: + TestFactory4() : TestFactory3() { print_default_created(this); } + TestFactory4(int v) : TestFactory3(v) { print_created(this, v); } + virtual ~TestFactory4() { print_destroyed(this); } +}; +// Another class for an invalid downcast test +class TestFactory5 : public TestFactory3 { +public: + TestFactory5(int i) : TestFactory3(i) { print_created(this, i); } + virtual ~TestFactory5() { print_destroyed(this); } +}; + +class TestFactory6 { +protected: + int value; + bool alias = false; +public: + TestFactory6(int i) : value{i} { print_created(this, i); } + TestFactory6(TestFactory6 &&f) { print_move_created(this); value = f.value; alias = f.alias; } + TestFactory6(const TestFactory6 &f) { print_copy_created(this); value = f.value; alias = f.alias; } + virtual ~TestFactory6() { print_destroyed(this); } + virtual int get() { return value; } + bool has_alias() { return alias; } +}; +class PyTF6 : public TestFactory6 { +public: + // Special constructor that allows the factory to construct a PyTF6 from a TestFactory6 only + // when an alias is needed: + PyTF6(TestFactory6 &&base) : TestFactory6(std::move(base)) { alias = true; print_created(this, "move", value); } + PyTF6(int i) : TestFactory6(i) { alias = true; print_created(this, i); } + PyTF6(PyTF6 &&f) : TestFactory6(std::move(f)) { print_move_created(this); } + PyTF6(const PyTF6 &f) : TestFactory6(f) { print_copy_created(this); } + PyTF6(std::string s) : TestFactory6((int) s.size()) { alias = true; print_created(this, s); } + virtual ~PyTF6() { print_destroyed(this); } + int get() override { PYBIND11_OVERLOAD(int, TestFactory6, get, /*no args*/); } +}; + +class TestFactory7 { +protected: + int value; + bool alias = false; +public: + TestFactory7(int i) : value{i} { print_created(this, i); } + TestFactory7(TestFactory7 &&f) { print_move_created(this); value = f.value; alias = f.alias; } + TestFactory7(const TestFactory7 &f) { print_copy_created(this); value = f.value; alias = f.alias; } + virtual ~TestFactory7() { print_destroyed(this); } + virtual int get() { return value; } + bool has_alias() { return alias; } +}; +class PyTF7 : public TestFactory7 { +public: + PyTF7(int i) : TestFactory7(i) { alias = true; print_created(this, i); } + PyTF7(PyTF7 &&f) : TestFactory7(std::move(f)) { print_move_created(this); } + PyTF7(const PyTF7 &f) : TestFactory7(f) { print_copy_created(this); } + virtual ~PyTF7() { print_destroyed(this); } + int get() override { PYBIND11_OVERLOAD(int, TestFactory7, get, /*no args*/); } +}; + + +class TestFactoryHelper { +public: + // Non-movable, non-copyable type: + // Return via pointer: + static TestFactory1 *construct1() { return new TestFactory1(); } + // Holder: + static std::unique_ptr construct1(int a) { return std::unique_ptr(new TestFactory1(a)); } + // pointer again + static TestFactory1 *construct1_string(std::string a) { return new TestFactory1(a); } + + // Moveable type: + // pointer: + static TestFactory2 *construct2() { return new TestFactory2(); } + // holder: + static std::unique_ptr construct2(int a) { return std::unique_ptr(new TestFactory2(a)); } + // by value moving: + static TestFactory2 construct2(std::string a) { return TestFactory2(a); } + + // shared_ptr holder type: + // pointer: + static TestFactory3 *construct3() { return new TestFactory3(); } + // holder: + static std::shared_ptr construct3(int a) { return std::shared_ptr(new TestFactory3(a)); } +}; + +TEST_SUBMODULE(factory_constructors, m) { + + // Define various trivial types to allow simpler overload resolution: + py::module m_tag = m.def_submodule("tag"); +#define MAKE_TAG_TYPE(Name) \ + struct Name##_tag {}; \ + py::class_(m_tag, #Name "_tag").def(py::init<>()); \ + m_tag.attr(#Name) = py::cast(Name##_tag{}) + MAKE_TAG_TYPE(pointer); + MAKE_TAG_TYPE(unique_ptr); + MAKE_TAG_TYPE(move); + MAKE_TAG_TYPE(shared_ptr); + MAKE_TAG_TYPE(derived); + MAKE_TAG_TYPE(TF4); + MAKE_TAG_TYPE(TF5); + MAKE_TAG_TYPE(null_ptr); + MAKE_TAG_TYPE(base); + MAKE_TAG_TYPE(invalid_base); + MAKE_TAG_TYPE(alias); + MAKE_TAG_TYPE(unaliasable); + MAKE_TAG_TYPE(mixed); + + // test_init_factory_basic, test_bad_type + py::class_(m, "TestFactory1") + .def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); })) + .def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer + .def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); })) + .def(py::init([](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); })) + .def_readwrite("value", &TestFactory1::value) + ; + py::class_(m, "TestFactory2") + .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); })) + .def(py::init([](unique_ptr_tag, std::string v) { return TestFactoryHelper::construct2(v); })) + .def(py::init([](move_tag) { return TestFactoryHelper::construct2(); })) + .def_readwrite("value", &TestFactory2::value) + ; + + // Stateful & reused: + int c = 1; + auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);}; + + // test_init_factory_basic, test_init_factory_casting + py::class_>(m, "TestFactory3") + .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); })) + .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })) + .def("__init__", [](TestFactory3 &self, std::string v) { new (&self) TestFactory3(v); }) // placement-new ctor + + // factories returning a derived type: + .def(py::init(c4a)) // derived ptr + .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); })) + // derived shared ptr: + .def(py::init([](shared_ptr_tag, TF4_tag, int a) { return std::make_shared(a); })) + .def(py::init([](shared_ptr_tag, TF5_tag, int a) { return std::make_shared(a); })) + + // Returns nullptr: + .def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; })) + + .def_readwrite("value", &TestFactory3::value) + ; + + // test_init_factory_casting + py::class_>(m, "TestFactory4") + .def(py::init(c4a)) // pointer + ; + + // Doesn't need to be registered, but registering makes getting ConstructorStats easier: + py::class_>(m, "TestFactory5"); + + // test_init_factory_alias + // Alias testing + py::class_(m, "TestFactory6") + .def(py::init([](base_tag, int i) { return TestFactory6(i); })) + .def(py::init([](alias_tag, int i) { return PyTF6(i); })) + .def(py::init([](alias_tag, std::string s) { return PyTF6(s); })) + .def(py::init([](alias_tag, pointer_tag, int i) { return new PyTF6(i); })) + .def(py::init([](base_tag, pointer_tag, int i) { return new TestFactory6(i); })) + .def(py::init([](base_tag, alias_tag, pointer_tag, int i) { return (TestFactory6 *) new PyTF6(i); })) + + .def("get", &TestFactory6::get) + .def("has_alias", &TestFactory6::has_alias) + + .def_static("get_cstats", &ConstructorStats::get, py::return_value_policy::reference) + .def_static("get_alias_cstats", &ConstructorStats::get, py::return_value_policy::reference) + ; + + // test_init_factory_dual + // Separate alias constructor testing + py::class_>(m, "TestFactory7") + .def(py::init( + [](int i) { return TestFactory7(i); }, + [](int i) { return PyTF7(i); })) + .def(py::init( + [](pointer_tag, int i) { return new TestFactory7(i); }, + [](pointer_tag, int i) { return new PyTF7(i); })) + .def(py::init( + [](mixed_tag, int i) { return new TestFactory7(i); }, + [](mixed_tag, int i) { return PyTF7(i); })) + .def(py::init( + [](mixed_tag, std::string s) { return TestFactory7((int) s.size()); }, + [](mixed_tag, std::string s) { return new PyTF7((int) s.size()); })) + .def(py::init( + [](base_tag, pointer_tag, int i) { return new TestFactory7(i); }, + [](base_tag, pointer_tag, int i) { return (TestFactory7 *) new PyTF7(i); })) + .def(py::init( + [](alias_tag, pointer_tag, int i) { return new PyTF7(i); }, + [](alias_tag, pointer_tag, int i) { return new PyTF7(10*i); })) + .def(py::init( + [](shared_ptr_tag, base_tag, int i) { return std::make_shared(i); }, + [](shared_ptr_tag, base_tag, int i) { auto *p = new PyTF7(i); return std::shared_ptr(p); })) + .def(py::init( + [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared(i); }, + [](shared_ptr_tag, invalid_base_tag, int i) { return std::make_shared(i); })) // <-- invalid alias factory + + .def("get", &TestFactory7::get) + .def("has_alias", &TestFactory7::has_alias) + + .def_static("get_cstats", &ConstructorStats::get, py::return_value_policy::reference) + .def_static("get_alias_cstats", &ConstructorStats::get, py::return_value_policy::reference) + ; + + // test_placement_new_alternative + // Class with a custom new operator but *without* a placement new operator (issue #948) + class NoPlacementNew { + public: + NoPlacementNew(int i) : i(i) { } + static void *operator new(std::size_t s) { + auto *p = ::operator new(s); + py::print("operator new called, returning", reinterpret_cast(p)); + return p; + } + static void operator delete(void *p) { + py::print("operator delete called on", reinterpret_cast(p)); + ::operator delete(p); + } + int i; + }; + // As of 2.2, `py::init` no longer requires placement new + py::class_(m, "NoPlacementNew") + .def(py::init()) + .def(py::init([]() { return new NoPlacementNew(100); })) + .def_readwrite("i", &NoPlacementNew::i) + ; + + + // test_reallocations + // Class that has verbose operator_new/operator_delete calls + struct NoisyAlloc { + NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); } + NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); } + ~NoisyAlloc() { py::print("~NoisyAlloc()"); } + + static void *operator new(size_t s) { py::print("noisy new"); return ::operator new(s); } + static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; } + static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); } + static void operator delete(void *, void *) { py::print("noisy placement delete"); } +#if defined(_MSC_VER) && _MSC_VER < 1910 + // MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017) + static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); } +#endif + }; + py::class_(m, "NoisyAlloc") + // Since these overloads have the same number of arguments, the dispatcher will try each of + // them until the arguments convert. Thus we can get a pre-allocation here when passing a + // single non-integer: + .def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }) // Regular constructor, runs first, requires preallocation + .def(py::init([](double d) { return new NoisyAlloc(d); })) + + // The two-argument version: first the factory pointer overload. + .def(py::init([](int i, int) { return new NoisyAlloc(i); })) + // Return-by-value: + .def(py::init([](double d, int) { return NoisyAlloc(d); })) + // Old-style placement new init; requires preallocation + .def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); }) + // Requires deallocation of previous overload preallocated value: + .def(py::init([](int i, double) { return new NoisyAlloc(i); })) + // Regular again: requires yet another preallocation + .def("__init__", [](NoisyAlloc &a, int i, std::string) { new (&a) NoisyAlloc(i); }) + ; + + + + + // static_assert testing (the following def's should all fail with appropriate compilation errors): +#if 0 + struct BadF1Base {}; + struct BadF1 : BadF1Base {}; + struct PyBadF1 : BadF1 {}; + py::class_> bf1(m, "BadF1"); + // wrapped factory function must return a compatible pointer, holder, or value + bf1.def(py::init([]() { return 3; })); + // incompatible factory function pointer return type + bf1.def(py::init([]() { static int three = 3; return &three; })); + // incompatible factory function std::shared_ptr return type: cannot convert shared_ptr to holder + // (non-polymorphic base) + bf1.def(py::init([]() { return std::shared_ptr(new BadF1()); })); +#endif +} diff --git a/external/pybind11/tests/test_factory_constructors.py b/external/pybind11/tests/test_factory_constructors.py new file mode 100644 index 0000000..78a3910 --- /dev/null +++ b/external/pybind11/tests/test_factory_constructors.py @@ -0,0 +1,459 @@ +import pytest +import re + +from pybind11_tests import factory_constructors as m +from pybind11_tests.factory_constructors import tag +from pybind11_tests import ConstructorStats + + +def test_init_factory_basic(): + """Tests py::init_factory() wrapper around various ways of returning the object""" + + cstats = [ConstructorStats.get(c) for c in [m.TestFactory1, m.TestFactory2, m.TestFactory3]] + cstats[0].alive() # force gc + n_inst = ConstructorStats.detail_reg_inst() + + x1 = m.TestFactory1(tag.unique_ptr, 3) + assert x1.value == "3" + y1 = m.TestFactory1(tag.pointer) + assert y1.value == "(empty)" + z1 = m.TestFactory1("hi!") + assert z1.value == "hi!" + + assert ConstructorStats.detail_reg_inst() == n_inst + 3 + + x2 = m.TestFactory2(tag.move) + assert x2.value == "(empty2)" + y2 = m.TestFactory2(tag.pointer, 7) + assert y2.value == "7" + z2 = m.TestFactory2(tag.unique_ptr, "hi again") + assert z2.value == "hi again" + + assert ConstructorStats.detail_reg_inst() == n_inst + 6 + + x3 = m.TestFactory3(tag.shared_ptr) + assert x3.value == "(empty3)" + y3 = m.TestFactory3(tag.pointer, 42) + assert y3.value == "42" + z3 = m.TestFactory3("bye") + assert z3.value == "bye" + + with pytest.raises(TypeError) as excinfo: + m.TestFactory3(tag.null_ptr) + assert str(excinfo.value) == "pybind11::init(): factory function returned nullptr" + + assert [i.alive() for i in cstats] == [3, 3, 3] + assert ConstructorStats.detail_reg_inst() == n_inst + 9 + + del x1, y2, y3, z3 + assert [i.alive() for i in cstats] == [2, 2, 1] + assert ConstructorStats.detail_reg_inst() == n_inst + 5 + del x2, x3, y1, z1, z2 + assert [i.alive() for i in cstats] == [0, 0, 0] + assert ConstructorStats.detail_reg_inst() == n_inst + + assert [i.values() for i in cstats] == [ + ["3", "hi!"], + ["7", "hi again"], + ["42", "bye"] + ] + assert [i.default_constructions for i in cstats] == [1, 1, 1] + + +def test_init_factory_signature(msg): + with pytest.raises(TypeError) as excinfo: + m.TestFactory1("invalid", "constructor", "arguments") + assert msg(excinfo.value) == """ + __init__(): incompatible constructor arguments. The following argument types are supported: + 1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) + 2. m.factory_constructors.TestFactory1(arg0: str) + 3. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.pointer_tag) + 4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle) + + Invoked with: 'invalid', 'constructor', 'arguments' + """ # noqa: E501 line too long + + assert msg(m.TestFactory1.__init__.__doc__) == """ + __init__(*args, **kwargs) + Overloaded function. + + 1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None + + 2. __init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None + + 3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None + + 4. __init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None + """ # noqa: E501 line too long + + +def test_init_factory_casting(): + """Tests py::init_factory() wrapper with various upcasting and downcasting returns""" + + cstats = [ConstructorStats.get(c) for c in [m.TestFactory3, m.TestFactory4, m.TestFactory5]] + cstats[0].alive() # force gc + n_inst = ConstructorStats.detail_reg_inst() + + # Construction from derived references: + a = m.TestFactory3(tag.pointer, tag.TF4, 4) + assert a.value == "4" + b = m.TestFactory3(tag.shared_ptr, tag.TF4, 5) + assert b.value == "5" + c = m.TestFactory3(tag.pointer, tag.TF5, 6) + assert c.value == "6" + d = m.TestFactory3(tag.shared_ptr, tag.TF5, 7) + assert d.value == "7" + + assert ConstructorStats.detail_reg_inst() == n_inst + 4 + + # Shared a lambda with TF3: + e = m.TestFactory4(tag.pointer, tag.TF4, 8) + assert e.value == "8" + + assert ConstructorStats.detail_reg_inst() == n_inst + 5 + assert [i.alive() for i in cstats] == [5, 3, 2] + + del a + assert [i.alive() for i in cstats] == [4, 2, 2] + assert ConstructorStats.detail_reg_inst() == n_inst + 4 + + del b, c, e + assert [i.alive() for i in cstats] == [1, 0, 1] + assert ConstructorStats.detail_reg_inst() == n_inst + 1 + + del d + assert [i.alive() for i in cstats] == [0, 0, 0] + assert ConstructorStats.detail_reg_inst() == n_inst + + assert [i.values() for i in cstats] == [ + ["4", "5", "6", "7", "8"], + ["4", "5", "8"], + ["6", "7"] + ] + + +def test_init_factory_alias(): + """Tests py::init_factory() wrapper with value conversions and alias types""" + + cstats = [m.TestFactory6.get_cstats(), m.TestFactory6.get_alias_cstats()] + cstats[0].alive() # force gc + n_inst = ConstructorStats.detail_reg_inst() + + a = m.TestFactory6(tag.base, 1) + assert a.get() == 1 + assert not a.has_alias() + b = m.TestFactory6(tag.alias, "hi there") + assert b.get() == 8 + assert b.has_alias() + c = m.TestFactory6(tag.alias, 3) + assert c.get() == 3 + assert c.has_alias() + d = m.TestFactory6(tag.alias, tag.pointer, 4) + assert d.get() == 4 + assert d.has_alias() + e = m.TestFactory6(tag.base, tag.pointer, 5) + assert e.get() == 5 + assert not e.has_alias() + f = m.TestFactory6(tag.base, tag.alias, tag.pointer, 6) + assert f.get() == 6 + assert f.has_alias() + + assert ConstructorStats.detail_reg_inst() == n_inst + 6 + assert [i.alive() for i in cstats] == [6, 4] + + del a, b, e + assert [i.alive() for i in cstats] == [3, 3] + assert ConstructorStats.detail_reg_inst() == n_inst + 3 + del f, c, d + assert [i.alive() for i in cstats] == [0, 0] + assert ConstructorStats.detail_reg_inst() == n_inst + + class MyTest(m.TestFactory6): + def __init__(self, *args): + m.TestFactory6.__init__(self, *args) + + def get(self): + return -5 + m.TestFactory6.get(self) + + # Return Class by value, moved into new alias: + z = MyTest(tag.base, 123) + assert z.get() == 118 + assert z.has_alias() + + # Return alias by value, moved into new alias: + y = MyTest(tag.alias, "why hello!") + assert y.get() == 5 + assert y.has_alias() + + # Return Class by pointer, moved into new alias then original destroyed: + x = MyTest(tag.base, tag.pointer, 47) + assert x.get() == 42 + assert x.has_alias() + + assert ConstructorStats.detail_reg_inst() == n_inst + 3 + assert [i.alive() for i in cstats] == [3, 3] + del x, y, z + assert [i.alive() for i in cstats] == [0, 0] + assert ConstructorStats.detail_reg_inst() == n_inst + + assert [i.values() for i in cstats] == [ + ["1", "8", "3", "4", "5", "6", "123", "10", "47"], + ["hi there", "3", "4", "6", "move", "123", "why hello!", "move", "47"] + ] + + +def test_init_factory_dual(): + """Tests init factory functions with dual main/alias factory functions""" + from pybind11_tests.factory_constructors import TestFactory7 + + cstats = [TestFactory7.get_cstats(), TestFactory7.get_alias_cstats()] + cstats[0].alive() # force gc + n_inst = ConstructorStats.detail_reg_inst() + + class PythFactory7(TestFactory7): + def get(self): + return 100 + TestFactory7.get(self) + + a1 = TestFactory7(1) + a2 = PythFactory7(2) + assert a1.get() == 1 + assert a2.get() == 102 + assert not a1.has_alias() + assert a2.has_alias() + + b1 = TestFactory7(tag.pointer, 3) + b2 = PythFactory7(tag.pointer, 4) + assert b1.get() == 3 + assert b2.get() == 104 + assert not b1.has_alias() + assert b2.has_alias() + + c1 = TestFactory7(tag.mixed, 5) + c2 = PythFactory7(tag.mixed, 6) + assert c1.get() == 5 + assert c2.get() == 106 + assert not c1.has_alias() + assert c2.has_alias() + + d1 = TestFactory7(tag.base, tag.pointer, 7) + d2 = PythFactory7(tag.base, tag.pointer, 8) + assert d1.get() == 7 + assert d2.get() == 108 + assert not d1.has_alias() + assert d2.has_alias() + + # Both return an alias; the second multiplies the value by 10: + e1 = TestFactory7(tag.alias, tag.pointer, 9) + e2 = PythFactory7(tag.alias, tag.pointer, 10) + assert e1.get() == 9 + assert e2.get() == 200 + assert e1.has_alias() + assert e2.has_alias() + + f1 = TestFactory7(tag.shared_ptr, tag.base, 11) + f2 = PythFactory7(tag.shared_ptr, tag.base, 12) + assert f1.get() == 11 + assert f2.get() == 112 + assert not f1.has_alias() + assert f2.has_alias() + + g1 = TestFactory7(tag.shared_ptr, tag.invalid_base, 13) + assert g1.get() == 13 + assert not g1.has_alias() + with pytest.raises(TypeError) as excinfo: + PythFactory7(tag.shared_ptr, tag.invalid_base, 14) + assert (str(excinfo.value) == + "pybind11::init(): construction failed: returned holder-wrapped instance is not an " + "alias instance") + + assert [i.alive() for i in cstats] == [13, 7] + assert ConstructorStats.detail_reg_inst() == n_inst + 13 + + del a1, a2, b1, d1, e1, e2 + assert [i.alive() for i in cstats] == [7, 4] + assert ConstructorStats.detail_reg_inst() == n_inst + 7 + del b2, c1, c2, d2, f1, f2, g1 + assert [i.alive() for i in cstats] == [0, 0] + assert ConstructorStats.detail_reg_inst() == n_inst + + assert [i.values() for i in cstats] == [ + ["1", "2", "3", "4", "5", "6", "7", "8", "9", "100", "11", "12", "13", "14"], + ["2", "4", "6", "8", "9", "100", "12"] + ] + + +def test_no_placement_new(capture): + """Prior to 2.2, `py::init<...>` relied on the type supporting placement + new; this tests a class without placement new support.""" + with capture: + a = m.NoPlacementNew(123) + + found = re.search(r'^operator new called, returning (\d+)\n$', str(capture)) + assert found + assert a.i == 123 + with capture: + del a + pytest.gc_collect() + assert capture == "operator delete called on " + found.group(1) + + with capture: + b = m.NoPlacementNew() + + found = re.search(r'^operator new called, returning (\d+)\n$', str(capture)) + assert found + assert b.i == 100 + with capture: + del b + pytest.gc_collect() + assert capture == "operator delete called on " + found.group(1) + + +def test_multiple_inheritance(): + class MITest(m.TestFactory1, m.TestFactory2): + def __init__(self): + m.TestFactory1.__init__(self, tag.unique_ptr, 33) + m.TestFactory2.__init__(self, tag.move) + + a = MITest() + assert m.TestFactory1.value.fget(a) == "33" + assert m.TestFactory2.value.fget(a) == "(empty2)" + + +def create_and_destroy(*args): + a = m.NoisyAlloc(*args) + print("---") + del a + pytest.gc_collect() + + +def strip_comments(s): + return re.sub(r'\s+#.*', '', s) + + +def test_reallocations(capture, msg): + """When the constructor is overloaded, previous overloads can require a preallocated value. + This test makes sure that such preallocated values only happen when they might be necessary, + and that they are deallocated properly""" + + pytest.gc_collect() + + with capture: + create_and_destroy(1) + assert msg(capture) == """ + noisy new + noisy placement new + NoisyAlloc(int 1) + --- + ~NoisyAlloc() + noisy delete + """ + with capture: + create_and_destroy(1.5) + assert msg(capture) == strip_comments(""" + noisy new # allocation required to attempt first overload + noisy delete # have to dealloc before considering factory init overload + noisy new # pointer factory calling "new", part 1: allocation + NoisyAlloc(double 1.5) # ... part two, invoking constructor + --- + ~NoisyAlloc() # Destructor + noisy delete # operator delete + """) + + with capture: + create_and_destroy(2, 3) + assert msg(capture) == strip_comments(""" + noisy new # pointer factory calling "new", allocation + NoisyAlloc(int 2) # constructor + --- + ~NoisyAlloc() # Destructor + noisy delete # operator delete + """) + + with capture: + create_and_destroy(2.5, 3) + assert msg(capture) == strip_comments(""" + NoisyAlloc(double 2.5) # construction (local func variable: operator_new not called) + noisy new # return-by-value "new" part 1: allocation + ~NoisyAlloc() # moved-away local func variable destruction + --- + ~NoisyAlloc() # Destructor + noisy delete # operator delete + """) + + with capture: + create_and_destroy(3.5, 4.5) + assert msg(capture) == strip_comments(""" + noisy new # preallocation needed before invoking placement-new overload + noisy placement new # Placement new + NoisyAlloc(double 3.5) # construction + --- + ~NoisyAlloc() # Destructor + noisy delete # operator delete + """) + + with capture: + create_and_destroy(4, 0.5) + assert msg(capture) == strip_comments(""" + noisy new # preallocation needed before invoking placement-new overload + noisy delete # deallocation of preallocated storage + noisy new # Factory pointer allocation + NoisyAlloc(int 4) # factory pointer construction + --- + ~NoisyAlloc() # Destructor + noisy delete # operator delete + """) + + with capture: + create_and_destroy(5, "hi") + assert msg(capture) == strip_comments(""" + noisy new # preallocation needed before invoking first placement new + noisy delete # delete before considering new-style constructor + noisy new # preallocation for second placement new + noisy placement new # Placement new in the second placement new overload + NoisyAlloc(int 5) # construction + --- + ~NoisyAlloc() # Destructor + noisy delete # operator delete + """) + + +@pytest.unsupported_on_py2 +def test_invalid_self(): + """Tests invocation of the pybind-registered base class with an invalid `self` argument. You + can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" + class NotPybindDerived(object): + pass + + # Attempts to initialize with an invalid type passed as `self`: + class BrokenTF1(m.TestFactory1): + def __init__(self, bad): + if bad == 1: + a = m.TestFactory2(tag.pointer, 1) + m.TestFactory1.__init__(a, tag.pointer) + elif bad == 2: + a = NotPybindDerived() + m.TestFactory1.__init__(a, tag.pointer) + + # Same as above, but for a class with an alias: + class BrokenTF6(m.TestFactory6): + def __init__(self, bad): + if bad == 1: + a = m.TestFactory2(tag.pointer, 1) + m.TestFactory6.__init__(a, tag.base, 1) + elif bad == 2: + a = m.TestFactory2(tag.pointer, 1) + m.TestFactory6.__init__(a, tag.alias, 1) + elif bad == 3: + m.TestFactory6.__init__(NotPybindDerived.__new__(NotPybindDerived), tag.base, 1) + elif bad == 4: + m.TestFactory6.__init__(NotPybindDerived.__new__(NotPybindDerived), tag.alias, 1) + + for arg in (1, 2): + with pytest.raises(TypeError) as excinfo: + BrokenTF1(arg) + assert str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument" + + for arg in (1, 2, 3, 4): + with pytest.raises(TypeError) as excinfo: + BrokenTF6(arg) + assert str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument" diff --git a/external/pybind11/tests/test_iostream.cpp b/external/pybind11/tests/test_iostream.cpp new file mode 100644 index 0000000..e67f88a --- /dev/null +++ b/external/pybind11/tests/test_iostream.cpp @@ -0,0 +1,73 @@ +/* + tests/test_iostream.cpp -- Usage of scoped_output_redirect + + Copyright (c) 2017 Henry F. Schreiner + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + + +#include +#include "pybind11_tests.h" +#include + + +void noisy_function(std::string msg, bool flush) { + + std::cout << msg; + if (flush) + std::cout << std::flush; +} + +void noisy_funct_dual(std::string msg, std::string emsg) { + std::cout << msg; + std::cerr << emsg; +} + +TEST_SUBMODULE(iostream, m) { + + add_ostream_redirect(m); + + // test_evals + + m.def("captured_output_default", [](std::string msg) { + py::scoped_ostream_redirect redir; + std::cout << msg << std::flush; + }); + + m.def("captured_output", [](std::string msg) { + py::scoped_ostream_redirect redir(std::cout, py::module::import("sys").attr("stdout")); + std::cout << msg << std::flush; + }); + + m.def("guard_output", &noisy_function, + py::call_guard(), + py::arg("msg"), py::arg("flush")=true); + + m.def("captured_err", [](std::string msg) { + py::scoped_ostream_redirect redir(std::cerr, py::module::import("sys").attr("stderr")); + std::cerr << msg << std::flush; + }); + + m.def("noisy_function", &noisy_function, py::arg("msg"), py::arg("flush") = true); + + m.def("dual_guard", &noisy_funct_dual, + py::call_guard(), + py::arg("msg"), py::arg("emsg")); + + m.def("raw_output", [](std::string msg) { + std::cout << msg << std::flush; + }); + + m.def("raw_err", [](std::string msg) { + std::cerr << msg << std::flush; + }); + + m.def("captured_dual", [](std::string msg, std::string emsg) { + py::scoped_ostream_redirect redirout(std::cout, py::module::import("sys").attr("stdout")); + py::scoped_ostream_redirect redirerr(std::cerr, py::module::import("sys").attr("stderr")); + std::cout << msg << std::flush; + std::cerr << emsg << std::flush; + }); +} diff --git a/external/pybind11/tests/test_iostream.py b/external/pybind11/tests/test_iostream.py new file mode 100644 index 0000000..3364849 --- /dev/null +++ b/external/pybind11/tests/test_iostream.py @@ -0,0 +1,203 @@ +from pybind11_tests import iostream as m +import sys + +from contextlib import contextmanager + +try: + # Python 3 + from io import StringIO +except ImportError: + # Python 2 + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO + +try: + # Python 3.4 + from contextlib import redirect_stdout +except ImportError: + @contextmanager + def redirect_stdout(target): + original = sys.stdout + sys.stdout = target + yield + sys.stdout = original + +try: + # Python 3.5 + from contextlib import redirect_stderr +except ImportError: + @contextmanager + def redirect_stderr(target): + original = sys.stderr + sys.stderr = target + yield + sys.stderr = original + + +def test_captured(capsys): + msg = "I've been redirected to Python, I hope!" + m.captured_output(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == '' + + m.captured_output_default(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == '' + + m.captured_err(msg) + stdout, stderr = capsys.readouterr() + assert stdout == '' + assert stderr == msg + + +def test_guard_capture(capsys): + msg = "I've been redirected to Python, I hope!" + m.guard_output(msg) + stdout, stderr = capsys.readouterr() + assert stdout == msg + assert stderr == '' + + +def test_series_captured(capture): + with capture: + m.captured_output("a") + m.captured_output("b") + assert capture == "ab" + + +def test_flush(capfd): + msg = "(not flushed)" + msg2 = "(flushed)" + + with m.ostream_redirect(): + m.noisy_function(msg, flush=False) + stdout, stderr = capfd.readouterr() + assert stdout == '' + + m.noisy_function(msg2, flush=True) + stdout, stderr = capfd.readouterr() + assert stdout == msg + msg2 + + m.noisy_function(msg, flush=False) + + stdout, stderr = capfd.readouterr() + assert stdout == msg + + +def test_not_captured(capfd): + msg = "Something that should not show up in log" + stream = StringIO() + with redirect_stdout(stream): + m.raw_output(msg) + stdout, stderr = capfd.readouterr() + assert stdout == msg + assert stderr == '' + assert stream.getvalue() == '' + + stream = StringIO() + with redirect_stdout(stream): + m.captured_output(msg) + stdout, stderr = capfd.readouterr() + assert stdout == '' + assert stderr == '' + assert stream.getvalue() == msg + + +def test_err(capfd): + msg = "Something that should not show up in log" + stream = StringIO() + with redirect_stderr(stream): + m.raw_err(msg) + stdout, stderr = capfd.readouterr() + assert stdout == '' + assert stderr == msg + assert stream.getvalue() == '' + + stream = StringIO() + with redirect_stderr(stream): + m.captured_err(msg) + stdout, stderr = capfd.readouterr() + assert stdout == '' + assert stderr == '' + assert stream.getvalue() == msg + + +def test_multi_captured(capfd): + stream = StringIO() + with redirect_stdout(stream): + m.captured_output("a") + m.raw_output("b") + m.captured_output("c") + m.raw_output("d") + stdout, stderr = capfd.readouterr() + assert stdout == 'bd' + assert stream.getvalue() == 'ac' + + +def test_dual(capsys): + m.captured_dual("a", "b") + stdout, stderr = capsys.readouterr() + assert stdout == "a" + assert stderr == "b" + + +def test_redirect(capfd): + msg = "Should not be in log!" + stream = StringIO() + with redirect_stdout(stream): + m.raw_output(msg) + stdout, stderr = capfd.readouterr() + assert stdout == msg + assert stream.getvalue() == '' + + stream = StringIO() + with redirect_stdout(stream): + with m.ostream_redirect(): + m.raw_output(msg) + stdout, stderr = capfd.readouterr() + assert stdout == '' + assert stream.getvalue() == msg + + stream = StringIO() + with redirect_stdout(stream): + m.raw_output(msg) + stdout, stderr = capfd.readouterr() + assert stdout == msg + assert stream.getvalue() == '' + + +def test_redirect_err(capfd): + msg = "StdOut" + msg2 = "StdErr" + + stream = StringIO() + with redirect_stderr(stream): + with m.ostream_redirect(stdout=False): + m.raw_output(msg) + m.raw_err(msg2) + stdout, stderr = capfd.readouterr() + assert stdout == msg + assert stderr == '' + assert stream.getvalue() == msg2 + + +def test_redirect_both(capfd): + msg = "StdOut" + msg2 = "StdErr" + + stream = StringIO() + stream2 = StringIO() + with redirect_stdout(stream): + with redirect_stderr(stream2): + with m.ostream_redirect(): + m.raw_output(msg) + m.raw_err(msg2) + stdout, stderr = capfd.readouterr() + assert stdout == '' + assert stderr == '' + assert stream.getvalue() == msg + assert stream2.getvalue() == msg2 diff --git a/external/pybind11/tests/test_kwargs_and_defaults.cpp b/external/pybind11/tests/test_kwargs_and_defaults.cpp new file mode 100644 index 0000000..165f801 --- /dev/null +++ b/external/pybind11/tests/test_kwargs_and_defaults.cpp @@ -0,0 +1,71 @@ +/* + tests/test_kwargs_and_defaults.cpp -- keyword arguments and default values + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +TEST_SUBMODULE(kwargs_and_defaults, m) { + auto kw_func = [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); }; + + // test_named_arguments + m.def("kw_func0", kw_func); + m.def("kw_func1", kw_func, py::arg("x"), py::arg("y")); + m.def("kw_func2", kw_func, py::arg("x") = 100, py::arg("y") = 200); + m.def("kw_func3", [](const char *) { }, py::arg("data") = std::string("Hello world!")); + + /* A fancier default argument */ + std::vector list{{13, 17}}; + m.def("kw_func4", [](const std::vector &entries) { + std::string ret = "{"; + for (int i : entries) + ret += std::to_string(i) + " "; + ret.back() = '}'; + return ret; + }, py::arg("myList") = list); + + m.def("kw_func_udl", kw_func, "x"_a, "y"_a=300); + m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a=0); + + // test_args_and_kwargs + m.def("args_function", [](py::args args) -> py::tuple { return args; }); + m.def("args_kwargs_function", [](py::args args, py::kwargs kwargs) { + return py::make_tuple(args, kwargs); + }); + + // test_mixed_args_and_kwargs + m.def("mixed_plus_args", [](int i, double j, py::args args) { + return py::make_tuple(i, j, args); + }); + m.def("mixed_plus_kwargs", [](int i, double j, py::kwargs kwargs) { + return py::make_tuple(i, j, kwargs); + }); + auto mixed_plus_both = [](int i, double j, py::args args, py::kwargs kwargs) { + return py::make_tuple(i, j, args, kwargs); + }; + m.def("mixed_plus_args_kwargs", mixed_plus_both); + + m.def("mixed_plus_args_kwargs_defaults", mixed_plus_both, + py::arg("i") = 1, py::arg("j") = 3.14159); + + // pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end. + // Uncomment these to test that the static_assert is indeed working: +// m.def("bad_args1", [](py::args, int) {}); +// m.def("bad_args2", [](py::kwargs, int) {}); +// m.def("bad_args3", [](py::kwargs, py::args) {}); +// m.def("bad_args4", [](py::args, int, py::kwargs) {}); +// m.def("bad_args5", [](py::args, py::kwargs, int) {}); +// m.def("bad_args6", [](py::args, py::args) {}); +// m.def("bad_args7", [](py::kwargs, py::kwargs) {}); + + // test_function_signatures (along with most of the above) + struct KWClass { void foo(int, float) {} }; + py::class_(m, "KWClass") + .def("foo0", &KWClass::foo) + .def("foo1", &KWClass::foo, "x"_a, "y"_a); +} diff --git a/external/pybind11/tests/test_kwargs_and_defaults.py b/external/pybind11/tests/test_kwargs_and_defaults.py new file mode 100644 index 0000000..733fe85 --- /dev/null +++ b/external/pybind11/tests/test_kwargs_and_defaults.py @@ -0,0 +1,107 @@ +import pytest +from pybind11_tests import kwargs_and_defaults as m + + +def test_function_signatures(doc): + assert doc(m.kw_func0) == "kw_func0(arg0: int, arg1: int) -> str" + assert doc(m.kw_func1) == "kw_func1(x: int, y: int) -> str" + assert doc(m.kw_func2) == "kw_func2(x: int=100, y: int=200) -> str" + assert doc(m.kw_func3) == "kw_func3(data: str='Hello world!') -> None" + assert doc(m.kw_func4) == "kw_func4(myList: List[int]=[13, 17]) -> str" + assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int=300) -> str" + assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int=0) -> str" + assert doc(m.args_function) == "args_function(*args) -> tuple" + assert doc(m.args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple" + assert doc(m.KWClass.foo0) == \ + "foo0(self: m.kwargs_and_defaults.KWClass, arg0: int, arg1: float) -> None" + assert doc(m.KWClass.foo1) == \ + "foo1(self: m.kwargs_and_defaults.KWClass, x: int, y: float) -> None" + + +def test_named_arguments(msg): + assert m.kw_func0(5, 10) == "x=5, y=10" + + assert m.kw_func1(5, 10) == "x=5, y=10" + assert m.kw_func1(5, y=10) == "x=5, y=10" + assert m.kw_func1(y=10, x=5) == "x=5, y=10" + + assert m.kw_func2() == "x=100, y=200" + assert m.kw_func2(5) == "x=5, y=200" + assert m.kw_func2(x=5) == "x=5, y=200" + assert m.kw_func2(y=10) == "x=100, y=10" + assert m.kw_func2(5, 10) == "x=5, y=10" + assert m.kw_func2(x=5, y=10) == "x=5, y=10" + + with pytest.raises(TypeError) as excinfo: + # noinspection PyArgumentList + m.kw_func2(x=5, y=10, z=12) + assert excinfo.match( + r'(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$))' + '{3}$') + + assert m.kw_func4() == "{13 17}" + assert m.kw_func4(myList=[1, 2, 3]) == "{1 2 3}" + + assert m.kw_func_udl(x=5, y=10) == "x=5, y=10" + assert m.kw_func_udl_z(x=5) == "x=5, y=0" + + +def test_arg_and_kwargs(): + args = 'arg1_value', 'arg2_value', 3 + assert m.args_function(*args) == args + + args = 'a1', 'a2' + kwargs = dict(arg3='a3', arg4=4) + assert m.args_kwargs_function(*args, **kwargs) == (args, kwargs) + + +def test_mixed_args_and_kwargs(msg): + mpa = m.mixed_plus_args + mpk = m.mixed_plus_kwargs + mpak = m.mixed_plus_args_kwargs + mpakd = m.mixed_plus_args_kwargs_defaults + + assert mpa(1, 2.5, 4, 99.5, None) == (1, 2.5, (4, 99.5, None)) + assert mpa(1, 2.5) == (1, 2.5, ()) + with pytest.raises(TypeError) as excinfo: + assert mpa(1) + assert msg(excinfo.value) == """ + mixed_plus_args(): incompatible function arguments. The following argument types are supported: + 1. (arg0: int, arg1: float, *args) -> tuple + + Invoked with: 1 + """ # noqa: E501 line too long + with pytest.raises(TypeError) as excinfo: + assert mpa() + assert msg(excinfo.value) == """ + mixed_plus_args(): incompatible function arguments. The following argument types are supported: + 1. (arg0: int, arg1: float, *args) -> tuple + + Invoked with: + """ # noqa: E501 line too long + + assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == (-2, 3.5, {'e': 2.71828, 'pi': 3.14159}) + assert mpak(7, 7.7, 7.77, 7.777, 7.7777, minusseven=-7) == ( + 7, 7.7, (7.77, 7.777, 7.7777), {'minusseven': -7}) + assert mpakd() == (1, 3.14159, (), {}) + assert mpakd(3) == (3, 3.14159, (), {}) + assert mpakd(j=2.71828) == (1, 2.71828, (), {}) + assert mpakd(k=42) == (1, 3.14159, (), {'k': 42}) + assert mpakd(1, 1, 2, 3, 5, 8, then=13, followedby=21) == ( + 1, 1, (2, 3, 5, 8), {'then': 13, 'followedby': 21}) + # Arguments specified both positionally and via kwargs should fail: + with pytest.raises(TypeError) as excinfo: + assert mpakd(1, i=1) + assert msg(excinfo.value) == """ + mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: + 1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple + + Invoked with: 1; kwargs: i=1 + """ # noqa: E501 line too long + with pytest.raises(TypeError) as excinfo: + assert mpakd(1, 2, j=1) + assert msg(excinfo.value) == """ + mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: + 1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple + + Invoked with: 1, 2; kwargs: j=1 + """ # noqa: E501 line too long diff --git a/external/pybind11/tests/test_local_bindings.cpp b/external/pybind11/tests/test_local_bindings.cpp new file mode 100644 index 0000000..97c02db --- /dev/null +++ b/external/pybind11/tests/test_local_bindings.cpp @@ -0,0 +1,101 @@ +/* + tests/test_local_bindings.cpp -- tests the py::module_local class feature which makes a class + binding local to the module in which it is defined. + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "local_bindings.h" +#include +#include +#include + +TEST_SUBMODULE(local_bindings, m) { + // test_load_external + m.def("load_external1", [](ExternalType1 &e) { return e.i; }); + m.def("load_external2", [](ExternalType2 &e) { return e.i; }); + + // test_local_bindings + // Register a class with py::module_local: + bind_local(m, "LocalType", py::module_local()) + .def("get3", [](LocalType &t) { return t.i + 3; }) + ; + + m.def("local_value", [](LocalType &l) { return l.i; }); + + // test_nonlocal_failure + // The main pybind11 test module is loaded first, so this registration will succeed (the second + // one, in pybind11_cross_module_tests.cpp, is designed to fail): + bind_local(m, "NonLocalType") + .def(py::init()) + .def("get", [](LocalType &i) { return i.i; }) + ; + + // test_duplicate_local + // py::module_local declarations should be visible across compilation units that get linked together; + // this tries to register a duplicate local. It depends on a definition in test_class.cpp and + // should raise a runtime error from the duplicate definition attempt. If test_class isn't + // available it *also* throws a runtime error (with "test_class not enabled" as value). + m.def("register_local_external", [m]() { + auto main = py::module::import("pybind11_tests"); + if (py::hasattr(main, "class_")) { + bind_local(m, "LocalExternal", py::module_local()); + } + else throw std::runtime_error("test_class not enabled"); + }); + + // test_stl_bind_local + // stl_bind.h binders defaults to py::module_local if the types are local or converting: + py::bind_vector(m, "LocalVec"); + py::bind_map(m, "LocalMap"); + // and global if the type (or one of the types, for the map) is global: + py::bind_vector(m, "NonLocalVec"); + py::bind_map(m, "NonLocalMap"); + + // test_stl_bind_global + // They can, however, be overridden to global using `py::module_local(false)`: + bind_local(m, "NonLocal2"); + py::bind_vector(m, "LocalVec2", py::module_local()); + py::bind_map(m, "NonLocalMap2", py::module_local(false)); + + // test_mixed_local_global + // We try this both with the global type registered first and vice versa (the order shouldn't + // matter). + m.def("register_mixed_global", [m]() { + bind_local(m, "MixedGlobalLocal", py::module_local(false)); + }); + m.def("register_mixed_local", [m]() { + bind_local(m, "MixedLocalGlobal", py::module_local()); + }); + m.def("get_mixed_gl", [](int i) { return MixedGlobalLocal(i); }); + m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); + + // test_internal_locals_differ + m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); }); + + // test_stl_caster_vs_stl_bind + m.def("load_vector_via_caster", [](std::vector v) { + return std::accumulate(v.begin(), v.end(), 0); + }); + + // test_cross_module_calls + m.def("return_self", [](LocalVec *v) { return v; }); + m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); }); + + class Cat : public pets::Pet { public: Cat(std::string name) : Pet(name) {}; }; + py::class_(m, "Pet", py::module_local()) + .def("get_name", &pets::Pet::name); + // Binding for local extending class: + py::class_(m, "Cat") + .def(py::init()); + m.def("pet_name", [](pets::Pet &p) { return p.name(); }); + + py::class_(m, "MixGL").def(py::init()); + m.def("get_gl_value", [](MixGL &o) { return o.i + 10; }); + + py::class_(m, "MixGL2").def(py::init()); +} diff --git a/external/pybind11/tests/test_local_bindings.py b/external/pybind11/tests/test_local_bindings.py new file mode 100644 index 0000000..b3dc361 --- /dev/null +++ b/external/pybind11/tests/test_local_bindings.py @@ -0,0 +1,226 @@ +import pytest + +from pybind11_tests import local_bindings as m + + +def test_load_external(): + """Load a `py::module_local` type that's only registered in an external module""" + import pybind11_cross_module_tests as cm + + assert m.load_external1(cm.ExternalType1(11)) == 11 + assert m.load_external2(cm.ExternalType2(22)) == 22 + + with pytest.raises(TypeError) as excinfo: + assert m.load_external2(cm.ExternalType1(21)) == 21 + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + assert m.load_external1(cm.ExternalType2(12)) == 12 + assert "incompatible function arguments" in str(excinfo.value) + + +def test_local_bindings(): + """Tests that duplicate `py::module_local` class bindings work across modules""" + + # Make sure we can load the second module with the conflicting (but local) definition: + import pybind11_cross_module_tests as cm + + i1 = m.LocalType(5) + assert i1.get() == 4 + assert i1.get3() == 8 + + i2 = cm.LocalType(10) + assert i2.get() == 11 + assert i2.get2() == 12 + + assert not hasattr(i1, 'get2') + assert not hasattr(i2, 'get3') + + # Loading within the local module + assert m.local_value(i1) == 5 + assert cm.local_value(i2) == 10 + + # Cross-module loading works as well (on failure, the type loader looks for + # external module-local converters): + assert m.local_value(i2) == 10 + assert cm.local_value(i1) == 5 + + +def test_nonlocal_failure(): + """Tests that attempting to register a non-local type in multiple modules fails""" + import pybind11_cross_module_tests as cm + + with pytest.raises(RuntimeError) as excinfo: + cm.register_nonlocal() + assert str(excinfo.value) == 'generic_type: type "NonLocalType" is already registered!' + + +def test_duplicate_local(): + """Tests expected failure when registering a class twice with py::local in the same module""" + with pytest.raises(RuntimeError) as excinfo: + m.register_local_external() + import pybind11_tests + assert str(excinfo.value) == ( + 'generic_type: type "LocalExternal" is already registered!' + if hasattr(pybind11_tests, 'class_') else 'test_class not enabled') + + +def test_stl_bind_local(): + import pybind11_cross_module_tests as cm + + v1, v2 = m.LocalVec(), cm.LocalVec() + v1.append(m.LocalType(1)) + v1.append(m.LocalType(2)) + v2.append(cm.LocalType(1)) + v2.append(cm.LocalType(2)) + + # Cross module value loading: + v1.append(cm.LocalType(3)) + v2.append(m.LocalType(3)) + + assert [i.get() for i in v1] == [0, 1, 2] + assert [i.get() for i in v2] == [2, 3, 4] + + v3, v4 = m.NonLocalVec(), cm.NonLocalVec2() + v3.append(m.NonLocalType(1)) + v3.append(m.NonLocalType(2)) + v4.append(m.NonLocal2(3)) + v4.append(m.NonLocal2(4)) + + assert [i.get() for i in v3] == [1, 2] + assert [i.get() for i in v4] == [13, 14] + + d1, d2 = m.LocalMap(), cm.LocalMap() + d1["a"] = v1[0] + d1["b"] = v1[1] + d2["c"] = v2[0] + d2["d"] = v2[1] + assert {i: d1[i].get() for i in d1} == {'a': 0, 'b': 1} + assert {i: d2[i].get() for i in d2} == {'c': 2, 'd': 3} + + +def test_stl_bind_global(): + import pybind11_cross_module_tests as cm + + with pytest.raises(RuntimeError) as excinfo: + cm.register_nonlocal_map() + assert str(excinfo.value) == 'generic_type: type "NonLocalMap" is already registered!' + + with pytest.raises(RuntimeError) as excinfo: + cm.register_nonlocal_vec() + assert str(excinfo.value) == 'generic_type: type "NonLocalVec" is already registered!' + + with pytest.raises(RuntimeError) as excinfo: + cm.register_nonlocal_map2() + assert str(excinfo.value) == 'generic_type: type "NonLocalMap2" is already registered!' + + +def test_mixed_local_global(): + """Local types take precedence over globally registered types: a module with a `module_local` + type can be registered even if the type is already registered globally. With the module, + casting will go to the local type; outside the module casting goes to the global type.""" + import pybind11_cross_module_tests as cm + m.register_mixed_global() + m.register_mixed_local() + + a = [] + a.append(m.MixedGlobalLocal(1)) + a.append(m.MixedLocalGlobal(2)) + a.append(m.get_mixed_gl(3)) + a.append(m.get_mixed_lg(4)) + + assert [x.get() for x in a] == [101, 1002, 103, 1004] + + cm.register_mixed_global_local() + cm.register_mixed_local_global() + a.append(m.MixedGlobalLocal(5)) + a.append(m.MixedLocalGlobal(6)) + a.append(cm.MixedGlobalLocal(7)) + a.append(cm.MixedLocalGlobal(8)) + a.append(m.get_mixed_gl(9)) + a.append(m.get_mixed_lg(10)) + a.append(cm.get_mixed_gl(11)) + a.append(cm.get_mixed_lg(12)) + + assert [x.get() for x in a] == \ + [101, 1002, 103, 1004, 105, 1006, 207, 2008, 109, 1010, 211, 2012] + + +def test_internal_locals_differ(): + """Makes sure the internal local type map differs across the two modules""" + import pybind11_cross_module_tests as cm + assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() + + +def test_stl_caster_vs_stl_bind(msg): + """One module uses a generic vector caster from `` while the other + exports `std::vector` via `py:bind_vector` and `py::module_local`""" + import pybind11_cross_module_tests as cm + + v1 = cm.VectorInt([1, 2, 3]) + assert m.load_vector_via_caster(v1) == 6 + assert cm.load_vector_via_binding(v1) == 6 + + v2 = [1, 2, 3] + assert m.load_vector_via_caster(v2) == 6 + with pytest.raises(TypeError) as excinfo: + cm.load_vector_via_binding(v2) == 6 + assert msg(excinfo.value) == """ + load_vector_via_binding(): incompatible function arguments. The following argument types are supported: + 1. (arg0: pybind11_cross_module_tests.VectorInt) -> int + + Invoked with: [1, 2, 3] + """ # noqa: E501 line too long + + +def test_cross_module_calls(): + import pybind11_cross_module_tests as cm + + v1 = m.LocalVec() + v1.append(m.LocalType(1)) + v2 = cm.LocalVec() + v2.append(cm.LocalType(2)) + + # Returning the self pointer should get picked up as returning an existing + # instance (even when that instance is of a foreign, non-local type). + assert m.return_self(v1) is v1 + assert cm.return_self(v2) is v2 + assert m.return_self(v2) is v2 + assert cm.return_self(v1) is v1 + + assert m.LocalVec is not cm.LocalVec + # Returning a copy, on the other hand, always goes to the local type, + # regardless of where the source type came from. + assert type(m.return_copy(v1)) is m.LocalVec + assert type(m.return_copy(v2)) is m.LocalVec + assert type(cm.return_copy(v1)) is cm.LocalVec + assert type(cm.return_copy(v2)) is cm.LocalVec + + # Test the example given in the documentation (which also tests inheritance casting): + mycat = m.Cat("Fluffy") + mydog = cm.Dog("Rover") + assert mycat.get_name() == "Fluffy" + assert mydog.name() == "Rover" + assert m.Cat.__base__.__name__ == "Pet" + assert cm.Dog.__base__.__name__ == "Pet" + assert m.Cat.__base__ is not cm.Dog.__base__ + assert m.pet_name(mycat) == "Fluffy" + assert m.pet_name(mydog) == "Rover" + assert cm.pet_name(mycat) == "Fluffy" + assert cm.pet_name(mydog) == "Rover" + + assert m.MixGL is not cm.MixGL + a = m.MixGL(1) + b = cm.MixGL(2) + assert m.get_gl_value(a) == 11 + assert m.get_gl_value(b) == 12 + assert cm.get_gl_value(a) == 101 + assert cm.get_gl_value(b) == 102 + + c, d = m.MixGL2(3), cm.MixGL2(4) + with pytest.raises(TypeError) as excinfo: + m.get_gl_value(c) + assert "incompatible function arguments" in str(excinfo) + with pytest.raises(TypeError) as excinfo: + m.get_gl_value(d) + assert "incompatible function arguments" in str(excinfo) diff --git a/external/pybind11/tests/test_methods_and_attributes.cpp b/external/pybind11/tests/test_methods_and_attributes.cpp new file mode 100644 index 0000000..cd15869 --- /dev/null +++ b/external/pybind11/tests/test_methods_and_attributes.cpp @@ -0,0 +1,446 @@ +/* + tests/test_methods_and_attributes.cpp -- constructors, deconstructors, attribute access, + __str__, argument and return value conventions + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" + +class ExampleMandA { +public: + ExampleMandA() { print_default_created(this); } + ExampleMandA(int value) : value(value) { print_created(this, value); } + ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); } + ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); } + ~ExampleMandA() { print_destroyed(this); } + + std::string toString() { + return "ExampleMandA[value=" + std::to_string(value) + "]"; + } + + void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; } + void operator=(ExampleMandA &&e) { print_move_assigned(this); value = e.value; } + + void add1(ExampleMandA other) { value += other.value; } // passing by value + void add2(ExampleMandA &other) { value += other.value; } // passing by reference + void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference + void add4(ExampleMandA *other) { value += other->value; } // passing by pointer + void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer + + void add6(int other) { value += other; } // passing by value + void add7(int &other) { value += other; } // passing by reference + void add8(const int &other) { value += other; } // passing by const reference + void add9(int *other) { value += *other; } // passing by pointer + void add10(const int *other) { value += *other; } // passing by const pointer + + ExampleMandA self1() { return *this; } // return by value + ExampleMandA &self2() { return *this; } // return by reference + const ExampleMandA &self3() { return *this; } // return by const reference + ExampleMandA *self4() { return this; } // return by pointer + const ExampleMandA *self5() { return this; } // return by const pointer + + int internal1() { return value; } // return by value + int &internal2() { return value; } // return by reference + const int &internal3() { return value; } // return by const reference + int *internal4() { return &value; } // return by pointer + const int *internal5() { return &value; } // return by const pointer + + py::str overloaded() { return "()"; } + py::str overloaded(int) { return "(int)"; } + py::str overloaded(int, float) { return "(int, float)"; } + py::str overloaded(float, int) { return "(float, int)"; } + py::str overloaded(int, int) { return "(int, int)"; } + py::str overloaded(float, float) { return "(float, float)"; } + py::str overloaded(int) const { return "(int) const"; } + py::str overloaded(int, float) const { return "(int, float) const"; } + py::str overloaded(float, int) const { return "(float, int) const"; } + py::str overloaded(int, int) const { return "(int, int) const"; } + py::str overloaded(float, float) const { return "(float, float) const"; } + + static py::str overloaded(float) { return "static float"; } + + int value = 0; +}; + +struct TestProperties { + int value = 1; + static int static_value; + + int get() const { return value; } + void set(int v) { value = v; } + + static int static_get() { return static_value; } + static void static_set(int v) { static_value = v; } +}; +int TestProperties::static_value = 1; + +struct TestPropertiesOverride : TestProperties { + int value = 99; + static int static_value; +}; +int TestPropertiesOverride::static_value = 99; + +struct TestPropRVP { + UserType v1{1}; + UserType v2{1}; + static UserType sv1; + static UserType sv2; + + const UserType &get1() const { return v1; } + const UserType &get2() const { return v2; } + UserType get_rvalue() const { return v2; } + void set1(int v) { v1.set(v); } + void set2(int v) { v2.set(v); } +}; +UserType TestPropRVP::sv1(1); +UserType TestPropRVP::sv2(1); + +// py::arg/py::arg_v testing: these arguments just record their argument when invoked +class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; }; +class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; }; +class ArgAlwaysConverts { }; +namespace pybind11 { namespace detail { +template <> struct type_caster { +public: + PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); + + bool load(handle src, bool convert) { + value.arg = "loading ArgInspector1 argument " + + std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + "Argument value = " + (std::string) str(src); + return true; + } + + static handle cast(const ArgInspector1 &src, return_value_policy, handle) { + return str(src.arg).release(); + } +}; +template <> struct type_caster { +public: + PYBIND11_TYPE_CASTER(ArgInspector2, _("ArgInspector2")); + + bool load(handle src, bool convert) { + value.arg = "loading ArgInspector2 argument " + + std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + "Argument value = " + (std::string) str(src); + return true; + } + + static handle cast(const ArgInspector2 &src, return_value_policy, handle) { + return str(src.arg).release(); + } +}; +template <> struct type_caster { +public: + PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _("ArgAlwaysConverts")); + + bool load(handle, bool convert) { + return convert; + } + + static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { + return py::none().release(); + } +}; +}} + +// test_custom_caster_destruction +class DestructionTester { +public: + DestructionTester() { print_default_created(this); } + ~DestructionTester() { print_destroyed(this); } + DestructionTester(const DestructionTester &) { print_copy_created(this); } + DestructionTester(DestructionTester &&) { print_move_created(this); } + DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; } + DestructionTester &operator=(DestructionTester &&) { print_move_assigned(this); return *this; } +}; +namespace pybind11 { namespace detail { +template <> struct type_caster { + PYBIND11_TYPE_CASTER(DestructionTester, _("DestructionTester")); + bool load(handle, bool) { return true; } + + static handle cast(const DestructionTester &, return_value_policy, handle) { + return py::bool_(true).release(); + } +}; +}} + +// Test None-allowed py::arg argument policy +class NoneTester { public: int answer = 42; }; +int none1(const NoneTester &obj) { return obj.answer; } +int none2(NoneTester *obj) { return obj ? obj->answer : -1; } +int none3(std::shared_ptr &obj) { return obj ? obj->answer : -1; } +int none4(std::shared_ptr *obj) { return obj && *obj ? (*obj)->answer : -1; } +int none5(std::shared_ptr obj) { return obj ? obj->answer : -1; } + +struct StrIssue { + int val = -1; + + StrIssue() = default; + StrIssue(int i) : val{i} {} +}; + +// Issues #854, #910: incompatible function args when member function/pointer is in unregistered base class +class UnregisteredBase { +public: + void do_nothing() const {} + void increase_value() { rw_value++; ro_value += 0.25; } + void set_int(int v) { rw_value = v; } + int get_int() const { return rw_value; } + double get_double() const { return ro_value; } + int rw_value = 42; + double ro_value = 1.25; +}; +class RegisteredDerived : public UnregisteredBase { +public: + using UnregisteredBase::UnregisteredBase; + double sum() const { return rw_value + ro_value; } +}; + +TEST_SUBMODULE(methods_and_attributes, m) { + // test_methods_and_attributes + py::class_ emna(m, "ExampleMandA"); + emna.def(py::init<>()) + .def(py::init()) + .def(py::init()) + .def("add1", &ExampleMandA::add1) + .def("add2", &ExampleMandA::add2) + .def("add3", &ExampleMandA::add3) + .def("add4", &ExampleMandA::add4) + .def("add5", &ExampleMandA::add5) + .def("add6", &ExampleMandA::add6) + .def("add7", &ExampleMandA::add7) + .def("add8", &ExampleMandA::add8) + .def("add9", &ExampleMandA::add9) + .def("add10", &ExampleMandA::add10) + .def("self1", &ExampleMandA::self1) + .def("self2", &ExampleMandA::self2) + .def("self3", &ExampleMandA::self3) + .def("self4", &ExampleMandA::self4) + .def("self5", &ExampleMandA::self5) + .def("internal1", &ExampleMandA::internal1) + .def("internal2", &ExampleMandA::internal2) + .def("internal3", &ExampleMandA::internal3) + .def("internal4", &ExampleMandA::internal4) + .def("internal5", &ExampleMandA::internal5) +#if defined(PYBIND11_OVERLOAD_CAST) + .def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded_float", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) +#else + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_float", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) +#endif + // test_no_mixed_overloads + // Raise error if trying to mix static/non-static overloads on the same name: + .def_static("add_mixed_overloads1", []() { + auto emna = py::reinterpret_borrow>(py::module::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); + emna.def ("overload_mixed1", static_cast(&ExampleMandA::overloaded)) + .def_static("overload_mixed1", static_cast(&ExampleMandA::overloaded)); + }) + .def_static("add_mixed_overloads2", []() { + auto emna = py::reinterpret_borrow>(py::module::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); + emna.def_static("overload_mixed2", static_cast(&ExampleMandA::overloaded)) + .def ("overload_mixed2", static_cast(&ExampleMandA::overloaded)); + }) + .def("__str__", &ExampleMandA::toString) + .def_readwrite("value", &ExampleMandA::value); + + // test_copy_method + // Issue #443: can't call copied methods in Python 3 + emna.attr("add2b") = emna.attr("add2"); + + // test_properties, test_static_properties, test_static_cls + py::class_(m, "TestProperties") + .def(py::init<>()) + .def_readonly("def_readonly", &TestProperties::value) + .def_readwrite("def_readwrite", &TestProperties::value) + .def_property_readonly("def_property_readonly", &TestProperties::get) + .def_property("def_property", &TestProperties::get, &TestProperties::set) + .def_readonly_static("def_readonly_static", &TestProperties::static_value) + .def_readwrite_static("def_readwrite_static", &TestProperties::static_value) + .def_property_readonly_static("def_property_readonly_static", + [](py::object) { return TestProperties::static_get(); }) + .def_property_static("def_property_static", + [](py::object) { return TestProperties::static_get(); }, + [](py::object, int v) { TestProperties::static_set(v); }) + .def_property_static("static_cls", + [](py::object cls) { return cls; }, + [](py::object cls, py::function f) { f(cls); }); + + py::class_(m, "TestPropertiesOverride") + .def(py::init<>()) + .def_readonly("def_readonly", &TestPropertiesOverride::value) + .def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value); + + auto static_get1 = [](py::object) -> const UserType & { return TestPropRVP::sv1; }; + auto static_get2 = [](py::object) -> const UserType & { return TestPropRVP::sv2; }; + auto static_set1 = [](py::object, int v) { TestPropRVP::sv1.set(v); }; + auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.set(v); }; + auto rvp_copy = py::return_value_policy::copy; + + // test_property_return_value_policies + py::class_(m, "TestPropRVP") + .def(py::init<>()) + .def_property_readonly("ro_ref", &TestPropRVP::get1) + .def_property_readonly("ro_copy", &TestPropRVP::get2, rvp_copy) + .def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy)) + .def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1) + .def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy) + .def_property("rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2) + .def_property_readonly_static("static_ro_ref", static_get1) + .def_property_readonly_static("static_ro_copy", static_get2, rvp_copy) + .def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy)) + .def_property_static("static_rw_ref", static_get1, static_set1) + .def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy) + .def_property_static("static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2) + // test_property_rvalue_policy + .def_property_readonly("rvalue", &TestPropRVP::get_rvalue) + .def_property_readonly_static("static_rvalue", [](py::object) { return UserType(1); }); + + // test_metaclass_override + struct MetaclassOverride { }; + py::class_(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type)) + .def_property_readonly_static("readonly", [](py::object) { return 1; }); + +#if !defined(PYPY_VERSION) + // test_dynamic_attributes + class DynamicClass { + public: + DynamicClass() { print_default_created(this); } + ~DynamicClass() { print_destroyed(this); } + }; + py::class_(m, "DynamicClass", py::dynamic_attr()) + .def(py::init()); + + class CppDerivedDynamicClass : public DynamicClass { }; + py::class_(m, "CppDerivedDynamicClass") + .def(py::init()); +#endif + + // test_noconvert_args + // + // Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass + // fail so that our call always ends up happening via the second dispatch (the one that allows + // some conversion). + class ArgInspector { + public: + ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; } + std::string g(ArgInspector1 a, const ArgInspector1 &b, int c, ArgInspector2 *d, ArgAlwaysConverts) { + return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg; + } + static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; } + }; + py::class_(m, "ArgInspector") + .def(py::init<>()) + .def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts()) + .def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts()) + .def_static("h", &ArgInspector::h, py::arg().noconvert(), py::arg() = ArgAlwaysConverts()) + ; + m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b, ArgAlwaysConverts) { return a.arg + "\n" + b.arg; }, + py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg() = ArgAlwaysConverts()); + + m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); + m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); + m.def("ints_preferred", [](int i) { return i / 2; }, py::arg("i")); + m.def("ints_only", [](int i) { return i / 2; }, py::arg("i").noconvert()); + + // test_bad_arg_default + // Issue/PR #648: bad arg default debugging output +#if !defined(NDEBUG) + m.attr("debug_enabled") = true; +#else + m.attr("debug_enabled") = false; +#endif + m.def("bad_arg_def_named", []{ + auto m = py::module::import("pybind11_tests"); + m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg("a") = UnregisteredType()); + }); + m.def("bad_arg_def_unnamed", []{ + auto m = py::module::import("pybind11_tests"); + m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg() = UnregisteredType()); + }); + + // test_accepts_none + py::class_>(m, "NoneTester") + .def(py::init<>()); + m.def("no_none1", &none1, py::arg().none(false)); + m.def("no_none2", &none2, py::arg().none(false)); + m.def("no_none3", &none3, py::arg().none(false)); + m.def("no_none4", &none4, py::arg().none(false)); + m.def("no_none5", &none5, py::arg().none(false)); + m.def("ok_none1", &none1); + m.def("ok_none2", &none2, py::arg().none(true)); + m.def("ok_none3", &none3); + m.def("ok_none4", &none4, py::arg().none(true)); + m.def("ok_none5", &none5); + + // test_str_issue + // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid + py::class_(m, "StrIssue") + .def(py::init()) + .def(py::init<>()) + .def("__str__", [](const StrIssue &si) { + return "StrIssue[" + std::to_string(si.val) + "]"; } + ); + + // test_unregistered_base_implementations + // + // Issues #854/910: incompatible function args when member function/pointer is in unregistered + // base class The methods and member pointers below actually resolve to members/pointers in + // UnregisteredBase; before this test/fix they would be registered via lambda with a first + // argument of an unregistered type, and thus uncallable. + py::class_(m, "RegisteredDerived") + .def(py::init<>()) + .def("do_nothing", &RegisteredDerived::do_nothing) + .def("increase_value", &RegisteredDerived::increase_value) + .def_readwrite("rw_value", &RegisteredDerived::rw_value) + .def_readonly("ro_value", &RegisteredDerived::ro_value) + // These should trigger a static_assert if uncommented + //.def_readwrite("fails", &UserType::value) // should trigger a static_assert if uncommented + //.def_readonly("fails", &UserType::value) // should trigger a static_assert if uncommented + .def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int) + .def_property_readonly("ro_value_prop", &RegisteredDerived::get_double) + // This one is in the registered class: + .def("sum", &RegisteredDerived::sum) + ; + + using Adapted = decltype(py::method_adaptor(&RegisteredDerived::do_nothing)); + static_assert(std::is_same::value, ""); + + // test_custom_caster_destruction + // Test that `take_ownership` works on types with a custom type caster when given a pointer + + // default policy: don't take ownership: + m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; }); + + m.def("custom_caster_destroy", []() { return new DestructionTester(); }, + py::return_value_policy::take_ownership); // Takes ownership: destroy when finished + m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, + py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) + m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); +} diff --git a/external/pybind11/tests/test_methods_and_attributes.py b/external/pybind11/tests/test_methods_and_attributes.py new file mode 100644 index 0000000..9fd9cb7 --- /dev/null +++ b/external/pybind11/tests/test_methods_and_attributes.py @@ -0,0 +1,476 @@ +import pytest +from pybind11_tests import methods_and_attributes as m +from pybind11_tests import ConstructorStats + + +def test_methods_and_attributes(): + instance1 = m.ExampleMandA() + instance2 = m.ExampleMandA(32) + + instance1.add1(instance2) + instance1.add2(instance2) + instance1.add3(instance2) + instance1.add4(instance2) + instance1.add5(instance2) + instance1.add6(32) + instance1.add7(32) + instance1.add8(32) + instance1.add9(32) + instance1.add10(32) + + assert str(instance1) == "ExampleMandA[value=320]" + assert str(instance2) == "ExampleMandA[value=32]" + assert str(instance1.self1()) == "ExampleMandA[value=320]" + assert str(instance1.self2()) == "ExampleMandA[value=320]" + assert str(instance1.self3()) == "ExampleMandA[value=320]" + assert str(instance1.self4()) == "ExampleMandA[value=320]" + assert str(instance1.self5()) == "ExampleMandA[value=320]" + + assert instance1.internal1() == 320 + assert instance1.internal2() == 320 + assert instance1.internal3() == 320 + assert instance1.internal4() == 320 + assert instance1.internal5() == 320 + + assert instance1.overloaded() == "()" + assert instance1.overloaded(0) == "(int)" + assert instance1.overloaded(1, 1.0) == "(int, float)" + assert instance1.overloaded(2.0, 2) == "(float, int)" + assert instance1.overloaded(3, 3) == "(int, int)" + assert instance1.overloaded(4., 4.) == "(float, float)" + assert instance1.overloaded_const(-3) == "(int) const" + assert instance1.overloaded_const(5, 5.0) == "(int, float) const" + assert instance1.overloaded_const(6.0, 6) == "(float, int) const" + assert instance1.overloaded_const(7, 7) == "(int, int) const" + assert instance1.overloaded_const(8., 8.) == "(float, float) const" + assert instance1.overloaded_float(1, 1) == "(float, float)" + assert instance1.overloaded_float(1, 1.) == "(float, float)" + assert instance1.overloaded_float(1., 1) == "(float, float)" + assert instance1.overloaded_float(1., 1.) == "(float, float)" + + assert instance1.value == 320 + instance1.value = 100 + assert str(instance1) == "ExampleMandA[value=100]" + + cstats = ConstructorStats.get(m.ExampleMandA) + assert cstats.alive() == 2 + del instance1, instance2 + assert cstats.alive() == 0 + assert cstats.values() == ["32"] + assert cstats.default_constructions == 1 + assert cstats.copy_constructions == 3 + assert cstats.move_constructions >= 1 + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + +def test_copy_method(): + """Issue #443: calling copied methods fails in Python 3""" + + m.ExampleMandA.add2c = m.ExampleMandA.add2 + m.ExampleMandA.add2d = m.ExampleMandA.add2b + a = m.ExampleMandA(123) + assert a.value == 123 + a.add2(m.ExampleMandA(-100)) + assert a.value == 23 + a.add2b(m.ExampleMandA(20)) + assert a.value == 43 + a.add2c(m.ExampleMandA(6)) + assert a.value == 49 + a.add2d(m.ExampleMandA(-7)) + assert a.value == 42 + + +def test_properties(): + instance = m.TestProperties() + + assert instance.def_readonly == 1 + with pytest.raises(AttributeError): + instance.def_readonly = 2 + + instance.def_readwrite = 2 + assert instance.def_readwrite == 2 + + assert instance.def_property_readonly == 2 + with pytest.raises(AttributeError): + instance.def_property_readonly = 3 + + instance.def_property = 3 + assert instance.def_property == 3 + + +def test_static_properties(): + assert m.TestProperties.def_readonly_static == 1 + with pytest.raises(AttributeError) as excinfo: + m.TestProperties.def_readonly_static = 2 + assert "can't set attribute" in str(excinfo) + + m.TestProperties.def_readwrite_static = 2 + assert m.TestProperties.def_readwrite_static == 2 + + assert m.TestProperties.def_property_readonly_static == 2 + with pytest.raises(AttributeError) as excinfo: + m.TestProperties.def_property_readonly_static = 3 + assert "can't set attribute" in str(excinfo) + + m.TestProperties.def_property_static = 3 + assert m.TestProperties.def_property_static == 3 + + # Static property read and write via instance + instance = m.TestProperties() + + m.TestProperties.def_readwrite_static = 0 + assert m.TestProperties.def_readwrite_static == 0 + assert instance.def_readwrite_static == 0 + + instance.def_readwrite_static = 2 + assert m.TestProperties.def_readwrite_static == 2 + assert instance.def_readwrite_static == 2 + + # It should be possible to override properties in derived classes + assert m.TestPropertiesOverride().def_readonly == 99 + assert m.TestPropertiesOverride.def_readonly_static == 99 + + +def test_static_cls(): + """Static property getter and setters expect the type object as the their only argument""" + + instance = m.TestProperties() + assert m.TestProperties.static_cls is m.TestProperties + assert instance.static_cls is m.TestProperties + + def check_self(self): + assert self is m.TestProperties + + m.TestProperties.static_cls = check_self + instance.static_cls = check_self + + +def test_metaclass_override(): + """Overriding pybind11's default metaclass changes the behavior of `static_property`""" + + assert type(m.ExampleMandA).__name__ == "pybind11_type" + assert type(m.MetaclassOverride).__name__ == "type" + + assert m.MetaclassOverride.readonly == 1 + assert type(m.MetaclassOverride.__dict__["readonly"]).__name__ == "pybind11_static_property" + + # Regular `type` replaces the property instead of calling `__set__()` + m.MetaclassOverride.readonly = 2 + assert m.MetaclassOverride.readonly == 2 + assert isinstance(m.MetaclassOverride.__dict__["readonly"], int) + + +def test_no_mixed_overloads(): + from pybind11_tests import debug_enabled + + with pytest.raises(RuntimeError) as excinfo: + m.ExampleMandA.add_mixed_overloads1() + assert (str(excinfo.value) == + "overloading a method with both static and instance methods is not supported; " + + ("compile in debug mode for more details" if not debug_enabled else + "error while attempting to bind static method ExampleMandA.overload_mixed1" + "(arg0: float) -> str") + ) + + with pytest.raises(RuntimeError) as excinfo: + m.ExampleMandA.add_mixed_overloads2() + assert (str(excinfo.value) == + "overloading a method with both static and instance methods is not supported; " + + ("compile in debug mode for more details" if not debug_enabled else + "error while attempting to bind instance method ExampleMandA.overload_mixed2" + "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)" + " -> str") + ) + + +@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) +def test_property_return_value_policies(access): + if not access.startswith("static"): + obj = m.TestPropRVP() + else: + obj = m.TestPropRVP + + ref = getattr(obj, access + "_ref") + assert ref.value == 1 + ref.value = 2 + assert getattr(obj, access + "_ref").value == 2 + ref.value = 1 # restore original value for static properties + + copy = getattr(obj, access + "_copy") + assert copy.value == 1 + copy.value = 2 + assert getattr(obj, access + "_copy").value == 1 + + copy = getattr(obj, access + "_func") + assert copy.value == 1 + copy.value = 2 + assert getattr(obj, access + "_func").value == 1 + + +def test_property_rvalue_policy(): + """When returning an rvalue, the return value policy is automatically changed from + `reference(_internal)` to `move`. The following would not work otherwise.""" + + instance = m.TestPropRVP() + o = instance.rvalue + assert o.value == 1 + + os = m.TestPropRVP.static_rvalue + assert os.value == 1 + + +# https://bitbucket.org/pypy/pypy/issues/2447 +@pytest.unsupported_on_pypy +def test_dynamic_attributes(): + instance = m.DynamicClass() + assert not hasattr(instance, "foo") + assert "foo" not in dir(instance) + + # Dynamically add attribute + instance.foo = 42 + assert hasattr(instance, "foo") + assert instance.foo == 42 + assert "foo" in dir(instance) + + # __dict__ should be accessible and replaceable + assert "foo" in instance.__dict__ + instance.__dict__ = {"bar": True} + assert not hasattr(instance, "foo") + assert hasattr(instance, "bar") + + with pytest.raises(TypeError) as excinfo: + instance.__dict__ = [] + assert str(excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'" + + cstats = ConstructorStats.get(m.DynamicClass) + assert cstats.alive() == 1 + del instance + assert cstats.alive() == 0 + + # Derived classes should work as well + class PythonDerivedDynamicClass(m.DynamicClass): + pass + + for cls in m.CppDerivedDynamicClass, PythonDerivedDynamicClass: + derived = cls() + derived.foobar = 100 + assert derived.foobar == 100 + + assert cstats.alive() == 1 + del derived + assert cstats.alive() == 0 + + +# https://bitbucket.org/pypy/pypy/issues/2447 +@pytest.unsupported_on_pypy +def test_cyclic_gc(): + # One object references itself + instance = m.DynamicClass() + instance.circular_reference = instance + + cstats = ConstructorStats.get(m.DynamicClass) + assert cstats.alive() == 1 + del instance + assert cstats.alive() == 0 + + # Two object reference each other + i1 = m.DynamicClass() + i2 = m.DynamicClass() + i1.cycle = i2 + i2.cycle = i1 + + assert cstats.alive() == 2 + del i1, i2 + assert cstats.alive() == 0 + + +def test_noconvert_args(msg): + a = m.ArgInspector() + assert msg(a.f("hi")) == """ + loading ArgInspector1 argument WITH conversion allowed. Argument value = hi + """ + assert msg(a.g("this is a", "this is b")) == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 13 + loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) + """ # noqa: E501 line too long + assert msg(a.g("this is a", "this is b", 42)) == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 42 + loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) + """ # noqa: E501 line too long + assert msg(a.g("this is a", "this is b", 42, "this is d")) == """ + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a + loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b + 42 + loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d + """ + assert (a.h("arg 1") == + "loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1") + assert msg(m.arg_inspect_func("A1", "A2")) == """ + loading ArgInspector2 argument WITH conversion allowed. Argument value = A1 + loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2 + """ + + assert m.floats_preferred(4) == 2.0 + assert m.floats_only(4.0) == 2.0 + with pytest.raises(TypeError) as excinfo: + m.floats_only(4) + assert msg(excinfo.value) == """ + floats_only(): incompatible function arguments. The following argument types are supported: + 1. (f: float) -> float + + Invoked with: 4 + """ + + assert m.ints_preferred(4) == 2 + assert m.ints_preferred(True) == 0 + with pytest.raises(TypeError) as excinfo: + m.ints_preferred(4.0) + assert msg(excinfo.value) == """ + ints_preferred(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ # noqa: E501 line too long + + assert m.ints_only(4) == 2 + with pytest.raises(TypeError) as excinfo: + m.ints_only(4.0) + assert msg(excinfo.value) == """ + ints_only(): incompatible function arguments. The following argument types are supported: + 1. (i: int) -> int + + Invoked with: 4.0 + """ + + +def test_bad_arg_default(msg): + from pybind11_tests import debug_enabled + + with pytest.raises(RuntimeError) as excinfo: + m.bad_arg_def_named() + assert msg(excinfo.value) == ( + "arg(): could not convert default argument 'a: UnregisteredType' in function " + "'should_fail' into a Python object (type not registered yet?)" + if debug_enabled else + "arg(): could not convert default argument into a Python object (type not registered " + "yet?). Compile in debug mode for more information." + ) + + with pytest.raises(RuntimeError) as excinfo: + m.bad_arg_def_unnamed() + assert msg(excinfo.value) == ( + "arg(): could not convert default argument 'UnregisteredType' in function " + "'should_fail' into a Python object (type not registered yet?)" + if debug_enabled else + "arg(): could not convert default argument into a Python object (type not registered " + "yet?). Compile in debug mode for more information." + ) + + +def test_accepts_none(msg): + a = m.NoneTester() + assert m.no_none1(a) == 42 + assert m.no_none2(a) == 42 + assert m.no_none3(a) == 42 + assert m.no_none4(a) == 42 + assert m.no_none5(a) == 42 + assert m.ok_none1(a) == 42 + assert m.ok_none2(a) == 42 + assert m.ok_none3(a) == 42 + assert m.ok_none4(a) == 42 + assert m.ok_none5(a) == 42 + + with pytest.raises(TypeError) as excinfo: + m.no_none1(None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none2(None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none3(None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none4(None) + assert "incompatible function arguments" in str(excinfo.value) + with pytest.raises(TypeError) as excinfo: + m.no_none5(None) + assert "incompatible function arguments" in str(excinfo.value) + + # The first one still raises because you can't pass None as a lvalue reference arg: + with pytest.raises(TypeError) as excinfo: + assert m.ok_none1(None) == -1 + assert msg(excinfo.value) == """ + ok_none1(): incompatible function arguments. The following argument types are supported: + 1. (arg0: m.methods_and_attributes.NoneTester) -> int + + Invoked with: None + """ + + # The rest take the argument as pointer or holder, and accept None: + assert m.ok_none2(None) == -1 + assert m.ok_none3(None) == -1 + assert m.ok_none4(None) == -1 + assert m.ok_none5(None) == -1 + + +def test_str_issue(msg): + """#283: __str__ called on uninitialized instance when constructor arguments invalid""" + + assert str(m.StrIssue(3)) == "StrIssue[3]" + + with pytest.raises(TypeError) as excinfo: + str(m.StrIssue("no", "such", "constructor")) + assert msg(excinfo.value) == """ + __init__(): incompatible constructor arguments. The following argument types are supported: + 1. m.methods_and_attributes.StrIssue(arg0: int) + 2. m.methods_and_attributes.StrIssue() + + Invoked with: 'no', 'such', 'constructor' + """ + + +def test_unregistered_base_implementations(): + a = m.RegisteredDerived() + a.do_nothing() + assert a.rw_value == 42 + assert a.ro_value == 1.25 + a.rw_value += 5 + assert a.sum() == 48.25 + a.increase_value() + assert a.rw_value == 48 + assert a.ro_value == 1.5 + assert a.sum() == 49.5 + assert a.rw_value_prop == 48 + a.rw_value_prop += 1 + assert a.rw_value_prop == 49 + a.increase_value() + assert a.ro_value_prop == 1.75 + + +def test_custom_caster_destruction(): + """Tests that returning a pointer to a type that gets converted with a custom type caster gets + destroyed when the function has py::return_value_policy::take_ownership policy applied.""" + + cstats = m.destruction_tester_cstats() + # This one *doesn't* have take_ownership: the pointer should be used but not destroyed: + z = m.custom_caster_no_destroy() + assert cstats.alive() == 1 and cstats.default_constructions == 1 + assert z + + # take_ownership applied: this constructs a new object, casts it, then destroys it: + z = m.custom_caster_destroy() + assert z + assert cstats.default_constructions == 2 + + # Same, but with a const pointer return (which should *not* inhibit destruction): + z = m.custom_caster_destroy_const() + assert z + assert cstats.default_constructions == 3 + + # Make sure we still only have the original object (from ..._no_destroy()) alive: + assert cstats.alive() == 1 diff --git a/external/pybind11/tests/test_modules.cpp b/external/pybind11/tests/test_modules.cpp new file mode 100644 index 0000000..c1475fa --- /dev/null +++ b/external/pybind11/tests/test_modules.cpp @@ -0,0 +1,98 @@ +/* + tests/test_modules.cpp -- nested modules, importing modules, and + internal references + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" + +TEST_SUBMODULE(modules, m) { + // test_nested_modules + py::module m_sub = m.def_submodule("subsubmodule"); + m_sub.def("submodule_func", []() { return "submodule_func()"; }); + + // test_reference_internal + class A { + public: + A(int v) : v(v) { print_created(this, v); } + ~A() { print_destroyed(this); } + A(const A&) { print_copy_created(this); } + A& operator=(const A ©) { print_copy_assigned(this); v = copy.v; return *this; } + std::string toString() { return "A[" + std::to_string(v) + "]"; } + private: + int v; + }; + py::class_(m_sub, "A") + .def(py::init()) + .def("__repr__", &A::toString); + + class B { + public: + B() { print_default_created(this); } + ~B() { print_destroyed(this); } + B(const B&) { print_copy_created(this); } + B& operator=(const B ©) { print_copy_assigned(this); a1 = copy.a1; a2 = copy.a2; return *this; } + A &get_a1() { return a1; } + A &get_a2() { return a2; } + + A a1{1}; + A a2{2}; + }; + py::class_(m_sub, "B") + .def(py::init<>()) + .def("get_a1", &B::get_a1, "Return the internal A 1", py::return_value_policy::reference_internal) + .def("get_a2", &B::get_a2, "Return the internal A 2", py::return_value_policy::reference_internal) + .def_readwrite("a1", &B::a1) // def_readonly uses an internal reference return policy by default + .def_readwrite("a2", &B::a2); + + m.attr("OD") = py::module::import("collections").attr("OrderedDict"); + + // test_duplicate_registration + // Registering two things with the same name + m.def("duplicate_registration", []() { + class Dupe1 { }; + class Dupe2 { }; + class Dupe3 { }; + class DupeException { }; + + auto dm = py::module("dummy"); + auto failures = py::list(); + + py::class_(dm, "Dupe1"); + py::class_(dm, "Dupe2"); + dm.def("dupe1_factory", []() { return Dupe1(); }); + py::exception(dm, "DupeException"); + + try { + py::class_(dm, "Dupe1"); + failures.append("Dupe1 class"); + } catch (std::runtime_error &) {} + try { + dm.def("Dupe1", []() { return Dupe1(); }); + failures.append("Dupe1 function"); + } catch (std::runtime_error &) {} + try { + py::class_(dm, "dupe1_factory"); + failures.append("dupe1_factory"); + } catch (std::runtime_error &) {} + try { + py::exception(dm, "Dupe2"); + failures.append("Dupe2"); + } catch (std::runtime_error &) {} + try { + dm.def("DupeException", []() { return 30; }); + failures.append("DupeException1"); + } catch (std::runtime_error &) {} + try { + py::class_(dm, "DupeException"); + failures.append("DupeException2"); + } catch (std::runtime_error &) {} + + return failures; + }); +} diff --git a/external/pybind11/tests/test_modules.py b/external/pybind11/tests/test_modules.py new file mode 100644 index 0000000..2552838 --- /dev/null +++ b/external/pybind11/tests/test_modules.py @@ -0,0 +1,72 @@ +from pybind11_tests import modules as m +from pybind11_tests.modules import subsubmodule as ms +from pybind11_tests import ConstructorStats + + +def test_nested_modules(): + import pybind11_tests + assert pybind11_tests.__name__ == "pybind11_tests" + assert pybind11_tests.modules.__name__ == "pybind11_tests.modules" + assert pybind11_tests.modules.subsubmodule.__name__ == "pybind11_tests.modules.subsubmodule" + assert m.__name__ == "pybind11_tests.modules" + assert ms.__name__ == "pybind11_tests.modules.subsubmodule" + + assert ms.submodule_func() == "submodule_func()" + + +def test_reference_internal(): + b = ms.B() + assert str(b.get_a1()) == "A[1]" + assert str(b.a1) == "A[1]" + assert str(b.get_a2()) == "A[2]" + assert str(b.a2) == "A[2]" + + b.a1 = ms.A(42) + b.a2 = ms.A(43) + assert str(b.get_a1()) == "A[42]" + assert str(b.a1) == "A[42]" + assert str(b.get_a2()) == "A[43]" + assert str(b.a2) == "A[43]" + + astats, bstats = ConstructorStats.get(ms.A), ConstructorStats.get(ms.B) + assert astats.alive() == 2 + assert bstats.alive() == 1 + del b + assert astats.alive() == 0 + assert bstats.alive() == 0 + assert astats.values() == ['1', '2', '42', '43'] + assert bstats.values() == [] + assert astats.default_constructions == 0 + assert bstats.default_constructions == 1 + assert astats.copy_constructions == 0 + assert bstats.copy_constructions == 0 + # assert astats.move_constructions >= 0 # Don't invoke any + # assert bstats.move_constructions >= 0 # Don't invoke any + assert astats.copy_assignments == 2 + assert bstats.copy_assignments == 0 + assert astats.move_assignments == 0 + assert bstats.move_assignments == 0 + + +def test_importing(): + from pybind11_tests.modules import OD + from collections import OrderedDict + + assert OD is OrderedDict + assert str(OD([(1, 'a'), (2, 'b')])) == "OrderedDict([(1, 'a'), (2, 'b')])" + + +def test_pydoc(): + """Pydoc needs to be able to provide help() for everything inside a pybind11 module""" + import pybind11_tests + import pydoc + + assert pybind11_tests.__name__ == "pybind11_tests" + assert pybind11_tests.__doc__ == "pybind11 test module" + assert pydoc.text.docmodule(pybind11_tests) + + +def test_duplicate_registration(): + """Registering two things with the same name""" + + assert m.duplicate_registration() == [] diff --git a/external/pybind11/tests/test_multiple_inheritance.cpp b/external/pybind11/tests/test_multiple_inheritance.cpp new file mode 100644 index 0000000..35f9d9c --- /dev/null +++ b/external/pybind11/tests/test_multiple_inheritance.cpp @@ -0,0 +1,220 @@ +/* + tests/test_multiple_inheritance.cpp -- multiple inheritance, + implicit MI casts + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" + +// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra +// space for holder constructed flags) works. +template struct BaseN { + BaseN(int i) : i(i) { } + int i; +}; + +// test_mi_static_properties +struct Vanilla { + std::string vanilla() { return "Vanilla"; }; +}; +struct WithStatic1 { + static std::string static_func1() { return "WithStatic1"; }; + static int static_value1; +}; +struct WithStatic2 { + static std::string static_func2() { return "WithStatic2"; }; + static int static_value2; +}; +struct VanillaStaticMix1 : Vanilla, WithStatic1, WithStatic2 { + static std::string static_func() { return "VanillaStaticMix1"; } + static int static_value; +}; +struct VanillaStaticMix2 : WithStatic1, Vanilla, WithStatic2 { + static std::string static_func() { return "VanillaStaticMix2"; } + static int static_value; +}; +int WithStatic1::static_value1 = 1; +int WithStatic2::static_value2 = 2; +int VanillaStaticMix1::static_value = 12; +int VanillaStaticMix2::static_value = 12; + +TEST_SUBMODULE(multiple_inheritance, m) { + + // test_multiple_inheritance_mix1 + // test_multiple_inheritance_mix2 + struct Base1 { + Base1(int i) : i(i) { } + int foo() { return i; } + int i; + }; + py::class_ b1(m, "Base1"); + b1.def(py::init()) + .def("foo", &Base1::foo); + + struct Base2 { + Base2(int i) : i(i) { } + int bar() { return i; } + int i; + }; + py::class_ b2(m, "Base2"); + b2.def(py::init()) + .def("bar", &Base2::bar); + + + // test_multiple_inheritance_cpp + struct Base12 : Base1, Base2 { + Base12(int i, int j) : Base1(i), Base2(j) { } + }; + struct MIType : Base12 { + MIType(int i, int j) : Base12(i, j) { } + }; + py::class_(m, "Base12"); + py::class_(m, "MIType") + .def(py::init()); + + + // test_multiple_inheritance_python_many_bases + #define PYBIND11_BASEN(N) py::class_>(m, "BaseN" #N).def(py::init()).def("f" #N, [](BaseN &b) { return b.i + N; }) + PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4); + PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8); + PYBIND11_BASEN( 9); PYBIND11_BASEN(10); PYBIND11_BASEN(11); PYBIND11_BASEN(12); + PYBIND11_BASEN(13); PYBIND11_BASEN(14); PYBIND11_BASEN(15); PYBIND11_BASEN(16); + PYBIND11_BASEN(17); + + // Uncommenting this should result in a compile time failure (MI can only be specified via + // template parameters because pybind has to know the types involved; see discussion in #742 for + // details). +// struct Base12v2 : Base1, Base2 { +// Base12v2(int i, int j) : Base1(i), Base2(j) { } +// }; +// py::class_(m, "Base12v2", b1, b2) +// .def(py::init()); + + + // test_multiple_inheritance_virtbase + // Test the case where not all base classes are specified, and where pybind11 requires the + // py::multiple_inheritance flag to perform proper casting between types. + struct Base1a { + Base1a(int i) : i(i) { } + int foo() { return i; } + int i; + }; + py::class_>(m, "Base1a") + .def(py::init()) + .def("foo", &Base1a::foo); + + struct Base2a { + Base2a(int i) : i(i) { } + int bar() { return i; } + int i; + }; + py::class_>(m, "Base2a") + .def(py::init()) + .def("bar", &Base2a::bar); + + struct Base12a : Base1a, Base2a { + Base12a(int i, int j) : Base1a(i), Base2a(j) { } + }; + py::class_>(m, "Base12a", py::multiple_inheritance()) + .def(py::init()); + + m.def("bar_base2a", [](Base2a *b) { return b->bar(); }); + m.def("bar_base2a_sharedptr", [](std::shared_ptr b) { return b->bar(); }); + + // test_mi_unaligned_base + // test_mi_base_return + // Issue #801: invalid casting to derived type with MI bases + struct I801B1 { int a = 1; virtual ~I801B1() = default; }; + struct I801B2 { int b = 2; virtual ~I801B2() = default; }; + struct I801C : I801B1, I801B2 {}; + struct I801D : I801C {}; // Indirect MI + // Unregistered classes: + struct I801B3 { int c = 3; virtual ~I801B3() = default; }; + struct I801E : I801B3, I801D {}; + + py::class_>(m, "I801B1").def(py::init<>()).def_readonly("a", &I801B1::a); + py::class_>(m, "I801B2").def(py::init<>()).def_readonly("b", &I801B2::b); + py::class_>(m, "I801C").def(py::init<>()); + py::class_>(m, "I801D").def(py::init<>()); + + // Two separate issues here: first, we want to recognize a pointer to a base type as being a + // known instance even when the pointer value is unequal (i.e. due to a non-first + // multiple-inheritance base class): + m.def("i801b1_c", [](I801C *c) { return static_cast(c); }); + m.def("i801b2_c", [](I801C *c) { return static_cast(c); }); + m.def("i801b1_d", [](I801D *d) { return static_cast(d); }); + m.def("i801b2_d", [](I801D *d) { return static_cast(d); }); + + // Second, when returned a base class pointer to a derived instance, we cannot assume that the + // pointer is `reinterpret_cast`able to the derived pointer because, like above, the base class + // pointer could be offset. + m.def("i801c_b1", []() -> I801B1 * { return new I801C(); }); + m.def("i801c_b2", []() -> I801B2 * { return new I801C(); }); + m.def("i801d_b1", []() -> I801B1 * { return new I801D(); }); + m.def("i801d_b2", []() -> I801B2 * { return new I801D(); }); + + // Return a base class pointer to a pybind-registered type when the actual derived type + // isn't pybind-registered (and uses multiple-inheritance to offset the pybind base) + m.def("i801e_c", []() -> I801C * { return new I801E(); }); + m.def("i801e_b2", []() -> I801B2 * { return new I801E(); }); + + + // test_mi_static_properties + py::class_(m, "Vanilla") + .def(py::init<>()) + .def("vanilla", &Vanilla::vanilla); + + py::class_(m, "WithStatic1") + .def(py::init<>()) + .def_static("static_func1", &WithStatic1::static_func1) + .def_readwrite_static("static_value1", &WithStatic1::static_value1); + + py::class_(m, "WithStatic2") + .def(py::init<>()) + .def_static("static_func2", &WithStatic2::static_func2) + .def_readwrite_static("static_value2", &WithStatic2::static_value2); + + py::class_( + m, "VanillaStaticMix1") + .def(py::init<>()) + .def_static("static_func", &VanillaStaticMix1::static_func) + .def_readwrite_static("static_value", &VanillaStaticMix1::static_value); + + py::class_( + m, "VanillaStaticMix2") + .def(py::init<>()) + .def_static("static_func", &VanillaStaticMix2::static_func) + .def_readwrite_static("static_value", &VanillaStaticMix2::static_value); + + +#if !defined(PYPY_VERSION) + struct WithDict { }; + struct VanillaDictMix1 : Vanilla, WithDict { }; + struct VanillaDictMix2 : WithDict, Vanilla { }; + py::class_(m, "WithDict", py::dynamic_attr()).def(py::init<>()); + py::class_(m, "VanillaDictMix1").def(py::init<>()); + py::class_(m, "VanillaDictMix2").def(py::init<>()); +#endif + + // test_diamond_inheritance + // Issue #959: segfault when constructing diamond inheritance instance + // All of these have int members so that there will be various unequal pointers involved. + struct B { int b; virtual ~B() = default; }; + struct C0 : public virtual B { int c0; }; + struct C1 : public virtual B { int c1; }; + struct D : public C0, public C1 { int d; }; + py::class_(m, "B") + .def("b", [](B *self) { return self; }); + py::class_(m, "C0") + .def("c0", [](C0 *self) { return self; }); + py::class_(m, "C1") + .def("c1", [](C1 *self) { return self; }); + py::class_(m, "D") + .def(py::init<>()); +} diff --git a/external/pybind11/tests/test_multiple_inheritance.py b/external/pybind11/tests/test_multiple_inheritance.py new file mode 100644 index 0000000..475dd3b --- /dev/null +++ b/external/pybind11/tests/test_multiple_inheritance.py @@ -0,0 +1,349 @@ +import pytest +from pybind11_tests import ConstructorStats +from pybind11_tests import multiple_inheritance as m + + +def test_multiple_inheritance_cpp(): + mt = m.MIType(3, 4) + + assert mt.foo() == 3 + assert mt.bar() == 4 + + +def test_multiple_inheritance_mix1(): + class Base1: + def __init__(self, i): + self.i = i + + def foo(self): + return self.i + + class MITypePy(Base1, m.Base2): + def __init__(self, i, j): + Base1.__init__(self, i) + m.Base2.__init__(self, j) + + mt = MITypePy(3, 4) + + assert mt.foo() == 3 + assert mt.bar() == 4 + + +def test_multiple_inheritance_mix2(): + + class Base2: + def __init__(self, i): + self.i = i + + def bar(self): + return self.i + + class MITypePy(m.Base1, Base2): + def __init__(self, i, j): + m.Base1.__init__(self, i) + Base2.__init__(self, j) + + mt = MITypePy(3, 4) + + assert mt.foo() == 3 + assert mt.bar() == 4 + + +def test_multiple_inheritance_python(): + + class MI1(m.Base1, m.Base2): + def __init__(self, i, j): + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) + + class B1(object): + def v(self): + return 1 + + class MI2(B1, m.Base1, m.Base2): + def __init__(self, i, j): + B1.__init__(self) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) + + class MI3(MI2): + def __init__(self, i, j): + MI2.__init__(self, i, j) + + class MI4(MI3, m.Base2): + def __init__(self, i, j): + MI3.__init__(self, i, j) + # This should be ignored (Base2 is already initialized via MI2): + m.Base2.__init__(self, i + 100) + + class MI5(m.Base2, B1, m.Base1): + def __init__(self, i, j): + B1.__init__(self) + m.Base1.__init__(self, i) + m.Base2.__init__(self, j) + + class MI6(m.Base2, B1): + def __init__(self, i): + m.Base2.__init__(self, i) + B1.__init__(self) + + class B2(B1): + def v(self): + return 2 + + class B3(object): + def v(self): + return 3 + + class B4(B3, B2): + def v(self): + return 4 + + class MI7(B4, MI6): + def __init__(self, i): + B4.__init__(self) + MI6.__init__(self, i) + + class MI8(MI6, B3): + def __init__(self, i): + MI6.__init__(self, i) + B3.__init__(self) + + class MI8b(B3, MI6): + def __init__(self, i): + B3.__init__(self) + MI6.__init__(self, i) + + mi1 = MI1(1, 2) + assert mi1.foo() == 1 + assert mi1.bar() == 2 + + mi2 = MI2(3, 4) + assert mi2.v() == 1 + assert mi2.foo() == 3 + assert mi2.bar() == 4 + + mi3 = MI3(5, 6) + assert mi3.v() == 1 + assert mi3.foo() == 5 + assert mi3.bar() == 6 + + mi4 = MI4(7, 8) + assert mi4.v() == 1 + assert mi4.foo() == 7 + assert mi4.bar() == 8 + + mi5 = MI5(10, 11) + assert mi5.v() == 1 + assert mi5.foo() == 10 + assert mi5.bar() == 11 + + mi6 = MI6(12) + assert mi6.v() == 1 + assert mi6.bar() == 12 + + mi7 = MI7(13) + assert mi7.v() == 4 + assert mi7.bar() == 13 + + mi8 = MI8(14) + assert mi8.v() == 1 + assert mi8.bar() == 14 + + mi8b = MI8b(15) + assert mi8b.v() == 3 + assert mi8b.bar() == 15 + + +def test_multiple_inheritance_python_many_bases(): + + class MIMany14(m.BaseN1, m.BaseN2, m.BaseN3, m.BaseN4): + def __init__(self): + m.BaseN1.__init__(self, 1) + m.BaseN2.__init__(self, 2) + m.BaseN3.__init__(self, 3) + m.BaseN4.__init__(self, 4) + + class MIMany58(m.BaseN5, m.BaseN6, m.BaseN7, m.BaseN8): + def __init__(self): + m.BaseN5.__init__(self, 5) + m.BaseN6.__init__(self, 6) + m.BaseN7.__init__(self, 7) + m.BaseN8.__init__(self, 8) + + class MIMany916(m.BaseN9, m.BaseN10, m.BaseN11, m.BaseN12, m.BaseN13, m.BaseN14, m.BaseN15, + m.BaseN16): + def __init__(self): + m.BaseN9.__init__(self, 9) + m.BaseN10.__init__(self, 10) + m.BaseN11.__init__(self, 11) + m.BaseN12.__init__(self, 12) + m.BaseN13.__init__(self, 13) + m.BaseN14.__init__(self, 14) + m.BaseN15.__init__(self, 15) + m.BaseN16.__init__(self, 16) + + class MIMany19(MIMany14, MIMany58, m.BaseN9): + def __init__(self): + MIMany14.__init__(self) + MIMany58.__init__(self) + m.BaseN9.__init__(self, 9) + + class MIMany117(MIMany14, MIMany58, MIMany916, m.BaseN17): + def __init__(self): + MIMany14.__init__(self) + MIMany58.__init__(self) + MIMany916.__init__(self) + m.BaseN17.__init__(self, 17) + + # Inherits from 4 registered C++ classes: can fit in one pointer on any modern arch: + a = MIMany14() + for i in range(1, 4): + assert getattr(a, "f" + str(i))() == 2 * i + + # Inherits from 8: requires 1/2 pointers worth of holder flags on 32/64-bit arch: + b = MIMany916() + for i in range(9, 16): + assert getattr(b, "f" + str(i))() == 2 * i + + # Inherits from 9: requires >= 2 pointers worth of holder flags + c = MIMany19() + for i in range(1, 9): + assert getattr(c, "f" + str(i))() == 2 * i + + # Inherits from 17: requires >= 3 pointers worth of holder flags + d = MIMany117() + for i in range(1, 17): + assert getattr(d, "f" + str(i))() == 2 * i + + +def test_multiple_inheritance_virtbase(): + + class MITypePy(m.Base12a): + def __init__(self, i, j): + m.Base12a.__init__(self, i, j) + + mt = MITypePy(3, 4) + assert mt.bar() == 4 + assert m.bar_base2a(mt) == 4 + assert m.bar_base2a_sharedptr(mt) == 4 + + +def test_mi_static_properties(): + """Mixing bases with and without static properties should be possible + and the result should be independent of base definition order""" + + for d in (m.VanillaStaticMix1(), m.VanillaStaticMix2()): + assert d.vanilla() == "Vanilla" + assert d.static_func1() == "WithStatic1" + assert d.static_func2() == "WithStatic2" + assert d.static_func() == d.__class__.__name__ + + m.WithStatic1.static_value1 = 1 + m.WithStatic2.static_value2 = 2 + assert d.static_value1 == 1 + assert d.static_value2 == 2 + assert d.static_value == 12 + + d.static_value1 = 0 + assert d.static_value1 == 0 + d.static_value2 = 0 + assert d.static_value2 == 0 + d.static_value = 0 + assert d.static_value == 0 + + +@pytest.unsupported_on_pypy +def test_mi_dynamic_attributes(): + """Mixing bases with and without dynamic attribute support""" + + for d in (m.VanillaDictMix1(), m.VanillaDictMix2()): + d.dynamic = 1 + assert d.dynamic == 1 + + +def test_mi_unaligned_base(): + """Returning an offset (non-first MI) base class pointer should recognize the instance""" + + n_inst = ConstructorStats.detail_reg_inst() + + c = m.I801C() + d = m.I801D() + # + 4 below because we have the two instances, and each instance has offset base I801B2 + assert ConstructorStats.detail_reg_inst() == n_inst + 4 + b1c = m.i801b1_c(c) + assert b1c is c + b2c = m.i801b2_c(c) + assert b2c is c + b1d = m.i801b1_d(d) + assert b1d is d + b2d = m.i801b2_d(d) + assert b2d is d + + assert ConstructorStats.detail_reg_inst() == n_inst + 4 # no extra instances + del c, b1c, b2c + assert ConstructorStats.detail_reg_inst() == n_inst + 2 + del d, b1d, b2d + assert ConstructorStats.detail_reg_inst() == n_inst + + +def test_mi_base_return(): + """Tests returning an offset (non-first MI) base class pointer to a derived instance""" + + n_inst = ConstructorStats.detail_reg_inst() + + c1 = m.i801c_b1() + assert type(c1) is m.I801C + assert c1.a == 1 + assert c1.b == 2 + + d1 = m.i801d_b1() + assert type(d1) is m.I801D + assert d1.a == 1 + assert d1.b == 2 + + assert ConstructorStats.detail_reg_inst() == n_inst + 4 + + c2 = m.i801c_b2() + assert type(c2) is m.I801C + assert c2.a == 1 + assert c2.b == 2 + + d2 = m.i801d_b2() + assert type(d2) is m.I801D + assert d2.a == 1 + assert d2.b == 2 + + assert ConstructorStats.detail_reg_inst() == n_inst + 8 + + del c2 + assert ConstructorStats.detail_reg_inst() == n_inst + 6 + del c1, d1, d2 + assert ConstructorStats.detail_reg_inst() == n_inst + + # Returning an unregistered derived type with a registered base; we won't + # pick up the derived type, obviously, but should still work (as an object + # of whatever type was returned). + e1 = m.i801e_c() + assert type(e1) is m.I801C + assert e1.a == 1 + assert e1.b == 2 + + e2 = m.i801e_b2() + assert type(e2) is m.I801B2 + assert e2.b == 2 + + +def test_diamond_inheritance(): + """Tests that diamond inheritance works as expected (issue #959)""" + + # Issue #959: this shouldn't segfault: + d = m.D() + + # Make sure all the various distinct pointers are all recognized as registered instances: + assert d is d.c0() + assert d is d.c1() + assert d is d.b() + assert d is d.c0().b() + assert d is d.c1().b() + assert d is d.c0().c1().b().c0().b() diff --git a/external/pybind11/tests/test_numpy_array.cpp b/external/pybind11/tests/test_numpy_array.cpp new file mode 100644 index 0000000..2046c0e --- /dev/null +++ b/external/pybind11/tests/test_numpy_array.cpp @@ -0,0 +1,295 @@ +/* + tests/test_numpy_array.cpp -- test core array functionality + + Copyright (c) 2016 Ivan Smirnov + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +#include +#include + +#include + +using arr = py::array; +using arr_t = py::array_t; +static_assert(std::is_same::value, ""); + +template arr data(const arr& a, Ix... index) { + return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); +} + +template arr data_t(const arr_t& a, Ix... index) { + return arr(a.size() - a.index_at(index...), a.data(index...)); +} + +template arr& mutate_data(arr& a, Ix... index) { + auto ptr = (uint8_t *) a.mutable_data(index...); + for (ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) + ptr[i] = (uint8_t) (ptr[i] * 2); + return a; +} + +template arr_t& mutate_data_t(arr_t& a, Ix... index) { + auto ptr = a.mutable_data(index...); + for (ssize_t i = 0; i < a.size() - a.index_at(index...); i++) + ptr[i]++; + return a; +} + +template ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } +template ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } +template ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } +template ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } +template ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } +template arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } + +#define def_index_fn(name, type) \ + sm.def(#name, [](type a) { return name(a); }); \ + sm.def(#name, [](type a, int i) { return name(a, i); }); \ + sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \ + sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); }); + +template py::handle auxiliaries(T &&r, T2 &&r2) { + if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); + py::list l; + l.append(*r.data(0, 0)); + l.append(*r2.mutable_data(0, 0)); + l.append(r.data(0, 1) == r2.mutable_data(0, 1)); + l.append(r.ndim()); + l.append(r.itemsize()); + l.append(r.shape(0)); + l.append(r.shape(1)); + l.append(r.size()); + l.append(r.nbytes()); + return l.release(); +} + +TEST_SUBMODULE(numpy_array, sm) { + try { py::module::import("numpy"); } + catch (...) { return; } + + // test_array_attributes + sm.def("ndim", [](const arr& a) { return a.ndim(); }); + sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); + sm.def("shape", [](const arr& a, ssize_t dim) { return a.shape(dim); }); + sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); }); + sm.def("strides", [](const arr& a, ssize_t dim) { return a.strides(dim); }); + sm.def("writeable", [](const arr& a) { return a.writeable(); }); + sm.def("size", [](const arr& a) { return a.size(); }); + sm.def("itemsize", [](const arr& a) { return a.itemsize(); }); + sm.def("nbytes", [](const arr& a) { return a.nbytes(); }); + sm.def("owndata", [](const arr& a) { return a.owndata(); }); + + // test_index_offset + def_index_fn(index_at, const arr&); + def_index_fn(index_at_t, const arr_t&); + def_index_fn(offset_at, const arr&); + def_index_fn(offset_at_t, const arr_t&); + // test_data + def_index_fn(data, const arr&); + def_index_fn(data_t, const arr_t&); + // test_mutate_data, test_mutate_readonly + def_index_fn(mutate_data, arr&); + def_index_fn(mutate_data_t, arr_t&); + def_index_fn(at_t, const arr_t&); + def_index_fn(mutate_at_t, arr_t&); + + // test_make_c_f_array + sm.def("make_f_array", [] { return py::array_t({ 2, 2 }, { 4, 8 }); }); + sm.def("make_c_array", [] { return py::array_t({ 2, 2 }, { 8, 4 }); }); + + // test_wrap + sm.def("wrap", [](py::array a) { + return py::array( + a.dtype(), + {a.shape(), a.shape() + a.ndim()}, + {a.strides(), a.strides() + a.ndim()}, + a.data(), + a + ); + }); + + // test_numpy_view + struct ArrayClass { + int data[2] = { 1, 2 }; + ArrayClass() { py::print("ArrayClass()"); } + ~ArrayClass() { py::print("~ArrayClass()"); } + }; + py::class_(sm, "ArrayClass") + .def(py::init<>()) + .def("numpy_view", [](py::object &obj) { + py::print("ArrayClass::numpy_view()"); + ArrayClass &a = obj.cast(); + return py::array_t({2}, {4}, a.data, obj); + } + ); + + // test_cast_numpy_int64_to_uint64 + sm.def("function_taking_uint64", [](uint64_t) { }); + + // test_isinstance + sm.def("isinstance_untyped", [](py::object yes, py::object no) { + return py::isinstance(yes) && !py::isinstance(no); + }); + sm.def("isinstance_typed", [](py::object o) { + return py::isinstance>(o) && !py::isinstance>(o); + }); + + // test_constructors + sm.def("default_constructors", []() { + return py::dict( + "array"_a=py::array(), + "array_t"_a=py::array_t(), + "array_t"_a=py::array_t() + ); + }); + sm.def("converting_constructors", [](py::object o) { + return py::dict( + "array"_a=py::array(o), + "array_t"_a=py::array_t(o), + "array_t"_a=py::array_t(o) + ); + }); + + // test_overload_resolution + sm.def("overloaded", [](py::array_t) { return "double"; }); + sm.def("overloaded", [](py::array_t) { return "float"; }); + sm.def("overloaded", [](py::array_t) { return "int"; }); + sm.def("overloaded", [](py::array_t) { return "unsigned short"; }); + sm.def("overloaded", [](py::array_t) { return "long long"; }); + sm.def("overloaded", [](py::array_t>) { return "double complex"; }); + sm.def("overloaded", [](py::array_t>) { return "float complex"; }); + + sm.def("overloaded2", [](py::array_t>) { return "double complex"; }); + sm.def("overloaded2", [](py::array_t) { return "double"; }); + sm.def("overloaded2", [](py::array_t>) { return "float complex"; }); + sm.def("overloaded2", [](py::array_t) { return "float"; }); + + // Only accept the exact types: + sm.def("overloaded3", [](py::array_t) { return "int"; }, py::arg().noconvert()); + sm.def("overloaded3", [](py::array_t) { return "double"; }, py::arg().noconvert()); + + // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but + // rather that float gets converted via the safe (conversion to double) overload: + sm.def("overloaded4", [](py::array_t) { return "long long"; }); + sm.def("overloaded4", [](py::array_t) { return "double"; }); + + // But we do allow conversion to int if forcecast is enabled (but only if no overload matches + // without conversion) + sm.def("overloaded5", [](py::array_t) { return "unsigned int"; }); + sm.def("overloaded5", [](py::array_t) { return "double"; }); + + // test_greedy_string_overload + // Issue 685: ndarray shouldn't go to std::string overload + sm.def("issue685", [](std::string) { return "string"; }); + sm.def("issue685", [](py::array) { return "array"; }); + sm.def("issue685", [](py::object) { return "other"; }); + + // test_array_unchecked_fixed_dims + sm.def("proxy_add2", [](py::array_t a, double v) { + auto r = a.mutable_unchecked<2>(); + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + r(i, j) += v; + }, py::arg().noconvert(), py::arg()); + + sm.def("proxy_init3", [](double start) { + py::array_t a({ 3, 3, 3 }); + auto r = a.mutable_unchecked<3>(); + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t k = 0; k < r.shape(2); k++) + r(i, j, k) = start++; + return a; + }); + sm.def("proxy_init3F", [](double start) { + py::array_t a({ 3, 3, 3 }); + auto r = a.mutable_unchecked<3>(); + for (ssize_t k = 0; k < r.shape(2); k++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t i = 0; i < r.shape(0); i++) + r(i, j, k) = start++; + return a; + }); + sm.def("proxy_squared_L2_norm", [](py::array_t a) { + auto r = a.unchecked<1>(); + double sumsq = 0; + for (ssize_t i = 0; i < r.shape(0); i++) + sumsq += r[i] * r(i); // Either notation works for a 1D array + return sumsq; + }); + + sm.def("proxy_auxiliaries2", [](py::array_t a) { + auto r = a.unchecked<2>(); + auto r2 = a.mutable_unchecked<2>(); + return auxiliaries(r, r2); + }); + + // test_array_unchecked_dyn_dims + // Same as the above, but without a compile-time dimensions specification: + sm.def("proxy_add2_dyn", [](py::array_t a, double v) { + auto r = a.mutable_unchecked(); + if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + r(i, j) += v; + }, py::arg().noconvert(), py::arg()); + sm.def("proxy_init3_dyn", [](double start) { + py::array_t a({ 3, 3, 3 }); + auto r = a.mutable_unchecked(); + if (r.ndim() != 3) throw std::domain_error("error: ndim != 3"); + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t k = 0; k < r.shape(2); k++) + r(i, j, k) = start++; + return a; + }); + sm.def("proxy_auxiliaries2_dyn", [](py::array_t a) { + return auxiliaries(a.unchecked(), a.mutable_unchecked()); + }); + + sm.def("array_auxiliaries2", [](py::array_t a) { + return auxiliaries(a, a); + }); + + // test_array_failures + // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object: + sm.def("array_fail_test", []() { return py::array(py::object()); }); + sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); + // Make sure the error from numpy is being passed through: + sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); }); + + // test_initializer_list + // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous + sm.def("array_initializer_list1", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it + sm.def("array_initializer_list2", []() { return py::array_t({ 1, 2 }); }); + sm.def("array_initializer_list3", []() { return py::array_t({ 1, 2, 3 }); }); + sm.def("array_initializer_list4", []() { return py::array_t({ 1, 2, 3, 4 }); }); + + // test_array_resize + // reshape array to 2D without changing size + sm.def("array_reshape2", [](py::array_t a) { + const ssize_t dim_sz = (ssize_t)std::sqrt(a.size()); + if (dim_sz * dim_sz != a.size()) + throw std::domain_error("array_reshape2: input array total size is not a squared integer"); + a.resize({dim_sz, dim_sz}); + }); + + // resize to 3D array with each dimension = N + sm.def("array_resize3", [](py::array_t a, size_t N, bool refcheck) { + a.resize({N, N, N}, refcheck); + }); + + // test_array_create_and_resize + // return 2D array with Nrows = Ncols = N + sm.def("create_and_resize", [](size_t N) { + py::array_t a; + a.resize({N, N}); + std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.); + return a; + }); +} diff --git a/external/pybind11/tests/test_numpy_array.py b/external/pybind11/tests/test_numpy_array.py new file mode 100644 index 0000000..2743393 --- /dev/null +++ b/external/pybind11/tests/test_numpy_array.py @@ -0,0 +1,402 @@ +import pytest +from pybind11_tests import numpy_array as m + +pytestmark = pytest.requires_numpy + +with pytest.suppress(ImportError): + import numpy as np + + +@pytest.fixture(scope='function') +def arr(): + return np.array([[1, 2, 3], [4, 5, 6]], '=u2') + + +def test_array_attributes(): + a = np.array(0, 'f8') + assert m.ndim(a) == 0 + assert all(m.shape(a) == []) + assert all(m.strides(a) == []) + with pytest.raises(IndexError) as excinfo: + m.shape(a, 0) + assert str(excinfo.value) == 'invalid axis: 0 (ndim = 0)' + with pytest.raises(IndexError) as excinfo: + m.strides(a, 0) + assert str(excinfo.value) == 'invalid axis: 0 (ndim = 0)' + assert m.writeable(a) + assert m.size(a) == 1 + assert m.itemsize(a) == 8 + assert m.nbytes(a) == 8 + assert m.owndata(a) + + a = np.array([[1, 2, 3], [4, 5, 6]], 'u2').view() + a.flags.writeable = False + assert m.ndim(a) == 2 + assert all(m.shape(a) == [2, 3]) + assert m.shape(a, 0) == 2 + assert m.shape(a, 1) == 3 + assert all(m.strides(a) == [6, 2]) + assert m.strides(a, 0) == 6 + assert m.strides(a, 1) == 2 + with pytest.raises(IndexError) as excinfo: + m.shape(a, 2) + assert str(excinfo.value) == 'invalid axis: 2 (ndim = 2)' + with pytest.raises(IndexError) as excinfo: + m.strides(a, 2) + assert str(excinfo.value) == 'invalid axis: 2 (ndim = 2)' + assert not m.writeable(a) + assert m.size(a) == 6 + assert m.itemsize(a) == 2 + assert m.nbytes(a) == 12 + assert not m.owndata(a) + + +@pytest.mark.parametrize('args, ret', [([], 0), ([0], 0), ([1], 3), ([0, 1], 1), ([1, 2], 5)]) +def test_index_offset(arr, args, ret): + assert m.index_at(arr, *args) == ret + assert m.index_at_t(arr, *args) == ret + assert m.offset_at(arr, *args) == ret * arr.dtype.itemsize + assert m.offset_at_t(arr, *args) == ret * arr.dtype.itemsize + + +def test_dim_check_fail(arr): + for func in (m.index_at, m.index_at_t, m.offset_at, m.offset_at_t, m.data, m.data_t, + m.mutate_data, m.mutate_data_t): + with pytest.raises(IndexError) as excinfo: + func(arr, 1, 2, 3) + assert str(excinfo.value) == 'too many indices for an array: 3 (ndim = 2)' + + +@pytest.mark.parametrize('args, ret', + [([], [1, 2, 3, 4, 5, 6]), + ([1], [4, 5, 6]), + ([0, 1], [2, 3, 4, 5, 6]), + ([1, 2], [6])]) +def test_data(arr, args, ret): + from sys import byteorder + assert all(m.data_t(arr, *args) == ret) + assert all(m.data(arr, *args)[(0 if byteorder == 'little' else 1)::2] == ret) + assert all(m.data(arr, *args)[(1 if byteorder == 'little' else 0)::2] == 0) + + +@pytest.mark.parametrize('dim', [0, 1, 3]) +def test_at_fail(arr, dim): + for func in m.at_t, m.mutate_at_t: + with pytest.raises(IndexError) as excinfo: + func(arr, *([0] * dim)) + assert str(excinfo.value) == 'index dimension mismatch: {} (ndim = 2)'.format(dim) + + +def test_at(arr): + assert m.at_t(arr, 0, 2) == 3 + assert m.at_t(arr, 1, 0) == 4 + + assert all(m.mutate_at_t(arr, 0, 2).ravel() == [1, 2, 4, 4, 5, 6]) + assert all(m.mutate_at_t(arr, 1, 0).ravel() == [1, 2, 4, 5, 5, 6]) + + +def test_mutate_readonly(arr): + arr.flags.writeable = False + for func, args in (m.mutate_data, ()), (m.mutate_data_t, ()), (m.mutate_at_t, (0, 0)): + with pytest.raises(ValueError) as excinfo: + func(arr, *args) + assert str(excinfo.value) == 'array is not writeable' + + +def test_mutate_data(arr): + assert all(m.mutate_data(arr).ravel() == [2, 4, 6, 8, 10, 12]) + assert all(m.mutate_data(arr).ravel() == [4, 8, 12, 16, 20, 24]) + assert all(m.mutate_data(arr, 1).ravel() == [4, 8, 12, 32, 40, 48]) + assert all(m.mutate_data(arr, 0, 1).ravel() == [4, 16, 24, 64, 80, 96]) + assert all(m.mutate_data(arr, 1, 2).ravel() == [4, 16, 24, 64, 80, 192]) + + assert all(m.mutate_data_t(arr).ravel() == [5, 17, 25, 65, 81, 193]) + assert all(m.mutate_data_t(arr).ravel() == [6, 18, 26, 66, 82, 194]) + assert all(m.mutate_data_t(arr, 1).ravel() == [6, 18, 26, 67, 83, 195]) + assert all(m.mutate_data_t(arr, 0, 1).ravel() == [6, 19, 27, 68, 84, 196]) + assert all(m.mutate_data_t(arr, 1, 2).ravel() == [6, 19, 27, 68, 84, 197]) + + +def test_bounds_check(arr): + for func in (m.index_at, m.index_at_t, m.data, m.data_t, + m.mutate_data, m.mutate_data_t, m.at_t, m.mutate_at_t): + with pytest.raises(IndexError) as excinfo: + func(arr, 2, 0) + assert str(excinfo.value) == 'index 2 is out of bounds for axis 0 with size 2' + with pytest.raises(IndexError) as excinfo: + func(arr, 0, 4) + assert str(excinfo.value) == 'index 4 is out of bounds for axis 1 with size 3' + + +def test_make_c_f_array(): + assert m.make_c_array().flags.c_contiguous + assert not m.make_c_array().flags.f_contiguous + assert m.make_f_array().flags.f_contiguous + assert not m.make_f_array().flags.c_contiguous + + +def test_wrap(): + def assert_references(a, b, base=None): + if base is None: + base = a + assert a is not b + assert a.__array_interface__['data'][0] == b.__array_interface__['data'][0] + assert a.shape == b.shape + assert a.strides == b.strides + assert a.flags.c_contiguous == b.flags.c_contiguous + assert a.flags.f_contiguous == b.flags.f_contiguous + assert a.flags.writeable == b.flags.writeable + assert a.flags.aligned == b.flags.aligned + assert a.flags.updateifcopy == b.flags.updateifcopy + assert np.all(a == b) + assert not b.flags.owndata + assert b.base is base + if a.flags.writeable and a.ndim == 2: + a[0, 0] = 1234 + assert b[0, 0] == 1234 + + a1 = np.array([1, 2], dtype=np.int16) + assert a1.flags.owndata and a1.base is None + a2 = m.wrap(a1) + assert_references(a1, a2) + + a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='F') + assert a1.flags.owndata and a1.base is None + a2 = m.wrap(a1) + assert_references(a1, a2) + + a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='C') + a1.flags.writeable = False + a2 = m.wrap(a1) + assert_references(a1, a2) + + a1 = np.random.random((4, 4, 4)) + a2 = m.wrap(a1) + assert_references(a1, a2) + + a1t = a1.transpose() + a2 = m.wrap(a1t) + assert_references(a1t, a2, a1) + + a1d = a1.diagonal() + a2 = m.wrap(a1d) + assert_references(a1d, a2, a1) + + a1m = a1[::-1, ::-1, ::-1] + a2 = m.wrap(a1m) + assert_references(a1m, a2, a1) + + +def test_numpy_view(capture): + with capture: + ac = m.ArrayClass() + ac_view_1 = ac.numpy_view() + ac_view_2 = ac.numpy_view() + assert np.all(ac_view_1 == np.array([1, 2], dtype=np.int32)) + del ac + pytest.gc_collect() + assert capture == """ + ArrayClass() + ArrayClass::numpy_view() + ArrayClass::numpy_view() + """ + ac_view_1[0] = 4 + ac_view_1[1] = 3 + assert ac_view_2[0] == 4 + assert ac_view_2[1] == 3 + with capture: + del ac_view_1 + del ac_view_2 + pytest.gc_collect() + pytest.gc_collect() + assert capture == """ + ~ArrayClass() + """ + + +@pytest.unsupported_on_pypy +def test_cast_numpy_int64_to_uint64(): + m.function_taking_uint64(123) + m.function_taking_uint64(np.uint64(123)) + + +def test_isinstance(): + assert m.isinstance_untyped(np.array([1, 2, 3]), "not an array") + assert m.isinstance_typed(np.array([1.0, 2.0, 3.0])) + + +def test_constructors(): + defaults = m.default_constructors() + for a in defaults.values(): + assert a.size == 0 + assert defaults["array"].dtype == np.array([]).dtype + assert defaults["array_t"].dtype == np.int32 + assert defaults["array_t"].dtype == np.float64 + + results = m.converting_constructors([1, 2, 3]) + for a in results.values(): + np.testing.assert_array_equal(a, [1, 2, 3]) + assert results["array"].dtype == np.int_ + assert results["array_t"].dtype == np.int32 + assert results["array_t"].dtype == np.float64 + + +def test_overload_resolution(msg): + # Exact overload matches: + assert m.overloaded(np.array([1], dtype='float64')) == 'double' + assert m.overloaded(np.array([1], dtype='float32')) == 'float' + assert m.overloaded(np.array([1], dtype='ushort')) == 'unsigned short' + assert m.overloaded(np.array([1], dtype='intc')) == 'int' + assert m.overloaded(np.array([1], dtype='longlong')) == 'long long' + assert m.overloaded(np.array([1], dtype='complex')) == 'double complex' + assert m.overloaded(np.array([1], dtype='csingle')) == 'float complex' + + # No exact match, should call first convertible version: + assert m.overloaded(np.array([1], dtype='uint8')) == 'double' + + with pytest.raises(TypeError) as excinfo: + m.overloaded("not an array") + assert msg(excinfo.value) == """ + overloaded(): incompatible function arguments. The following argument types are supported: + 1. (arg0: numpy.ndarray[float64]) -> str + 2. (arg0: numpy.ndarray[float32]) -> str + 3. (arg0: numpy.ndarray[int32]) -> str + 4. (arg0: numpy.ndarray[uint16]) -> str + 5. (arg0: numpy.ndarray[int64]) -> str + 6. (arg0: numpy.ndarray[complex128]) -> str + 7. (arg0: numpy.ndarray[complex64]) -> str + + Invoked with: 'not an array' + """ + + assert m.overloaded2(np.array([1], dtype='float64')) == 'double' + assert m.overloaded2(np.array([1], dtype='float32')) == 'float' + assert m.overloaded2(np.array([1], dtype='complex64')) == 'float complex' + assert m.overloaded2(np.array([1], dtype='complex128')) == 'double complex' + assert m.overloaded2(np.array([1], dtype='float32')) == 'float' + + assert m.overloaded3(np.array([1], dtype='float64')) == 'double' + assert m.overloaded3(np.array([1], dtype='intc')) == 'int' + expected_exc = """ + overloaded3(): incompatible function arguments. The following argument types are supported: + 1. (arg0: numpy.ndarray[int32]) -> str + 2. (arg0: numpy.ndarray[float64]) -> str + + Invoked with:""" + + with pytest.raises(TypeError) as excinfo: + m.overloaded3(np.array([1], dtype='uintc')) + assert msg(excinfo.value) == expected_exc + " array([1], dtype=uint32)" + with pytest.raises(TypeError) as excinfo: + m.overloaded3(np.array([1], dtype='float32')) + assert msg(excinfo.value) == expected_exc + " array([ 1.], dtype=float32)" + with pytest.raises(TypeError) as excinfo: + m.overloaded3(np.array([1], dtype='complex')) + assert msg(excinfo.value) == expected_exc + " array([ 1.+0.j])" + + # Exact matches: + assert m.overloaded4(np.array([1], dtype='double')) == 'double' + assert m.overloaded4(np.array([1], dtype='longlong')) == 'long long' + # Non-exact matches requiring conversion. Since float to integer isn't a + # save conversion, it should go to the double overload, but short can go to + # either (and so should end up on the first-registered, the long long). + assert m.overloaded4(np.array([1], dtype='float32')) == 'double' + assert m.overloaded4(np.array([1], dtype='short')) == 'long long' + + assert m.overloaded5(np.array([1], dtype='double')) == 'double' + assert m.overloaded5(np.array([1], dtype='uintc')) == 'unsigned int' + assert m.overloaded5(np.array([1], dtype='float32')) == 'unsigned int' + + +def test_greedy_string_overload(): + """Tests fix for #685 - ndarray shouldn't go to std::string overload""" + + assert m.issue685("abc") == "string" + assert m.issue685(np.array([97, 98, 99], dtype='b')) == "array" + assert m.issue685(123) == "other" + + +def test_array_unchecked_fixed_dims(msg): + z1 = np.array([[1, 2], [3, 4]], dtype='float64') + m.proxy_add2(z1, 10) + assert np.all(z1 == [[11, 12], [13, 14]]) + + with pytest.raises(ValueError) as excinfo: + m.proxy_add2(np.array([1., 2, 3]), 5.0) + assert msg(excinfo.value) == "array has incorrect number of dimensions: 1; expected 2" + + expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype='int') + assert np.all(m.proxy_init3(3.0) == expect_c) + expect_f = np.transpose(expect_c) + assert np.all(m.proxy_init3F(3.0) == expect_f) + + assert m.proxy_squared_L2_norm(np.array(range(6))) == 55 + assert m.proxy_squared_L2_norm(np.array(range(6), dtype="float64")) == 55 + + assert m.proxy_auxiliaries2(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32] + assert m.proxy_auxiliaries2(z1) == m.array_auxiliaries2(z1) + + +def test_array_unchecked_dyn_dims(msg): + z1 = np.array([[1, 2], [3, 4]], dtype='float64') + m.proxy_add2_dyn(z1, 10) + assert np.all(z1 == [[11, 12], [13, 14]]) + + expect_c = np.ndarray(shape=(3, 3, 3), buffer=np.array(range(3, 30)), dtype='int') + assert np.all(m.proxy_init3_dyn(3.0) == expect_c) + + assert m.proxy_auxiliaries2_dyn(z1) == [11, 11, True, 2, 8, 2, 2, 4, 32] + assert m.proxy_auxiliaries2_dyn(z1) == m.array_auxiliaries2(z1) + + +def test_array_failure(): + with pytest.raises(ValueError) as excinfo: + m.array_fail_test() + assert str(excinfo.value) == 'cannot create a pybind11::array from a nullptr' + + with pytest.raises(ValueError) as excinfo: + m.array_t_fail_test() + assert str(excinfo.value) == 'cannot create a pybind11::array_t from a nullptr' + + with pytest.raises(ValueError) as excinfo: + m.array_fail_test_negative_size() + assert str(excinfo.value) == 'negative dimensions are not allowed' + + +def test_initializer_list(): + assert m.array_initializer_list1().shape == (1,) + assert m.array_initializer_list2().shape == (1, 2) + assert m.array_initializer_list3().shape == (1, 2, 3) + assert m.array_initializer_list4().shape == (1, 2, 3, 4) + + +def test_array_resize(msg): + a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='float64') + m.array_reshape2(a) + assert(a.size == 9) + assert(np.all(a == [[1, 2, 3], [4, 5, 6], [7, 8, 9]])) + + # total size change should succced with refcheck off + m.array_resize3(a, 4, False) + assert(a.size == 64) + # ... and fail with refcheck on + try: + m.array_resize3(a, 3, True) + except ValueError as e: + assert(str(e).startswith("cannot resize an array")) + # transposed array doesn't own data + b = a.transpose() + try: + m.array_resize3(b, 3, False) + except ValueError as e: + assert(str(e).startswith("cannot resize this array: it does not own its data")) + # ... but reshape should be fine + m.array_reshape2(b) + assert(b.shape == (8, 8)) + + +@pytest.unsupported_on_pypy +def test_array_create_and_resize(msg): + a = m.create_and_resize(2) + assert(a.size == 4) + assert(np.all(a == 42.)) diff --git a/external/pybind11/tests/test_numpy_dtypes.cpp b/external/pybind11/tests/test_numpy_dtypes.cpp new file mode 100644 index 0000000..ddec851 --- /dev/null +++ b/external/pybind11/tests/test_numpy_dtypes.cpp @@ -0,0 +1,451 @@ +/* + tests/test_numpy_dtypes.cpp -- Structured and compound NumPy dtypes + + Copyright (c) 2016 Ivan Smirnov + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +#ifdef __GNUC__ +#define PYBIND11_PACKED(cls) cls __attribute__((__packed__)) +#else +#define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop)) +#endif + +namespace py = pybind11; + +struct SimpleStruct { + bool bool_; + uint32_t uint_; + float float_; + long double ldbl_; +}; + +std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) { + return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_; +} + +PYBIND11_PACKED(struct PackedStruct { + bool bool_; + uint32_t uint_; + float float_; + long double ldbl_; +}); + +std::ostream& operator<<(std::ostream& os, const PackedStruct& v) { + return os << "p:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_; +} + +PYBIND11_PACKED(struct NestedStruct { + SimpleStruct a; + PackedStruct b; +}); + +std::ostream& operator<<(std::ostream& os, const NestedStruct& v) { + return os << "n:a=" << v.a << ";b=" << v.b; +} + +struct PartialStruct { + bool bool_; + uint32_t uint_; + float float_; + uint64_t dummy2; + long double ldbl_; +}; + +struct PartialNestedStruct { + uint64_t dummy1; + PartialStruct a; + uint64_t dummy2; +}; + +struct UnboundStruct { }; + +struct StringStruct { + char a[3]; + std::array b; +}; + +struct ComplexStruct { + std::complex cflt; + std::complex cdbl; +}; + +std::ostream& operator<<(std::ostream& os, const ComplexStruct& v) { + return os << "c:" << v.cflt << "," << v.cdbl; +} + +struct ArrayStruct { + char a[3][4]; + int32_t b[2]; + std::array c; + std::array d[4]; +}; + +PYBIND11_PACKED(struct StructWithUglyNames { + int8_t __x__; + uint64_t __y__; +}); + +enum class E1 : int64_t { A = -1, B = 1 }; +enum E2 : uint8_t { X = 1, Y = 2 }; + +PYBIND11_PACKED(struct EnumStruct { + E1 e1; + E2 e2; +}); + +std::ostream& operator<<(std::ostream& os, const StringStruct& v) { + os << "a='"; + for (size_t i = 0; i < 3 && v.a[i]; i++) os << v.a[i]; + os << "',b='"; + for (size_t i = 0; i < 3 && v.b[i]; i++) os << v.b[i]; + return os << "'"; +} + +std::ostream& operator<<(std::ostream& os, const ArrayStruct& v) { + os << "a={"; + for (int i = 0; i < 3; i++) { + if (i > 0) + os << ','; + os << '{'; + for (int j = 0; j < 3; j++) + os << v.a[i][j] << ','; + os << v.a[i][3] << '}'; + } + os << "},b={" << v.b[0] << ',' << v.b[1]; + os << "},c={" << int(v.c[0]) << ',' << int(v.c[1]) << ',' << int(v.c[2]); + os << "},d={"; + for (int i = 0; i < 4; i++) { + if (i > 0) + os << ','; + os << '{' << v.d[i][0] << ',' << v.d[i][1] << '}'; + } + return os << '}'; +} + +std::ostream& operator<<(std::ostream& os, const EnumStruct& v) { + return os << "e1=" << (v.e1 == E1::A ? "A" : "B") << ",e2=" << (v.e2 == E2::X ? "X" : "Y"); +} + +template +py::array mkarray_via_buffer(size_t n) { + return py::array(py::buffer_info(nullptr, sizeof(T), + py::format_descriptor::format(), + 1, { n }, { sizeof(T) })); +} + +#define SET_TEST_VALS(s, i) do { \ + s.bool_ = (i) % 2 != 0; \ + s.uint_ = (uint32_t) (i); \ + s.float_ = (float) (i) * 1.5f; \ + s.ldbl_ = (long double) (i) * -2.5L; } while (0) + +template +py::array_t create_recarray(size_t n) { + auto arr = mkarray_via_buffer(n); + auto req = arr.request(); + auto ptr = static_cast(req.ptr); + for (size_t i = 0; i < n; i++) { + SET_TEST_VALS(ptr[i], i); + } + return arr; +} + +template +py::list print_recarray(py::array_t arr) { + const auto req = arr.request(); + const auto ptr = static_cast(req.ptr); + auto l = py::list(); + for (ssize_t i = 0; i < req.size; i++) { + std::stringstream ss; + ss << ptr[i]; + l.append(py::str(ss.str())); + } + return l; +} + +py::array_t test_array_ctors(int i) { + using arr_t = py::array_t; + + std::vector data { 1, 2, 3, 4, 5, 6 }; + std::vector shape { 3, 2 }; + std::vector strides { 8, 4 }; + + auto ptr = data.data(); + auto vptr = (void *) ptr; + auto dtype = py::dtype("int32"); + + py::buffer_info buf_ndim1(vptr, 4, "i", 6); + py::buffer_info buf_ndim1_null(nullptr, 4, "i", 6); + py::buffer_info buf_ndim2(vptr, 4, "i", 2, shape, strides); + py::buffer_info buf_ndim2_null(nullptr, 4, "i", 2, shape, strides); + + auto fill = [](py::array arr) { + auto req = arr.request(); + for (int i = 0; i < 6; i++) ((int32_t *) req.ptr)[i] = i + 1; + return arr; + }; + + switch (i) { + // shape: (3, 2) + case 10: return arr_t(shape, strides, ptr); + case 11: return py::array(shape, strides, ptr); + case 12: return py::array(dtype, shape, strides, vptr); + case 13: return arr_t(shape, ptr); + case 14: return py::array(shape, ptr); + case 15: return py::array(dtype, shape, vptr); + case 16: return arr_t(buf_ndim2); + case 17: return py::array(buf_ndim2); + // shape: (3, 2) - post-fill + case 20: return fill(arr_t(shape, strides)); + case 21: return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor + case 22: return fill(py::array(dtype, shape, strides)); + case 23: return fill(arr_t(shape)); + case 24: return py::array(shape, ptr); // can't have nullptr due to templated ctor + case 25: return fill(py::array(dtype, shape)); + case 26: return fill(arr_t(buf_ndim2_null)); + case 27: return fill(py::array(buf_ndim2_null)); + // shape: (6, ) + case 30: return arr_t(6, ptr); + case 31: return py::array(6, ptr); + case 32: return py::array(dtype, 6, vptr); + case 33: return arr_t(buf_ndim1); + case 34: return py::array(buf_ndim1); + // shape: (6, ) + case 40: return fill(arr_t(6)); + case 41: return py::array(6, ptr); // can't have nullptr due to templated ctor + case 42: return fill(py::array(dtype, 6)); + case 43: return fill(arr_t(buf_ndim1_null)); + case 44: return fill(py::array(buf_ndim1_null)); + } + return arr_t(); +} + +py::list test_dtype_ctors() { + py::list list; + list.append(py::dtype("int32")); + list.append(py::dtype(std::string("float64"))); + list.append(py::dtype::from_args(py::str("bool"))); + py::list names, offsets, formats; + py::dict dict; + names.append(py::str("a")); names.append(py::str("b")); dict["names"] = names; + offsets.append(py::int_(1)); offsets.append(py::int_(10)); dict["offsets"] = offsets; + formats.append(py::dtype("int32")); formats.append(py::dtype("float64")); dict["formats"] = formats; + dict["itemsize"] = py::int_(20); + list.append(py::dtype::from_args(dict)); + list.append(py::dtype(names, formats, offsets, 20)); + list.append(py::dtype(py::buffer_info((void *) 0, sizeof(unsigned int), "I", 1))); + list.append(py::dtype(py::buffer_info((void *) 0, 0, "T{i:a:f:b:}", 1))); + return list; +} + +TEST_SUBMODULE(numpy_dtypes, m) { + try { py::module::import("numpy"); } + catch (...) { return; } + + // typeinfo may be registered before the dtype descriptor for scalar casts to work... + py::class_(m, "SimpleStruct"); + + PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); + PYBIND11_NUMPY_DTYPE(PackedStruct, bool_, uint_, float_, ldbl_); + PYBIND11_NUMPY_DTYPE(NestedStruct, a, b); + PYBIND11_NUMPY_DTYPE(PartialStruct, bool_, uint_, float_, ldbl_); + PYBIND11_NUMPY_DTYPE(PartialNestedStruct, a); + PYBIND11_NUMPY_DTYPE(StringStruct, a, b); + PYBIND11_NUMPY_DTYPE(ArrayStruct, a, b, c, d); + PYBIND11_NUMPY_DTYPE(EnumStruct, e1, e2); + PYBIND11_NUMPY_DTYPE(ComplexStruct, cflt, cdbl); + + // ... or after + py::class_(m, "PackedStruct"); + + PYBIND11_NUMPY_DTYPE_EX(StructWithUglyNames, __x__, "x", __y__, "y"); + + // If uncommented, this should produce a static_assert failure telling the user that the struct + // is not a POD type +// struct NotPOD { std::string v; NotPOD() : v("hi") {}; }; +// PYBIND11_NUMPY_DTYPE(NotPOD, v); + + // test_recarray, test_scalar_conversion + m.def("create_rec_simple", &create_recarray); + m.def("create_rec_packed", &create_recarray); + m.def("create_rec_nested", [](size_t n) { // test_signature + py::array_t arr = mkarray_via_buffer(n); + auto req = arr.request(); + auto ptr = static_cast(req.ptr); + for (size_t i = 0; i < n; i++) { + SET_TEST_VALS(ptr[i].a, i); + SET_TEST_VALS(ptr[i].b, i + 1); + } + return arr; + }); + m.def("create_rec_partial", &create_recarray); + m.def("create_rec_partial_nested", [](size_t n) { + py::array_t arr = mkarray_via_buffer(n); + auto req = arr.request(); + auto ptr = static_cast(req.ptr); + for (size_t i = 0; i < n; i++) { + SET_TEST_VALS(ptr[i].a, i); + } + return arr; + }); + m.def("print_rec_simple", &print_recarray); + m.def("print_rec_packed", &print_recarray); + m.def("print_rec_nested", &print_recarray); + + // test_format_descriptors + m.def("get_format_unbound", []() { return py::format_descriptor::format(); }); + m.def("print_format_descriptors", []() { + py::list l; + for (const auto &fmt : { + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format(), + py::format_descriptor::format() + }) { + l.append(py::cast(fmt)); + } + return l; + }); + + // test_dtype + m.def("print_dtypes", []() { + py::list l; + for (const py::handle &d : { + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of(), + py::dtype::of() + }) + l.append(py::str(d)); + return l; + }); + m.def("test_dtype_ctors", &test_dtype_ctors); + m.def("test_dtype_methods", []() { + py::list list; + auto dt1 = py::dtype::of(); + auto dt2 = py::dtype::of(); + list.append(dt1); list.append(dt2); + list.append(py::bool_(dt1.has_fields())); list.append(py::bool_(dt2.has_fields())); + list.append(py::int_(dt1.itemsize())); list.append(py::int_(dt2.itemsize())); + return list; + }); + struct TrailingPaddingStruct { + int32_t a; + char b; + }; + PYBIND11_NUMPY_DTYPE(TrailingPaddingStruct, a, b); + m.def("trailing_padding_dtype", []() { return py::dtype::of(); }); + + // test_string_array + m.def("create_string_array", [](bool non_empty) { + py::array_t arr = mkarray_via_buffer(non_empty ? 4 : 0); + if (non_empty) { + auto req = arr.request(); + auto ptr = static_cast(req.ptr); + for (ssize_t i = 0; i < req.size * req.itemsize; i++) + static_cast(req.ptr)[i] = 0; + ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a'; + ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a'; + ptr[3].a[0] = 'a'; ptr[3].b[0] = 'a'; + + ptr[2].a[1] = 'b'; ptr[2].b[1] = 'b'; + ptr[3].a[1] = 'b'; ptr[3].b[1] = 'b'; + + ptr[3].a[2] = 'c'; ptr[3].b[2] = 'c'; + } + return arr; + }); + m.def("print_string_array", &print_recarray); + + // test_array_array + m.def("create_array_array", [](size_t n) { + py::array_t arr = mkarray_via_buffer(n); + auto ptr = (ArrayStruct *) arr.mutable_data(); + for (size_t i = 0; i < n; i++) { + for (size_t j = 0; j < 3; j++) + for (size_t k = 0; k < 4; k++) + ptr[i].a[j][k] = char('A' + (i * 100 + j * 10 + k) % 26); + for (size_t j = 0; j < 2; j++) + ptr[i].b[j] = int32_t(i * 1000 + j); + for (size_t j = 0; j < 3; j++) + ptr[i].c[j] = uint8_t(i * 10 + j); + for (size_t j = 0; j < 4; j++) + for (size_t k = 0; k < 2; k++) + ptr[i].d[j][k] = float(i) * 100.0f + float(j) * 10.0f + float(k); + } + return arr; + }); + m.def("print_array_array", &print_recarray); + + // test_enum_array + m.def("create_enum_array", [](size_t n) { + py::array_t arr = mkarray_via_buffer(n); + auto ptr = (EnumStruct *) arr.mutable_data(); + for (size_t i = 0; i < n; i++) { + ptr[i].e1 = static_cast(-1 + ((int) i % 2) * 2); + ptr[i].e2 = static_cast(1 + (i % 2)); + } + return arr; + }); + m.def("print_enum_array", &print_recarray); + + // test_complex_array + m.def("create_complex_array", [](size_t n) { + py::array_t arr = mkarray_via_buffer(n); + auto ptr = (ComplexStruct *) arr.mutable_data(); + for (size_t i = 0; i < n; i++) { + ptr[i].cflt.real(float(i)); + ptr[i].cflt.imag(float(i) + 0.25f); + ptr[i].cdbl.real(double(i) + 0.5); + ptr[i].cdbl.imag(double(i) + 0.75); + } + return arr; + }); + m.def("print_complex_array", &print_recarray); + + // test_array_constructors + m.def("test_array_ctors", &test_array_ctors); + + // test_compare_buffer_info + struct CompareStruct { + bool x; + uint32_t y; + float z; + }; + PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z); + m.def("compare_buffer_info", []() { + py::list list; + list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(float), "f", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(int), "I", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(long), "l", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1)))); + list.append(py::bool_(py::detail::compare_buffer_info::compare(py::buffer_info(nullptr, sizeof(CompareStruct), "T{?:x:3xI:y:f:z:}", 1)))); + return list; + }); + m.def("buffer_to_dtype", [](py::buffer& buf) { return py::dtype(buf.request()); }); + + // test_scalar_conversion + m.def("f_simple", [](SimpleStruct s) { return s.uint_ * 10; }); + m.def("f_packed", [](PackedStruct s) { return s.uint_ * 10; }); + m.def("f_nested", [](NestedStruct s) { return s.a.uint_ * 10; }); + + // test_register_dtype + m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); }); +} diff --git a/external/pybind11/tests/test_numpy_dtypes.py b/external/pybind11/tests/test_numpy_dtypes.py new file mode 100644 index 0000000..5f9a954 --- /dev/null +++ b/external/pybind11/tests/test_numpy_dtypes.py @@ -0,0 +1,298 @@ +import re +import pytest +from pybind11_tests import numpy_dtypes as m + +pytestmark = pytest.requires_numpy + +with pytest.suppress(ImportError): + import numpy as np + + +@pytest.fixture(scope='module') +def simple_dtype(): + ld = np.dtype('longdouble') + return np.dtype({'names': ['bool_', 'uint_', 'float_', 'ldbl_'], + 'formats': ['?', 'u4', 'f4', 'f{}'.format(ld.itemsize)], + 'offsets': [0, 4, 8, (16 if ld.alignment > 4 else 12)]}) + + +@pytest.fixture(scope='module') +def packed_dtype(): + return np.dtype([('bool_', '?'), ('uint_', 'u4'), ('float_', 'f4'), ('ldbl_', 'g')]) + + +def dt_fmt(): + from sys import byteorder + e = '<' if byteorder == 'little' else '>' + return ("{{'names':['bool_','uint_','float_','ldbl_']," + " 'formats':['?','" + e + "u4','" + e + "f4','" + e + "f{}']," + " 'offsets':[0,4,8,{}], 'itemsize':{}}}") + + +def simple_dtype_fmt(): + ld = np.dtype('longdouble') + simple_ld_off = 12 + 4 * (ld.alignment > 4) + return dt_fmt().format(ld.itemsize, simple_ld_off, simple_ld_off + ld.itemsize) + + +def packed_dtype_fmt(): + from sys import byteorder + return "[('bool_', '?'), ('uint_', '{e}u4'), ('float_', '{e}f4'), ('ldbl_', '{e}f{}')]".format( + np.dtype('longdouble').itemsize, e='<' if byteorder == 'little' else '>') + + +def partial_ld_offset(): + return 12 + 4 * (np.dtype('uint64').alignment > 4) + 8 + 8 * ( + np.dtype('longdouble').alignment > 8) + + +def partial_dtype_fmt(): + ld = np.dtype('longdouble') + partial_ld_off = partial_ld_offset() + return dt_fmt().format(ld.itemsize, partial_ld_off, partial_ld_off + ld.itemsize) + + +def partial_nested_fmt(): + ld = np.dtype('longdouble') + partial_nested_off = 8 + 8 * (ld.alignment > 8) + partial_ld_off = partial_ld_offset() + partial_nested_size = partial_nested_off * 2 + partial_ld_off + ld.itemsize + return "{{'names':['a'], 'formats':[{}], 'offsets':[{}], 'itemsize':{}}}".format( + partial_dtype_fmt(), partial_nested_off, partial_nested_size) + + +def assert_equal(actual, expected_data, expected_dtype): + np.testing.assert_equal(actual, np.array(expected_data, dtype=expected_dtype)) + + +def test_format_descriptors(): + with pytest.raises(RuntimeError) as excinfo: + m.get_format_unbound() + assert re.match('^NumPy type info missing for .*UnboundStruct.*$', str(excinfo.value)) + + ld = np.dtype('longdouble') + ldbl_fmt = ('4x' if ld.alignment > 4 else '') + ld.char + ss_fmt = "^T{?:bool_:3xI:uint_:f:float_:" + ldbl_fmt + ":ldbl_:}" + dbl = np.dtype('double') + partial_fmt = ("^T{?:bool_:3xI:uint_:f:float_:" + + str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) + + "xg:ldbl_:}") + nested_extra = str(max(8, ld.alignment)) + assert m.print_format_descriptors() == [ + ss_fmt, + "^T{?:bool_:I:uint_:f:float_:g:ldbl_:}", + "^T{" + ss_fmt + ":a:^T{?:bool_:I:uint_:f:float_:g:ldbl_:}:b:}", + partial_fmt, + "^T{" + nested_extra + "x" + partial_fmt + ":a:" + nested_extra + "x}", + "^T{3s:a:3s:b:}", + "^T{(3)4s:a:(2)i:b:(3)B:c:1x(4, 2)f:d:}", + '^T{q:e1:B:e2:}', + '^T{Zf:cflt:Zd:cdbl:}' + ] + + +def test_dtype(simple_dtype): + from sys import byteorder + e = '<' if byteorder == 'little' else '>' + + assert m.print_dtypes() == [ + simple_dtype_fmt(), + packed_dtype_fmt(), + "[('a', {}), ('b', {})]".format(simple_dtype_fmt(), packed_dtype_fmt()), + partial_dtype_fmt(), + partial_nested_fmt(), + "[('a', 'S3'), ('b', 'S3')]", + ("{{'names':['a','b','c','d'], " + + "'formats':[('S4', (3,)),(' simple_dtype.itemsize + assert_equal(arr, elements, simple_dtype) + assert_equal(arr, elements, packed_dtype) + + arr = m.create_rec_partial_nested(3) + assert str(arr.dtype) == partial_nested_fmt() + assert '' not in arr.dtype.fields + assert '' not in arr.dtype.fields['a'][0].fields + assert arr.dtype.itemsize > partial_dtype.itemsize + np.testing.assert_equal(arr['a'], m.create_rec_partial(3)) + + +def test_array_constructors(): + data = np.arange(1, 7, dtype='int32') + for i in range(8): + np.testing.assert_array_equal(m.test_array_ctors(10 + i), data.reshape((3, 2))) + np.testing.assert_array_equal(m.test_array_ctors(20 + i), data.reshape((3, 2))) + for i in range(5): + np.testing.assert_array_equal(m.test_array_ctors(30 + i), data) + np.testing.assert_array_equal(m.test_array_ctors(40 + i), data) + + +def test_string_array(): + arr = m.create_string_array(True) + assert str(arr.dtype) == "[('a', 'S3'), ('b', 'S3')]" + assert m.print_string_array(arr) == [ + "a='',b=''", + "a='a',b='a'", + "a='ab',b='ab'", + "a='abc',b='abc'" + ] + dtype = arr.dtype + assert arr['a'].tolist() == [b'', b'a', b'ab', b'abc'] + assert arr['b'].tolist() == [b'', b'a', b'ab', b'abc'] + arr = m.create_string_array(False) + assert dtype == arr.dtype + + +def test_array_array(): + from sys import byteorder + e = '<' if byteorder == 'little' else '>' + + arr = m.create_array_array(3) + assert str(arr.dtype) == ( + "{{'names':['a','b','c','d'], " + + "'formats':[('S4', (3,)),('' + + arr = m.create_enum_array(3) + dtype = arr.dtype + assert dtype == np.dtype([('e1', e + 'i8'), ('e2', 'u1')]) + assert m.print_enum_array(arr) == [ + "e1=A,e2=X", + "e1=B,e2=Y", + "e1=A,e2=X" + ] + assert arr['e1'].tolist() == [-1, 1, -1] + assert arr['e2'].tolist() == [1, 2, 1] + assert m.create_enum_array(0).dtype == dtype + + +def test_complex_array(): + from sys import byteorder + e = '<' if byteorder == 'little' else '>' + + arr = m.create_complex_array(3) + dtype = arr.dtype + assert dtype == np.dtype([('cflt', e + 'c8'), ('cdbl', e + 'c16')]) + assert m.print_complex_array(arr) == [ + "c:(0,0.25),(0.5,0.75)", + "c:(1,1.25),(1.5,1.75)", + "c:(2,2.25),(2.5,2.75)" + ] + assert arr['cflt'].tolist() == [0.0 + 0.25j, 1.0 + 1.25j, 2.0 + 2.25j] + assert arr['cdbl'].tolist() == [0.5 + 0.75j, 1.5 + 1.75j, 2.5 + 2.75j] + assert m.create_complex_array(0).dtype == dtype + + +def test_signature(doc): + assert doc(m.create_rec_nested) == \ + "create_rec_nested(arg0: int) -> numpy.ndarray[NestedStruct]" + + +def test_scalar_conversion(): + n = 3 + arrays = [m.create_rec_simple(n), m.create_rec_packed(n), + m.create_rec_nested(n), m.create_enum_array(n)] + funcs = [m.f_simple, m.f_packed, m.f_nested] + + for i, func in enumerate(funcs): + for j, arr in enumerate(arrays): + if i == j and i < 2: + assert [func(arr[k]) for k in range(n)] == [k * 10 for k in range(n)] + else: + with pytest.raises(TypeError) as excinfo: + func(arr[0]) + assert 'incompatible function arguments' in str(excinfo.value) + + +def test_register_dtype(): + with pytest.raises(RuntimeError) as excinfo: + m.register_dtype() + assert 'dtype is already registered' in str(excinfo.value) + + +@pytest.requires_numpy +def test_compare_buffer_info(): + assert all(m.compare_buffer_info()) diff --git a/external/pybind11/tests/test_numpy_vectorize.cpp b/external/pybind11/tests/test_numpy_vectorize.cpp new file mode 100644 index 0000000..a875a74 --- /dev/null +++ b/external/pybind11/tests/test_numpy_vectorize.cpp @@ -0,0 +1,89 @@ +/* + tests/test_numpy_vectorize.cpp -- auto-vectorize functions over NumPy array + arguments + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +double my_func(int x, float y, double z) { + py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z)); + return (float) x*y*z; +} + +TEST_SUBMODULE(numpy_vectorize, m) { + try { py::module::import("numpy"); } + catch (...) { return; } + + // test_vectorize, test_docs, test_array_collapse + // Vectorize all arguments of a function (though non-vector arguments are also allowed) + m.def("vectorized_func", py::vectorize(my_func)); + + // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization) + m.def("vectorized_func2", + [](py::array_t x, py::array_t y, float z) { + return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(x, y); + } + ); + + // Vectorize a complex-valued function + m.def("vectorized_func3", py::vectorize( + [](std::complex c) { return c * std::complex(2.f); } + )); + + // test_type_selection + // Numpy function which only accepts specific data types + m.def("selective_func", [](py::array_t) { return "Int branch taken."; }); + m.def("selective_func", [](py::array_t) { return "Float branch taken."; }); + m.def("selective_func", [](py::array_t, py::array::c_style>) { return "Complex float branch taken."; }); + + + // test_passthrough_arguments + // Passthrough test: references and non-pod types should be automatically passed through (in the + // function definition below, only `b`, `d`, and `g` are vectorized): + struct NonPODClass { + NonPODClass(int v) : value{v} {} + int value; + }; + py::class_(m, "NonPODClass").def(py::init()); + m.def("vec_passthrough", py::vectorize( + [](double *a, double b, py::array_t c, const int &d, int &e, NonPODClass f, const double g) { + return *a + b + c.at(0) + d + e + f.value + g; + } + )); + + // test_method_vectorization + struct VectorizeTestClass { + VectorizeTestClass(int v) : value{v} {}; + float method(int x, float y) { return y + (float) (x + value); } + int value = 0; + }; + py::class_ vtc(m, "VectorizeTestClass"); + vtc .def(py::init()) + .def_readwrite("value", &VectorizeTestClass::value); + + // Automatic vectorizing of methods + vtc.def("method", py::vectorize(&VectorizeTestClass::method)); + + // test_trivial_broadcasting + // Internal optimization test for whether the input is trivially broadcastable: + py::enum_(m, "trivial") + .value("f_trivial", py::detail::broadcast_trivial::f_trivial) + .value("c_trivial", py::detail::broadcast_trivial::c_trivial) + .value("non_trivial", py::detail::broadcast_trivial::non_trivial); + m.def("vectorized_is_trivial", []( + py::array_t arg1, + py::array_t arg2, + py::array_t arg3 + ) { + ssize_t ndim; + std::vector shape; + std::array buffers {{ arg1.request(), arg2.request(), arg3.request() }}; + return py::detail::broadcast(buffers, ndim, shape); + }); +} diff --git a/external/pybind11/tests/test_numpy_vectorize.py b/external/pybind11/tests/test_numpy_vectorize.py new file mode 100644 index 0000000..0e9c883 --- /dev/null +++ b/external/pybind11/tests/test_numpy_vectorize.py @@ -0,0 +1,196 @@ +import pytest +from pybind11_tests import numpy_vectorize as m + +pytestmark = pytest.requires_numpy + +with pytest.suppress(ImportError): + import numpy as np + + +def test_vectorize(capture): + assert np.isclose(m.vectorized_func3(np.array(3 + 7j)), [6 + 14j]) + + for f in [m.vectorized_func, m.vectorized_func2]: + with capture: + assert np.isclose(f(1, 2, 3), 6) + assert capture == "my_func(x:int=1, y:float=2, z:float=3)" + with capture: + assert np.isclose(f(np.array(1), np.array(2), 3), 6) + assert capture == "my_func(x:int=1, y:float=2, z:float=3)" + with capture: + assert np.allclose(f(np.array([1, 3]), np.array([2, 4]), 3), [6, 36]) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=3) + my_func(x:int=3, y:float=4, z:float=3) + """ + with capture: + a = np.array([[1, 2], [3, 4]], order='F') + b = np.array([[10, 20], [30, 40]], order='F') + c = 3 + result = f(a, b, c) + assert np.allclose(result, a * b * c) + assert result.flags.f_contiguous + # All inputs are F order and full or singletons, so we the result is in col-major order: + assert capture == """ + my_func(x:int=1, y:float=10, z:float=3) + my_func(x:int=3, y:float=30, z:float=3) + my_func(x:int=2, y:float=20, z:float=3) + my_func(x:int=4, y:float=40, z:float=3) + """ + with capture: + a, b, c = np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3 + assert np.allclose(f(a, b, c), a * b * c) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=3) + my_func(x:int=3, y:float=4, z:float=3) + my_func(x:int=5, y:float=6, z:float=3) + my_func(x:int=7, y:float=8, z:float=3) + my_func(x:int=9, y:float=10, z:float=3) + my_func(x:int=11, y:float=12, z:float=3) + """ + with capture: + a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2 + assert np.allclose(f(a, b, c), a * b * c) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=2) + my_func(x:int=2, y:float=3, z:float=2) + my_func(x:int=3, y:float=4, z:float=2) + my_func(x:int=4, y:float=2, z:float=2) + my_func(x:int=5, y:float=3, z:float=2) + my_func(x:int=6, y:float=4, z:float=2) + """ + with capture: + a, b, c = np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2 + assert np.allclose(f(a, b, c), a * b * c) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=2) + my_func(x:int=2, y:float=2, z:float=2) + my_func(x:int=3, y:float=2, z:float=2) + my_func(x:int=4, y:float=3, z:float=2) + my_func(x:int=5, y:float=3, z:float=2) + my_func(x:int=6, y:float=3, z:float=2) + """ + with capture: + a, b, c = np.array([[1, 2, 3], [4, 5, 6]], order='F'), np.array([[2], [3]]), 2 + assert np.allclose(f(a, b, c), a * b * c) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=2) + my_func(x:int=2, y:float=2, z:float=2) + my_func(x:int=3, y:float=2, z:float=2) + my_func(x:int=4, y:float=3, z:float=2) + my_func(x:int=5, y:float=3, z:float=2) + my_func(x:int=6, y:float=3, z:float=2) + """ + with capture: + a, b, c = np.array([[1, 2, 3], [4, 5, 6]])[::, ::2], np.array([[2], [3]]), 2 + assert np.allclose(f(a, b, c), a * b * c) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=2) + my_func(x:int=3, y:float=2, z:float=2) + my_func(x:int=4, y:float=3, z:float=2) + my_func(x:int=6, y:float=3, z:float=2) + """ + with capture: + a, b, c = np.array([[1, 2, 3], [4, 5, 6]], order='F')[::, ::2], np.array([[2], [3]]), 2 + assert np.allclose(f(a, b, c), a * b * c) + assert capture == """ + my_func(x:int=1, y:float=2, z:float=2) + my_func(x:int=3, y:float=2, z:float=2) + my_func(x:int=4, y:float=3, z:float=2) + my_func(x:int=6, y:float=3, z:float=2) + """ + + +def test_type_selection(): + assert m.selective_func(np.array([1], dtype=np.int32)) == "Int branch taken." + assert m.selective_func(np.array([1.0], dtype=np.float32)) == "Float branch taken." + assert m.selective_func(np.array([1.0j], dtype=np.complex64)) == "Complex float branch taken." + + +def test_docs(doc): + assert doc(m.vectorized_func) == """ + vectorized_func(arg0: numpy.ndarray[int32], arg1: numpy.ndarray[float32], arg2: numpy.ndarray[float64]) -> object + """ # noqa: E501 line too long + + +def test_trivial_broadcasting(): + trivial, vectorized_is_trivial = m.trivial, m.vectorized_is_trivial + + assert vectorized_is_trivial(1, 2, 3) == trivial.c_trivial + assert vectorized_is_trivial(np.array(1), np.array(2), 3) == trivial.c_trivial + assert vectorized_is_trivial(np.array([1, 3]), np.array([2, 4]), 3) == trivial.c_trivial + assert trivial.c_trivial == vectorized_is_trivial( + np.array([[1, 3, 5], [7, 9, 11]]), np.array([[2, 4, 6], [8, 10, 12]]), 3) + assert vectorized_is_trivial( + np.array([[1, 2, 3], [4, 5, 6]]), np.array([2, 3, 4]), 2) == trivial.non_trivial + assert vectorized_is_trivial( + np.array([[1, 2, 3], [4, 5, 6]]), np.array([[2], [3]]), 2) == trivial.non_trivial + z1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype='int32') + z2 = np.array(z1, dtype='float32') + z3 = np.array(z1, dtype='float64') + assert vectorized_is_trivial(z1, z2, z3) == trivial.c_trivial + assert vectorized_is_trivial(1, z2, z3) == trivial.c_trivial + assert vectorized_is_trivial(z1, 1, z3) == trivial.c_trivial + assert vectorized_is_trivial(z1, z2, 1) == trivial.c_trivial + assert vectorized_is_trivial(z1[::2, ::2], 1, 1) == trivial.non_trivial + assert vectorized_is_trivial(1, 1, z1[::2, ::2]) == trivial.c_trivial + assert vectorized_is_trivial(1, 1, z3[::2, ::2]) == trivial.non_trivial + assert vectorized_is_trivial(z1, 1, z3[1::4, 1::4]) == trivial.c_trivial + + y1 = np.array(z1, order='F') + y2 = np.array(y1) + y3 = np.array(y1) + assert vectorized_is_trivial(y1, y2, y3) == trivial.f_trivial + assert vectorized_is_trivial(y1, 1, 1) == trivial.f_trivial + assert vectorized_is_trivial(1, y2, 1) == trivial.f_trivial + assert vectorized_is_trivial(1, 1, y3) == trivial.f_trivial + assert vectorized_is_trivial(y1, z2, 1) == trivial.non_trivial + assert vectorized_is_trivial(z1[1::4, 1::4], y2, 1) == trivial.f_trivial + assert vectorized_is_trivial(y1[1::4, 1::4], z2, 1) == trivial.c_trivial + + assert m.vectorized_func(z1, z2, z3).flags.c_contiguous + assert m.vectorized_func(y1, y2, y3).flags.f_contiguous + assert m.vectorized_func(z1, 1, 1).flags.c_contiguous + assert m.vectorized_func(1, y2, 1).flags.f_contiguous + assert m.vectorized_func(z1[1::4, 1::4], y2, 1).flags.f_contiguous + assert m.vectorized_func(y1[1::4, 1::4], z2, 1).flags.c_contiguous + + +def test_passthrough_arguments(doc): + assert doc(m.vec_passthrough) == ( + "vec_passthrough(" + ", ".join([ + "arg0: float", + "arg1: numpy.ndarray[float64]", + "arg2: numpy.ndarray[float64]", + "arg3: numpy.ndarray[int32]", + "arg4: int", + "arg5: m.numpy_vectorize.NonPODClass", + "arg6: numpy.ndarray[float64]"]) + ") -> object") + + b = np.array([[10, 20, 30]], dtype='float64') + c = np.array([100, 200]) # NOT a vectorized argument + d = np.array([[1000], [2000], [3000]], dtype='int') + g = np.array([[1000000, 2000000, 3000000]], dtype='int') # requires casting + assert np.all( + m.vec_passthrough(1, b, c, d, 10000, m.NonPODClass(100000), g) == + np.array([[1111111, 2111121, 3111131], + [1112111, 2112121, 3112131], + [1113111, 2113121, 3113131]])) + + +def test_method_vectorization(): + o = m.VectorizeTestClass(3) + x = np.array([1, 2], dtype='int') + y = np.array([[10], [20]], dtype='float32') + assert np.all(o.method(x, y) == [[14, 15], [24, 25]]) + + +def test_array_collapse(): + assert not isinstance(m.vectorized_func(1, 2, 3), np.ndarray) + assert not isinstance(m.vectorized_func(np.array(1), 2, 3), np.ndarray) + z = m.vectorized_func([1], 2, 3) + assert isinstance(z, np.ndarray) + assert z.shape == (1, ) + z = m.vectorized_func(1, [[[2]]], 3) + assert isinstance(z, np.ndarray) + assert z.shape == (1, 1, 1) diff --git a/external/pybind11/tests/test_opaque_types.cpp b/external/pybind11/tests/test_opaque_types.cpp new file mode 100644 index 0000000..5e83df0 --- /dev/null +++ b/external/pybind11/tests/test_opaque_types.cpp @@ -0,0 +1,63 @@ +/* + tests/test_opaque_types.cpp -- opaque types, passing void pointers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include +#include + +using StringList = std::vector; + +/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */ +PYBIND11_MAKE_OPAQUE(StringList); + +TEST_SUBMODULE(opaque_types, m) { + // test_string_list + py::class_(m, "StringList") + .def(py::init<>()) + .def("pop_back", &StringList::pop_back) + /* There are multiple versions of push_back(), etc. Select the right ones. */ + .def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back) + .def("back", (std::string &(StringList::*)()) &StringList::back) + .def("__len__", [](const StringList &v) { return v.size(); }) + .def("__iter__", [](StringList &v) { + return py::make_iterator(v.begin(), v.end()); + }, py::keep_alive<0, 1>()); + + class ClassWithSTLVecProperty { + public: + StringList stringList; + }; + py::class_(m, "ClassWithSTLVecProperty") + .def(py::init<>()) + .def_readwrite("stringList", &ClassWithSTLVecProperty::stringList); + + m.def("print_opaque_list", [](const StringList &l) { + std::string ret = "Opaque list: ["; + bool first = true; + for (auto entry : l) { + if (!first) + ret += ", "; + ret += entry; + first = false; + } + return ret + "]"; + }); + + // test_pointers + m.def("return_void_ptr", []() { return (void *) 0x1234; }); + m.def("get_void_ptr_value", [](void *ptr) { return reinterpret_cast(ptr); }); + m.def("return_null_str", []() { return (char *) nullptr; }); + m.def("get_null_str_value", [](char *ptr) { return reinterpret_cast(ptr); }); + + m.def("return_unique_ptr", []() -> std::unique_ptr { + StringList *result = new StringList(); + result->push_back("some value"); + return std::unique_ptr(result); + }); +} diff --git a/external/pybind11/tests/test_opaque_types.py b/external/pybind11/tests/test_opaque_types.py new file mode 100644 index 0000000..2d3aef5 --- /dev/null +++ b/external/pybind11/tests/test_opaque_types.py @@ -0,0 +1,46 @@ +import pytest +from pybind11_tests import opaque_types as m +from pybind11_tests import ConstructorStats, UserType + + +def test_string_list(): + l = m.StringList() + l.push_back("Element 1") + l.push_back("Element 2") + assert m.print_opaque_list(l) == "Opaque list: [Element 1, Element 2]" + assert l.back() == "Element 2" + + for i, k in enumerate(l, start=1): + assert k == "Element {}".format(i) + l.pop_back() + assert m.print_opaque_list(l) == "Opaque list: [Element 1]" + + cvp = m.ClassWithSTLVecProperty() + assert m.print_opaque_list(cvp.stringList) == "Opaque list: []" + + cvp.stringList = l + cvp.stringList.push_back("Element 3") + assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]" + + +def test_pointers(msg): + living_before = ConstructorStats.get(UserType).alive() + assert m.get_void_ptr_value(m.return_void_ptr()) == 0x1234 + assert m.get_void_ptr_value(UserType()) # Should also work for other C++ types + assert ConstructorStats.get(UserType).alive() == living_before + + with pytest.raises(TypeError) as excinfo: + m.get_void_ptr_value([1, 2, 3]) # This should not work + assert msg(excinfo.value) == """ + get_void_ptr_value(): incompatible function arguments. The following argument types are supported: + 1. (arg0: capsule) -> int + + Invoked with: [1, 2, 3] + """ # noqa: E501 line too long + + assert m.return_null_str() is None + assert m.get_null_str_value(m.return_null_str()) is not None + + ptr = m.return_unique_ptr() + assert "StringList" in repr(ptr) + assert m.print_opaque_list(ptr) == "Opaque list: [some value]" diff --git a/external/pybind11/tests/test_operator_overloading.cpp b/external/pybind11/tests/test_operator_overloading.cpp new file mode 100644 index 0000000..4ad34d1 --- /dev/null +++ b/external/pybind11/tests/test_operator_overloading.cpp @@ -0,0 +1,146 @@ +/* + tests/test_operator_overloading.cpp -- operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include +#include + +class Vector2 { +public: + Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); } + Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); } + Vector2(Vector2 &&v) : x(v.x), y(v.y) { print_move_created(this); v.x = v.y = 0; } + Vector2 &operator=(const Vector2 &v) { x = v.x; y = v.y; print_copy_assigned(this); return *this; } + Vector2 &operator=(Vector2 &&v) { x = v.x; y = v.y; v.x = v.y = 0; print_move_assigned(this); return *this; } + ~Vector2() { print_destroyed(this); } + + std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; } + + Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); } + Vector2 operator-(const Vector2 &v) const { return Vector2(x - v.x, y - v.y); } + Vector2 operator-(float value) const { return Vector2(x - value, y - value); } + Vector2 operator+(float value) const { return Vector2(x + value, y + value); } + Vector2 operator*(float value) const { return Vector2(x * value, y * value); } + Vector2 operator/(float value) const { return Vector2(x / value, y / value); } + Vector2 operator*(const Vector2 &v) const { return Vector2(x * v.x, y * v.y); } + Vector2 operator/(const Vector2 &v) const { return Vector2(x / v.x, y / v.y); } + Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; } + Vector2& operator-=(const Vector2 &v) { x -= v.x; y -= v.y; return *this; } + Vector2& operator*=(float v) { x *= v; y *= v; return *this; } + Vector2& operator/=(float v) { x /= v; y /= v; return *this; } + Vector2& operator*=(const Vector2 &v) { x *= v.x; y *= v.y; return *this; } + Vector2& operator/=(const Vector2 &v) { x /= v.x; y /= v.y; return *this; } + + friend Vector2 operator+(float f, const Vector2 &v) { return Vector2(f + v.x, f + v.y); } + friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); } + friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); } + friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); } +private: + float x, y; +}; + +class C1 { }; +class C2 { }; + +int operator+(const C1 &, const C1 &) { return 11; } +int operator+(const C2 &, const C2 &) { return 22; } +int operator+(const C2 &, const C1 &) { return 21; } +int operator+(const C1 &, const C2 &) { return 12; } + +namespace std { + template<> + struct hash { + // Not a good hash function, but easy to test + size_t operator()(const Vector2 &) { return 4; } + }; +} + +TEST_SUBMODULE(operators, m) { + + // test_operator_overloading + py::class_(m, "Vector2") + .def(py::init()) + .def(py::self + py::self) + .def(py::self + float()) + .def(py::self - py::self) + .def(py::self - float()) + .def(py::self * float()) + .def(py::self / float()) + .def(py::self * py::self) + .def(py::self / py::self) + .def(py::self += py::self) + .def(py::self -= py::self) + .def(py::self *= float()) + .def(py::self /= float()) + .def(py::self *= py::self) + .def(py::self /= py::self) + .def(float() + py::self) + .def(float() - py::self) + .def(float() * py::self) + .def(float() / py::self) + .def("__str__", &Vector2::toString) + .def(hash(py::self)) + ; + + m.attr("Vector") = m.attr("Vector2"); + + // test_operators_notimplemented + // #393: need to return NotSupported to ensure correct arithmetic operator behavior + py::class_(m, "C1") + .def(py::init<>()) + .def(py::self + py::self); + + py::class_(m, "C2") + .def(py::init<>()) + .def(py::self + py::self) + .def("__add__", [](const C2& c2, const C1& c1) { return c2 + c1; }) + .def("__radd__", [](const C2& c2, const C1& c1) { return c1 + c2; }); + + // test_nested + // #328: first member in a class can't be used in operators + struct NestABase { int value = -2; }; + py::class_(m, "NestABase") + .def(py::init<>()) + .def_readwrite("value", &NestABase::value); + + struct NestA : NestABase { + int value = 3; + NestA& operator+=(int i) { value += i; return *this; } + }; + py::class_(m, "NestA") + .def(py::init<>()) + .def(py::self += int()) + .def("as_base", [](NestA &a) -> NestABase& { + return (NestABase&) a; + }, py::return_value_policy::reference_internal); + m.def("get_NestA", [](const NestA &a) { return a.value; }); + + struct NestB { + NestA a; + int value = 4; + NestB& operator-=(int i) { value -= i; return *this; } + }; + py::class_(m, "NestB") + .def(py::init<>()) + .def(py::self -= int()) + .def_readwrite("a", &NestB::a); + m.def("get_NestB", [](const NestB &b) { return b.value; }); + + struct NestC { + NestB b; + int value = 5; + NestC& operator*=(int i) { value *= i; return *this; } + }; + py::class_(m, "NestC") + .def(py::init<>()) + .def(py::self *= int()) + .def_readwrite("b", &NestC::b); + m.def("get_NestC", [](const NestC &c) { return c.value; }); +} diff --git a/external/pybind11/tests/test_operator_overloading.py b/external/pybind11/tests/test_operator_overloading.py new file mode 100644 index 0000000..0d80e5e --- /dev/null +++ b/external/pybind11/tests/test_operator_overloading.py @@ -0,0 +1,106 @@ +import pytest +from pybind11_tests import operators as m +from pybind11_tests import ConstructorStats + + +def test_operator_overloading(): + v1 = m.Vector2(1, 2) + v2 = m.Vector(3, -1) + assert str(v1) == "[1.000000, 2.000000]" + assert str(v2) == "[3.000000, -1.000000]" + + assert str(v1 + v2) == "[4.000000, 1.000000]" + assert str(v1 - v2) == "[-2.000000, 3.000000]" + assert str(v1 - 8) == "[-7.000000, -6.000000]" + assert str(v1 + 8) == "[9.000000, 10.000000]" + assert str(v1 * 8) == "[8.000000, 16.000000]" + assert str(v1 / 8) == "[0.125000, 0.250000]" + assert str(8 - v1) == "[7.000000, 6.000000]" + assert str(8 + v1) == "[9.000000, 10.000000]" + assert str(8 * v1) == "[8.000000, 16.000000]" + assert str(8 / v1) == "[8.000000, 4.000000]" + assert str(v1 * v2) == "[3.000000, -2.000000]" + assert str(v2 / v1) == "[3.000000, -0.500000]" + + v1 += 2 * v2 + assert str(v1) == "[7.000000, 0.000000]" + v1 -= v2 + assert str(v1) == "[4.000000, 1.000000]" + v1 *= 2 + assert str(v1) == "[8.000000, 2.000000]" + v1 /= 16 + assert str(v1) == "[0.500000, 0.125000]" + v1 *= v2 + assert str(v1) == "[1.500000, -0.125000]" + v2 /= v1 + assert str(v2) == "[2.000000, 8.000000]" + + assert hash(v1) == 4 + + cstats = ConstructorStats.get(m.Vector2) + assert cstats.alive() == 2 + del v1 + assert cstats.alive() == 1 + del v2 + assert cstats.alive() == 0 + assert cstats.values() == ['[1.000000, 2.000000]', '[3.000000, -1.000000]', + '[4.000000, 1.000000]', '[-2.000000, 3.000000]', + '[-7.000000, -6.000000]', '[9.000000, 10.000000]', + '[8.000000, 16.000000]', '[0.125000, 0.250000]', + '[7.000000, 6.000000]', '[9.000000, 10.000000]', + '[8.000000, 16.000000]', '[8.000000, 4.000000]', + '[3.000000, -2.000000]', '[3.000000, -0.500000]', + '[6.000000, -2.000000]'] + assert cstats.default_constructions == 0 + assert cstats.copy_constructions == 0 + assert cstats.move_constructions >= 10 + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + +def test_operators_notimplemented(): + """#393: need to return NotSupported to ensure correct arithmetic operator behavior""" + + c1, c2 = m.C1(), m.C2() + assert c1 + c1 == 11 + assert c2 + c2 == 22 + assert c2 + c1 == 21 + assert c1 + c2 == 12 + + +def test_nested(): + """#328: first member in a class can't be used in operators""" + + a = m.NestA() + b = m.NestB() + c = m.NestC() + + a += 10 + assert m.get_NestA(a) == 13 + b.a += 100 + assert m.get_NestA(b.a) == 103 + c.b.a += 1000 + assert m.get_NestA(c.b.a) == 1003 + b -= 1 + assert m.get_NestB(b) == 3 + c.b -= 3 + assert m.get_NestB(c.b) == 1 + c *= 7 + assert m.get_NestC(c) == 35 + + abase = a.as_base() + assert abase.value == -2 + a.as_base().value += 44 + assert abase.value == 42 + assert c.b.a.as_base().value == -2 + c.b.a.as_base().value += 44 + assert c.b.a.as_base().value == 42 + + del c + pytest.gc_collect() + del a # Should't delete while abase is still alive + pytest.gc_collect() + + assert abase.value == 42 + del abase, b + pytest.gc_collect() diff --git a/external/pybind11/tests/test_pickling.cpp b/external/pybind11/tests/test_pickling.cpp new file mode 100644 index 0000000..9dc63bd --- /dev/null +++ b/external/pybind11/tests/test_pickling.cpp @@ -0,0 +1,130 @@ +/* + tests/test_pickling.cpp -- pickle support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +TEST_SUBMODULE(pickling, m) { + // test_roundtrip + class Pickleable { + public: + Pickleable(const std::string &value) : m_value(value) { } + const std::string &value() const { return m_value; } + + void setExtra1(int extra1) { m_extra1 = extra1; } + void setExtra2(int extra2) { m_extra2 = extra2; } + int extra1() const { return m_extra1; } + int extra2() const { return m_extra2; } + private: + std::string m_value; + int m_extra1 = 0; + int m_extra2 = 0; + }; + + class PickleableNew : public Pickleable { + public: + using Pickleable::Pickleable; + }; + + py::class_(m, "Pickleable") + .def(py::init()) + .def("value", &Pickleable::value) + .def("extra1", &Pickleable::extra1) + .def("extra2", &Pickleable::extra2) + .def("setExtra1", &Pickleable::setExtra1) + .def("setExtra2", &Pickleable::setExtra2) + // For details on the methods below, refer to + // http://docs.python.org/3/library/pickle.html#pickling-class-instances + .def("__getstate__", [](const Pickleable &p) { + /* Return a tuple that fully encodes the state of the object */ + return py::make_tuple(p.value(), p.extra1(), p.extra2()); + }) + .def("__setstate__", [](Pickleable &p, py::tuple t) { + if (t.size() != 3) + throw std::runtime_error("Invalid state!"); + /* Invoke the constructor (need to use in-place version) */ + new (&p) Pickleable(t[0].cast()); + + /* Assign any additional state */ + p.setExtra1(t[1].cast()); + p.setExtra2(t[2].cast()); + }); + + py::class_(m, "PickleableNew") + .def(py::init()) + .def(py::pickle( + [](const PickleableNew &p) { + return py::make_tuple(p.value(), p.extra1(), p.extra2()); + }, + [](py::tuple t) { + if (t.size() != 3) + throw std::runtime_error("Invalid state!"); + auto p = PickleableNew(t[0].cast()); + + p.setExtra1(t[1].cast()); + p.setExtra2(t[2].cast()); + return p; + } + )); + +#if !defined(PYPY_VERSION) + // test_roundtrip_with_dict + class PickleableWithDict { + public: + PickleableWithDict(const std::string &value) : value(value) { } + + std::string value; + int extra; + }; + + class PickleableWithDictNew : public PickleableWithDict { + public: + using PickleableWithDict::PickleableWithDict; + }; + + py::class_(m, "PickleableWithDict", py::dynamic_attr()) + .def(py::init()) + .def_readwrite("value", &PickleableWithDict::value) + .def_readwrite("extra", &PickleableWithDict::extra) + .def("__getstate__", [](py::object self) { + /* Also include __dict__ in state */ + return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); + }) + .def("__setstate__", [](py::object self, py::tuple t) { + if (t.size() != 3) + throw std::runtime_error("Invalid state!"); + /* Cast and construct */ + auto& p = self.cast(); + new (&p) PickleableWithDict(t[0].cast()); + + /* Assign C++ state */ + p.extra = t[1].cast(); + + /* Assign Python state */ + self.attr("__dict__") = t[2]; + }); + + py::class_(m, "PickleableWithDictNew") + .def(py::init()) + .def(py::pickle( + [](py::object self) { + return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); + }, + [](const py::tuple &t) { + if (t.size() != 3) + throw std::runtime_error("Invalid state!"); + + auto cpp_state = PickleableWithDictNew(t[0].cast()); + cpp_state.extra = t[1].cast(); + + auto py_state = t[2].cast(); + return std::make_pair(cpp_state, py_state); + } + )); +#endif +} diff --git a/external/pybind11/tests/test_pickling.py b/external/pybind11/tests/test_pickling.py new file mode 100644 index 0000000..707d347 --- /dev/null +++ b/external/pybind11/tests/test_pickling.py @@ -0,0 +1,36 @@ +import pytest +from pybind11_tests import pickling as m + +try: + import cPickle as pickle # Use cPickle on Python 2.7 +except ImportError: + import pickle + + +@pytest.mark.parametrize("cls_name", ["Pickleable", "PickleableNew"]) +def test_roundtrip(cls_name): + cls = getattr(m, cls_name) + p = cls("test_value") + p.setExtra1(15) + p.setExtra2(48) + + data = pickle.dumps(p, 2) # Must use pickle protocol >= 2 + p2 = pickle.loads(data) + assert p2.value() == p.value() + assert p2.extra1() == p.extra1() + assert p2.extra2() == p.extra2() + + +@pytest.unsupported_on_pypy +@pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) +def test_roundtrip_with_dict(cls_name): + cls = getattr(m, cls_name) + p = cls("test_value") + p.extra = 15 + p.dynamic = "Attribute" + + data = pickle.dumps(p, pickle.HIGHEST_PROTOCOL) + p2 = pickle.loads(data) + assert p2.value == p.value + assert p2.extra == p.extra + assert p2.dynamic == p.dynamic diff --git a/external/pybind11/tests/test_pytypes.cpp b/external/pybind11/tests/test_pytypes.cpp new file mode 100644 index 0000000..a962f0c --- /dev/null +++ b/external/pybind11/tests/test_pytypes.cpp @@ -0,0 +1,272 @@ +/* + tests/test_pytypes.cpp -- Python type casters + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + + +TEST_SUBMODULE(pytypes, m) { + // test_list + m.def("get_list", []() { + py::list list; + list.append("value"); + py::print("Entry at position 0:", list[0]); + list[0] = py::str("overwritten"); + return list; + }); + m.def("print_list", [](py::list list) { + int index = 0; + for (auto item : list) + py::print("list item {}: {}"_s.format(index++, item)); + }); + + // test_set + m.def("get_set", []() { + py::set set; + set.add(py::str("key1")); + set.add("key2"); + set.add(std::string("key3")); + return set; + }); + m.def("print_set", [](py::set set) { + for (auto item : set) + py::print("key:", item); + }); + + // test_dict + m.def("get_dict", []() { return py::dict("key"_a="value"); }); + m.def("print_dict", [](py::dict dict) { + for (auto item : dict) + py::print("key: {}, value={}"_s.format(item.first, item.second)); + }); + m.def("dict_keyword_constructor", []() { + auto d1 = py::dict("x"_a=1, "y"_a=2); + auto d2 = py::dict("z"_a=3, **d1); + return d2; + }); + + // test_str + m.def("str_from_string", []() { return py::str(std::string("baz")); }); + m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); }); + m.def("str_from_object", [](const py::object& obj) { return py::str(obj); }); + m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); }); + + m.def("str_format", []() { + auto s1 = "{} + {} = {}"_s.format(1, 2, 3); + auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3); + return py::make_tuple(s1, s2); + }); + + // test_bytes + m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); }); + m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); }); + + // test_capsule + m.def("return_capsule_with_destructor", []() { + py::print("creating capsule"); + return py::capsule([]() { + py::print("destructing capsule"); + }); + }); + + m.def("return_capsule_with_destructor_2", []() { + py::print("creating capsule"); + return py::capsule((void *) 1234, [](void *ptr) { + py::print("destructing capsule: {}"_s.format((size_t) ptr)); + }); + }); + + m.def("return_capsule_with_name_and_destructor", []() { + auto capsule = py::capsule((void *) 1234, "pointer type description", [](PyObject *ptr) { + if (ptr) { + auto name = PyCapsule_GetName(ptr); + py::print("destructing capsule ({}, '{}')"_s.format( + (size_t) PyCapsule_GetPointer(ptr, name), name + )); + } + }); + void *contents = capsule; + py::print("created capsule ({}, '{}')"_s.format((size_t) contents, capsule.name())); + return capsule; + }); + + // test_accessors + m.def("accessor_api", [](py::object o) { + auto d = py::dict(); + + d["basic_attr"] = o.attr("basic_attr"); + + auto l = py::list(); + for (const auto &item : o.attr("begin_end")) { + l.append(item); + } + d["begin_end"] = l; + + d["operator[object]"] = o.attr("d")["operator[object]"_s]; + d["operator[char *]"] = o.attr("d")["operator[char *]"]; + + d["attr(object)"] = o.attr("sub").attr("attr_obj"); + d["attr(char *)"] = o.attr("sub").attr("attr_char"); + try { + o.attr("sub").attr("missing").ptr(); + } catch (const py::error_already_set &) { + d["missing_attr_ptr"] = "raised"_s; + } + try { + o.attr("missing").attr("doesn't matter"); + } catch (const py::error_already_set &) { + d["missing_attr_chain"] = "raised"_s; + } + + d["is_none"] = o.attr("basic_attr").is_none(); + + d["operator()"] = o.attr("func")(1); + d["operator*"] = o.attr("func")(*o.attr("begin_end")); + + // Test implicit conversion + py::list implicit_list = o.attr("begin_end"); + d["implicit_list"] = implicit_list; + py::dict implicit_dict = o.attr("__dict__"); + d["implicit_dict"] = implicit_dict; + + return d; + }); + + m.def("tuple_accessor", [](py::tuple existing_t) { + try { + existing_t[0] = 1; + } catch (const py::error_already_set &) { + // --> Python system error + // Only new tuples (refcount == 1) are mutable + auto new_t = py::tuple(3); + for (size_t i = 0; i < new_t.size(); ++i) { + new_t[i] = i; + } + return new_t; + } + return py::tuple(); + }); + + m.def("accessor_assignment", []() { + auto l = py::list(1); + l[0] = 0; + + auto d = py::dict(); + d["get"] = l[0]; + auto var = l[0]; + d["deferred_get"] = var; + l[0] = 1; + d["set"] = l[0]; + var = 99; // this assignment should not overwrite l[0] + d["deferred_set"] = l[0]; + d["var"] = var; + + return d; + }); + + // test_constructors + m.def("default_constructors", []() { + return py::dict( + "str"_a=py::str(), + "bool"_a=py::bool_(), + "int"_a=py::int_(), + "float"_a=py::float_(), + "tuple"_a=py::tuple(), + "list"_a=py::list(), + "dict"_a=py::dict(), + "set"_a=py::set() + ); + }); + + m.def("converting_constructors", [](py::dict d) { + return py::dict( + "str"_a=py::str(d["str"]), + "bool"_a=py::bool_(d["bool"]), + "int"_a=py::int_(d["int"]), + "float"_a=py::float_(d["float"]), + "tuple"_a=py::tuple(d["tuple"]), + "list"_a=py::list(d["list"]), + "dict"_a=py::dict(d["dict"]), + "set"_a=py::set(d["set"]), + "memoryview"_a=py::memoryview(d["memoryview"]) + ); + }); + + m.def("cast_functions", [](py::dict d) { + // When converting between Python types, obj.cast() should be the same as T(obj) + return py::dict( + "str"_a=d["str"].cast(), + "bool"_a=d["bool"].cast(), + "int"_a=d["int"].cast(), + "float"_a=d["float"].cast(), + "tuple"_a=d["tuple"].cast(), + "list"_a=d["list"].cast(), + "dict"_a=d["dict"].cast(), + "set"_a=d["set"].cast(), + "memoryview"_a=d["memoryview"].cast() + ); + }); + + m.def("get_implicit_casting", []() { + py::dict d; + d["char*_i1"] = "abc"; + const char *c2 = "abc"; + d["char*_i2"] = c2; + d["char*_e"] = py::cast(c2); + d["char*_p"] = py::str(c2); + + d["int_i1"] = 42; + int i = 42; + d["int_i2"] = i; + i++; + d["int_e"] = py::cast(i); + i++; + d["int_p"] = py::int_(i); + + d["str_i1"] = std::string("str"); + std::string s2("str1"); + d["str_i2"] = s2; + s2[3] = '2'; + d["str_e"] = py::cast(s2); + s2[3] = '3'; + d["str_p"] = py::str(s2); + + py::list l(2); + l[0] = 3; + l[1] = py::cast(6); + l.append(9); + l.append(py::cast(12)); + l.append(py::int_(15)); + + return py::dict( + "d"_a=d, + "l"_a=l + ); + }); + + // test_print + m.def("print_function", []() { + py::print("Hello, World!"); + py::print(1, 2.0, "three", true, std::string("-- multiple args")); + auto args = py::make_tuple("and", "a", "custom", "separator"); + py::print("*args", *args, "sep"_a="-"); + py::print("no new line here", "end"_a=" -- "); + py::print("next print"); + + auto py_stderr = py::module::import("sys").attr("stderr"); + py::print("this goes to stderr", "file"_a=py_stderr); + + py::print("flush", "flush"_a=true); + + py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this")); + }); + + m.def("print_failure", []() { py::print(42, UnregisteredType()); }); + + m.def("hash_function", [](py::object obj) { return py::hash(obj); }); +} diff --git a/external/pybind11/tests/test_pytypes.py b/external/pybind11/tests/test_pytypes.py new file mode 100644 index 0000000..94c90a9 --- /dev/null +++ b/external/pybind11/tests/test_pytypes.py @@ -0,0 +1,240 @@ +import pytest +import sys + +from pybind11_tests import pytypes as m +from pybind11_tests import debug_enabled + + +def test_list(capture, doc): + with capture: + l = m.get_list() + assert l == ["overwritten"] + + l.append("value2") + m.print_list(l) + assert capture.unordered == """ + Entry at position 0: value + list item 0: overwritten + list item 1: value2 + """ + + assert doc(m.get_list) == "get_list() -> list" + assert doc(m.print_list) == "print_list(arg0: list) -> None" + + +def test_set(capture, doc): + s = m.get_set() + assert s == {"key1", "key2", "key3"} + + with capture: + s.add("key4") + m.print_set(s) + assert capture.unordered == """ + key: key1 + key: key2 + key: key3 + key: key4 + """ + + assert doc(m.get_list) == "get_list() -> list" + assert doc(m.print_list) == "print_list(arg0: list) -> None" + + +def test_dict(capture, doc): + d = m.get_dict() + assert d == {"key": "value"} + + with capture: + d["key2"] = "value2" + m.print_dict(d) + assert capture.unordered == """ + key: key, value=value + key: key2, value=value2 + """ + + assert doc(m.get_dict) == "get_dict() -> dict" + assert doc(m.print_dict) == "print_dict(arg0: dict) -> None" + + assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3} + + +def test_str(doc): + assert m.str_from_string().encode().decode() == "baz" + assert m.str_from_bytes().encode().decode() == "boo" + + assert doc(m.str_from_bytes) == "str_from_bytes() -> str" + + class A(object): + def __str__(self): + return "this is a str" + + def __repr__(self): + return "this is a repr" + + assert m.str_from_object(A()) == "this is a str" + assert m.repr_from_object(A()) == "this is a repr" + + s1, s2 = m.str_format() + assert s1 == "1 + 2 = 3" + assert s1 == s2 + + +def test_bytes(doc): + assert m.bytes_from_string().decode() == "foo" + assert m.bytes_from_str().decode() == "bar" + + assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( + "bytes" if sys.version_info[0] == 3 else "str" + ) + + +def test_capsule(capture): + pytest.gc_collect() + with capture: + a = m.return_capsule_with_destructor() + del a + pytest.gc_collect() + assert capture.unordered == """ + creating capsule + destructing capsule + """ + + with capture: + a = m.return_capsule_with_destructor_2() + del a + pytest.gc_collect() + assert capture.unordered == """ + creating capsule + destructing capsule: 1234 + """ + + with capture: + a = m.return_capsule_with_name_and_destructor() + del a + pytest.gc_collect() + assert capture.unordered == """ + created capsule (1234, 'pointer type description') + destructing capsule (1234, 'pointer type description') + """ + + +def test_accessors(): + class SubTestObject: + attr_obj = 1 + attr_char = 2 + + class TestObject: + basic_attr = 1 + begin_end = [1, 2, 3] + d = {"operator[object]": 1, "operator[char *]": 2} + sub = SubTestObject() + + def func(self, x, *args): + return self.basic_attr + x + sum(args) + + d = m.accessor_api(TestObject()) + assert d["basic_attr"] == 1 + assert d["begin_end"] == [1, 2, 3] + assert d["operator[object]"] == 1 + assert d["operator[char *]"] == 2 + assert d["attr(object)"] == 1 + assert d["attr(char *)"] == 2 + assert d["missing_attr_ptr"] == "raised" + assert d["missing_attr_chain"] == "raised" + assert d["is_none"] is False + assert d["operator()"] == 2 + assert d["operator*"] == 7 + assert d["implicit_list"] == [1, 2, 3] + assert all(x in TestObject.__dict__ for x in d["implicit_dict"]) + + assert m.tuple_accessor(tuple()) == (0, 1, 2) + + d = m.accessor_assignment() + assert d["get"] == 0 + assert d["deferred_get"] == 0 + assert d["set"] == 1 + assert d["deferred_set"] == 1 + assert d["var"] == 99 + + +def test_constructors(): + """C++ default and converting constructors are equivalent to type calls in Python""" + types = [str, bool, int, float, tuple, list, dict, set] + expected = {t.__name__: t() for t in types} + assert m.default_constructors() == expected + + data = { + str: 42, + bool: "Not empty", + int: "42", + float: "+1e3", + tuple: range(3), + list: range(3), + dict: [("two", 2), ("one", 1), ("three", 3)], + set: [4, 4, 5, 6, 6, 6], + memoryview: b'abc' + } + inputs = {k.__name__: v for k, v in data.items()} + expected = {k.__name__: k(v) for k, v in data.items()} + + assert m.converting_constructors(inputs) == expected + assert m.cast_functions(inputs) == expected + + # Converting constructors and cast functions should just reference rather + # than copy when no conversion is needed: + noconv1 = m.converting_constructors(expected) + for k in noconv1: + assert noconv1[k] is expected[k] + + noconv2 = m.cast_functions(expected) + for k in noconv2: + assert noconv2[k] is expected[k] + + +def test_implicit_casting(): + """Tests implicit casting when assigning or appending to dicts and lists.""" + z = m.get_implicit_casting() + assert z['d'] == { + 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc', + 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3', + 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44 + } + assert z['l'] == [3, 6, 9, 12, 15] + + +def test_print(capture): + with capture: + m.print_function() + assert capture == """ + Hello, World! + 1 2.0 three True -- multiple args + *args-and-a-custom-separator + no new line here -- next print + flush + py::print + str.format = this + """ + assert capture.stderr == "this goes to stderr" + + with pytest.raises(RuntimeError) as excinfo: + m.print_failure() + assert str(excinfo.value) == "make_tuple(): unable to convert " + ( + "argument of type 'UnregisteredType' to Python object" + if debug_enabled else + "arguments to Python object (compile in debug mode for details)" + ) + + +def test_hash(): + class Hashable(object): + def __init__(self, value): + self.value = value + + def __hash__(self): + return self.value + + class Unhashable(object): + __hash__ = None + + assert m.hash_function(Hashable(42)) == 42 + with pytest.raises(TypeError): + m.hash_function(Unhashable()) diff --git a/external/pybind11/tests/test_sequences_and_iterators.cpp b/external/pybind11/tests/test_sequences_and_iterators.cpp new file mode 100644 index 0000000..a455212 --- /dev/null +++ b/external/pybind11/tests/test_sequences_and_iterators.cpp @@ -0,0 +1,334 @@ +/* + tests/test_sequences_and_iterators.cpp -- supporting Pythons' sequence protocol, iterators, + etc. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include +#include + +template +class NonZeroIterator { + const T* ptr_; +public: + NonZeroIterator(const T* ptr) : ptr_(ptr) {} + const T& operator*() const { return *ptr_; } + NonZeroIterator& operator++() { ++ptr_; return *this; } +}; + +class NonZeroSentinel {}; + +template +bool operator==(const NonZeroIterator>& it, const NonZeroSentinel&) { + return !(*it).first || !(*it).second; +} + +template +py::list test_random_access_iterator(PythonType x) { + if (x.size() < 5) + throw py::value_error("Please provide at least 5 elements for testing."); + + auto checks = py::list(); + auto assert_equal = [&checks](py::handle a, py::handle b) { + auto result = PyObject_RichCompareBool(a.ptr(), b.ptr(), Py_EQ); + if (result == -1) { throw py::error_already_set(); } + checks.append(result != 0); + }; + + auto it = x.begin(); + assert_equal(x[0], *it); + assert_equal(x[0], it[0]); + assert_equal(x[1], it[1]); + + assert_equal(x[1], *(++it)); + assert_equal(x[1], *(it++)); + assert_equal(x[2], *it); + assert_equal(x[3], *(it += 1)); + assert_equal(x[2], *(--it)); + assert_equal(x[2], *(it--)); + assert_equal(x[1], *it); + assert_equal(x[0], *(it -= 1)); + + assert_equal(it->attr("real"), x[0].attr("real")); + assert_equal((it + 1)->attr("real"), x[1].attr("real")); + + assert_equal(x[1], *(it + 1)); + assert_equal(x[1], *(1 + it)); + it += 3; + assert_equal(x[1], *(it - 2)); + + checks.append(static_cast(x.end() - x.begin()) == x.size()); + checks.append((x.begin() + static_cast(x.size())) == x.end()); + checks.append(x.begin() < x.end()); + + return checks; +} + +TEST_SUBMODULE(sequences_and_iterators, m) { + + // test_sequence + class Sequence { + public: + Sequence(size_t size) : m_size(size) { + print_created(this, "of size", m_size); + m_data = new float[size]; + memset(m_data, 0, sizeof(float) * size); + } + Sequence(const std::vector &value) : m_size(value.size()) { + print_created(this, "of size", m_size, "from std::vector"); + m_data = new float[m_size]; + memcpy(m_data, &value[0], sizeof(float) * m_size); + } + Sequence(const Sequence &s) : m_size(s.m_size) { + print_copy_created(this); + m_data = new float[m_size]; + memcpy(m_data, s.m_data, sizeof(float)*m_size); + } + Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) { + print_move_created(this); + s.m_size = 0; + s.m_data = nullptr; + } + + ~Sequence() { print_destroyed(this); delete[] m_data; } + + Sequence &operator=(const Sequence &s) { + if (&s != this) { + delete[] m_data; + m_size = s.m_size; + m_data = new float[m_size]; + memcpy(m_data, s.m_data, sizeof(float)*m_size); + } + print_copy_assigned(this); + return *this; + } + + Sequence &operator=(Sequence &&s) { + if (&s != this) { + delete[] m_data; + m_size = s.m_size; + m_data = s.m_data; + s.m_size = 0; + s.m_data = nullptr; + } + print_move_assigned(this); + return *this; + } + + bool operator==(const Sequence &s) const { + if (m_size != s.size()) return false; + for (size_t i = 0; i < m_size; ++i) + if (m_data[i] != s[i]) + return false; + return true; + } + bool operator!=(const Sequence &s) const { return !operator==(s); } + + float operator[](size_t index) const { return m_data[index]; } + float &operator[](size_t index) { return m_data[index]; } + + bool contains(float v) const { + for (size_t i = 0; i < m_size; ++i) + if (v == m_data[i]) + return true; + return false; + } + + Sequence reversed() const { + Sequence result(m_size); + for (size_t i = 0; i < m_size; ++i) + result[m_size - i - 1] = m_data[i]; + return result; + } + + size_t size() const { return m_size; } + + const float *begin() const { return m_data; } + const float *end() const { return m_data+m_size; } + + private: + size_t m_size; + float *m_data; + }; + py::class_(m, "Sequence") + .def(py::init()) + .def(py::init&>()) + /// Bare bones interface + .def("__getitem__", [](const Sequence &s, size_t i) { + if (i >= s.size()) throw py::index_error(); + return s[i]; + }) + .def("__setitem__", [](Sequence &s, size_t i, float v) { + if (i >= s.size()) throw py::index_error(); + s[i] = v; + }) + .def("__len__", &Sequence::size) + /// Optional sequence protocol operations + .def("__iter__", [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); }, + py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) + .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); }) + .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); }) + /// Slicing protocol (optional) + .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* { + size_t start, stop, step, slicelength; + if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + Sequence *seq = new Sequence(slicelength); + for (size_t i = 0; i < slicelength; ++i) { + (*seq)[i] = s[start]; start += step; + } + return seq; + }) + .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + for (size_t i = 0; i < slicelength; ++i) { + s[start] = value[i]; start += step; + } + }) + /// Comparisons + .def(py::self == py::self) + .def(py::self != py::self) + // Could also define py::self + py::self for concatenation, etc. + ; + + // test_map_iterator + // Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic + // map-like functionality. + class StringMap { + public: + StringMap() = default; + StringMap(std::unordered_map init) + : map(std::move(init)) {} + + void set(std::string key, std::string val) { map[key] = val; } + std::string get(std::string key) const { return map.at(key); } + size_t size() const { return map.size(); } + private: + std::unordered_map map; + public: + decltype(map.cbegin()) begin() const { return map.cbegin(); } + decltype(map.cend()) end() const { return map.cend(); } + }; + py::class_(m, "StringMap") + .def(py::init<>()) + .def(py::init>()) + .def("__getitem__", [](const StringMap &map, std::string key) { + try { return map.get(key); } + catch (const std::out_of_range&) { + throw py::key_error("key '" + key + "' does not exist"); + } + }) + .def("__setitem__", &StringMap::set) + .def("__len__", &StringMap::size) + .def("__iter__", [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); }, + py::keep_alive<0, 1>()) + .def("items", [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); }, + py::keep_alive<0, 1>()) + ; + + // test_generalized_iterators + class IntPairs { + public: + IntPairs(std::vector> data) : data_(std::move(data)) {} + const std::pair* begin() const { return data_.data(); } + private: + std::vector> data_; + }; + py::class_(m, "IntPairs") + .def(py::init>>()) + .def("nonzero", [](const IntPairs& s) { + return py::make_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); + }, py::keep_alive<0, 1>()) + .def("nonzero_keys", [](const IntPairs& s) { + return py::make_key_iterator(NonZeroIterator>(s.begin()), NonZeroSentinel()); + }, py::keep_alive<0, 1>()) + ; + + +#if 0 + // Obsolete: special data structure for exposing custom iterator types to python + // kept here for illustrative purposes because there might be some use cases which + // are not covered by the much simpler py::make_iterator + + struct PySequenceIterator { + PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { } + + float next() { + if (index == seq.size()) + throw py::stop_iteration(); + return seq[index++]; + } + + const Sequence &seq; + py::object ref; // keep a reference + size_t index = 0; + }; + + py::class_(seq, "Iterator") + .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; }) + .def("__next__", &PySequenceIterator::next); + + On the actual Sequence object, the iterator would be constructed as follows: + .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast(), s); }) +#endif + + // test_python_iterator_in_cpp + m.def("object_to_list", [](py::object o) { + auto l = py::list(); + for (auto item : o) { + l.append(item); + } + return l; + }); + + m.def("iterator_to_list", [](py::iterator it) { + auto l = py::list(); + while (it != py::iterator::sentinel()) { + l.append(*it); + ++it; + } + return l; + }); + + // Make sure that py::iterator works with std algorithms + m.def("count_none", [](py::object o) { + return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); + }); + + m.def("find_none", [](py::object o) { + auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); + return it->is_none(); + }); + + m.def("count_nonzeros", [](py::dict d) { + return std::count_if(d.begin(), d.end(), [](std::pair p) { + return p.second.cast() != 0; + }); + }); + + m.def("tuple_iterator", &test_random_access_iterator); + m.def("list_iterator", &test_random_access_iterator); + m.def("sequence_iterator", &test_random_access_iterator); + + // test_iterator_passthrough + // #181: iterator passthrough did not compile + m.def("iterator_passthrough", [](py::iterator s) -> py::iterator { + return py::make_iterator(std::begin(s), std::end(s)); + }); + + // test_iterator_rvp + // #388: Can't make iterators via make_iterator() with different r/v policies + static std::vector list = { 1, 2, 3 }; + m.def("make_iterator_1", []() { return py::make_iterator(list); }); + m.def("make_iterator_2", []() { return py::make_iterator(list); }); +} diff --git a/external/pybind11/tests/test_sequences_and_iterators.py b/external/pybind11/tests/test_sequences_and_iterators.py new file mode 100644 index 0000000..640ca07 --- /dev/null +++ b/external/pybind11/tests/test_sequences_and_iterators.py @@ -0,0 +1,158 @@ +import pytest +from pybind11_tests import sequences_and_iterators as m +from pybind11_tests import ConstructorStats + + +def isclose(a, b, rel_tol=1e-05, abs_tol=0.0): + """Like math.isclose() from Python 3.5""" + return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) + + +def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0): + return all(isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list)) + + +def test_generalized_iterators(): + assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero()) == [(1, 2), (3, 4)] + assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero()) == [(1, 2)] + assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero()) == [] + + assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).nonzero_keys()) == [1, 3] + assert list(m.IntPairs([(1, 2), (2, 0), (0, 3), (4, 5)]).nonzero_keys()) == [1] + assert list(m.IntPairs([(0, 3), (1, 2), (3, 4)]).nonzero_keys()) == [] + + # __next__ must continue to raise StopIteration + it = m.IntPairs([(0, 0)]).nonzero() + for _ in range(3): + with pytest.raises(StopIteration): + next(it) + + it = m.IntPairs([(0, 0)]).nonzero_keys() + for _ in range(3): + with pytest.raises(StopIteration): + next(it) + + +def test_sequence(): + cstats = ConstructorStats.get(m.Sequence) + + s = m.Sequence(5) + assert cstats.values() == ['of size', '5'] + + assert "Sequence" in repr(s) + assert len(s) == 5 + assert s[0] == 0 and s[3] == 0 + assert 12.34 not in s + s[0], s[3] = 12.34, 56.78 + assert 12.34 in s + assert isclose(s[0], 12.34) and isclose(s[3], 56.78) + + rev = reversed(s) + assert cstats.values() == ['of size', '5'] + + rev2 = s[::-1] + assert cstats.values() == ['of size', '5'] + + it = iter(m.Sequence(0)) + for _ in range(3): # __next__ must continue to raise StopIteration + with pytest.raises(StopIteration): + next(it) + assert cstats.values() == ['of size', '0'] + + expected = [0, 56.78, 0, 0, 12.34] + assert allclose(rev, expected) + assert allclose(rev2, expected) + assert rev == rev2 + + rev[0::2] = m.Sequence([2.0, 2.0, 2.0]) + assert cstats.values() == ['of size', '3', 'from std::vector'] + + assert allclose(rev, [2, 56.78, 2, 0, 2]) + + assert cstats.alive() == 4 + del it + assert cstats.alive() == 3 + del s + assert cstats.alive() == 2 + del rev + assert cstats.alive() == 1 + del rev2 + assert cstats.alive() == 0 + + assert cstats.values() == [] + assert cstats.default_constructions == 0 + assert cstats.copy_constructions == 0 + assert cstats.move_constructions >= 1 + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + +def test_map_iterator(): + sm = m.StringMap({'hi': 'bye', 'black': 'white'}) + assert sm['hi'] == 'bye' + assert len(sm) == 2 + assert sm['black'] == 'white' + + with pytest.raises(KeyError): + assert sm['orange'] + sm['orange'] = 'banana' + assert sm['orange'] == 'banana' + + expected = {'hi': 'bye', 'black': 'white', 'orange': 'banana'} + for k in sm: + assert sm[k] == expected[k] + for k, v in sm.items(): + assert v == expected[k] + + it = iter(m.StringMap({})) + for _ in range(3): # __next__ must continue to raise StopIteration + with pytest.raises(StopIteration): + next(it) + + +def test_python_iterator_in_cpp(): + t = (1, 2, 3) + assert m.object_to_list(t) == [1, 2, 3] + assert m.object_to_list(iter(t)) == [1, 2, 3] + assert m.iterator_to_list(iter(t)) == [1, 2, 3] + + with pytest.raises(TypeError) as excinfo: + m.object_to_list(1) + assert "object is not iterable" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.iterator_to_list(1) + assert "incompatible function arguments" in str(excinfo.value) + + def bad_next_call(): + raise RuntimeError("py::iterator::advance() should propagate errors") + + with pytest.raises(RuntimeError) as excinfo: + m.iterator_to_list(iter(bad_next_call, None)) + assert str(excinfo.value) == "py::iterator::advance() should propagate errors" + + l = [1, None, 0, None] + assert m.count_none(l) == 2 + assert m.find_none(l) is True + assert m.count_nonzeros({"a": 0, "b": 1, "c": 2}) == 2 + + r = range(5) + assert all(m.tuple_iterator(tuple(r))) + assert all(m.list_iterator(list(r))) + assert all(m.sequence_iterator(r)) + + +def test_iterator_passthrough(): + """#181: iterator passthrough did not compile""" + from pybind11_tests.sequences_and_iterators import iterator_passthrough + + assert list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15]))) == [3, 5, 7, 9, 11, 13, 15] + + +def test_iterator_rvp(): + """#388: Can't make iterators via make_iterator() with different r/v policies """ + import pybind11_tests.sequences_and_iterators as m + + assert list(m.make_iterator_1()) == [1, 2, 3] + assert list(m.make_iterator_2()) == [1, 2, 3] + assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2())) diff --git a/external/pybind11/tests/test_smart_ptr.cpp b/external/pybind11/tests/test_smart_ptr.cpp new file mode 100644 index 0000000..dccb1e9 --- /dev/null +++ b/external/pybind11/tests/test_smart_ptr.cpp @@ -0,0 +1,270 @@ +/* + tests/test_smart_ptr.cpp -- binding classes with custom reference counting, + implicit conversions between types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#if defined(_MSC_VER) && _MSC_VER < 1910 +# pragma warning(disable: 4702) // unreachable code in system header +#endif + +#include "pybind11_tests.h" +#include "object.h" + +// Make pybind aware of the ref-counted wrapper type (s): + +// ref is a wrapper for 'Object' which uses intrusive reference counting +// It is always possible to construct a ref from an Object* pointer without +// possible incosistencies, hence the 'true' argument at the end. +PYBIND11_DECLARE_HOLDER_TYPE(T, ref, true); +// Make pybind11 aware of the non-standard getter member function +namespace pybind11 { namespace detail { + template + struct holder_helper> { + static const T *get(const ref &p) { return p.get_ptr(); } + }; +}} + +// The following is not required anymore for std::shared_ptr, but it should compile without error: +PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); + +// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the +// holder size to trigger the non-simple-layout internal instance layout for single inheritance with +// large holder type: +template class huge_unique_ptr { + std::unique_ptr ptr; + uint64_t padding[10]; +public: + huge_unique_ptr(T *p) : ptr(p) {}; + T *get() { return ptr.get(); } +}; +PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr); + +// Simple custom holder that works like unique_ptr +template +class custom_unique_ptr { + std::unique_ptr impl; +public: + custom_unique_ptr(T* p) : impl(p) { } + T* get() const { return impl.get(); } + T* release_ptr() { return impl.release(); } +}; +PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); + + +TEST_SUBMODULE(smart_ptr, m) { + + // test_smart_ptr + + // Object implementation in `object.h` + py::class_> obj(m, "Object"); + obj.def("getRefCount", &Object::getRefCount); + + // Custom object with builtin reference counting (see 'object.h' for the implementation) + class MyObject1 : public Object { + public: + MyObject1(int value) : value(value) { print_created(this, toString()); } + std::string toString() const { return "MyObject1[" + std::to_string(value) + "]"; } + protected: + virtual ~MyObject1() { print_destroyed(this); } + private: + int value; + }; + py::class_>(m, "MyObject1", obj) + .def(py::init()); + py::implicitly_convertible(); + + m.def("make_object_1", []() -> Object * { return new MyObject1(1); }); + m.def("make_object_2", []() -> ref { return new MyObject1(2); }); + m.def("make_myobject1_1", []() -> MyObject1 * { return new MyObject1(4); }); + m.def("make_myobject1_2", []() -> ref { return new MyObject1(5); }); + m.def("print_object_1", [](const Object *obj) { py::print(obj->toString()); }); + m.def("print_object_2", [](ref obj) { py::print(obj->toString()); }); + m.def("print_object_3", [](const ref &obj) { py::print(obj->toString()); }); + m.def("print_object_4", [](const ref *obj) { py::print((*obj)->toString()); }); + m.def("print_myobject1_1", [](const MyObject1 *obj) { py::print(obj->toString()); }); + m.def("print_myobject1_2", [](ref obj) { py::print(obj->toString()); }); + m.def("print_myobject1_3", [](const ref &obj) { py::print(obj->toString()); }); + m.def("print_myobject1_4", [](const ref *obj) { py::print((*obj)->toString()); }); + + // Expose constructor stats for the ref type + m.def("cstats_ref", &ConstructorStats::get); + + + // Object managed by a std::shared_ptr<> + class MyObject2 { + public: + MyObject2(int value) : value(value) { print_created(this, toString()); } + std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; } + virtual ~MyObject2() { print_destroyed(this); } + private: + int value; + }; + py::class_>(m, "MyObject2") + .def(py::init()); + m.def("make_myobject2_1", []() { return new MyObject2(6); }); + m.def("make_myobject2_2", []() { return std::make_shared(7); }); + m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); }); + m.def("print_myobject2_2", [](std::shared_ptr obj) { py::print(obj->toString()); }); + m.def("print_myobject2_3", [](const std::shared_ptr &obj) { py::print(obj->toString()); }); + m.def("print_myobject2_4", [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); + + // Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<> + class MyObject3 : public std::enable_shared_from_this { + public: + MyObject3(int value) : value(value) { print_created(this, toString()); } + std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } + virtual ~MyObject3() { print_destroyed(this); } + private: + int value; + }; + py::class_>(m, "MyObject3") + .def(py::init()); + m.def("make_myobject3_1", []() { return new MyObject3(8); }); + m.def("make_myobject3_2", []() { return std::make_shared(9); }); + m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); + m.def("print_myobject3_2", [](std::shared_ptr obj) { py::print(obj->toString()); }); + m.def("print_myobject3_3", [](const std::shared_ptr &obj) { py::print(obj->toString()); }); + m.def("print_myobject3_4", [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); + + // test_smart_ptr_refcounting + m.def("test_object1_refcounting", []() { + ref o = new MyObject1(0); + bool good = o->getRefCount() == 1; + py::object o2 = py::cast(o, py::return_value_policy::reference); + // always request (partial) ownership for objects with intrusive + // reference counting even when using the 'reference' RVP + good &= o->getRefCount() == 2; + return good; + }); + + // test_unique_nodelete + // Object with a private destructor + class MyObject4 { + public: + MyObject4(int value) : value{value} { print_created(this); } + int value; + private: + ~MyObject4() { print_destroyed(this); } + }; + py::class_>(m, "MyObject4") + .def(py::init()) + .def_readwrite("value", &MyObject4::value); + + // test_large_holder + class MyObject5 { // managed by huge_unique_ptr + public: + MyObject5(int value) : value{value} { print_created(this); } + ~MyObject5() { print_destroyed(this); } + int value; + }; + py::class_>(m, "MyObject5") + .def(py::init()) + .def_readwrite("value", &MyObject5::value); + + // test_shared_ptr_and_references + struct SharedPtrRef { + struct A { + A() { print_created(this); } + A(const A &) { print_copy_created(this); } + A(A &&) { print_move_created(this); } + ~A() { print_destroyed(this); } + }; + + A value = {}; + std::shared_ptr shared = std::make_shared(); + }; + using A = SharedPtrRef::A; + py::class_>(m, "A"); + py::class_(m, "SharedPtrRef") + .def(py::init<>()) + .def_readonly("ref", &SharedPtrRef::value) + .def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; }, + py::return_value_policy::copy) + .def_readonly("holder_ref", &SharedPtrRef::shared) + .def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; }, + py::return_value_policy::copy) + .def("set_ref", [](SharedPtrRef &, const A &) { return true; }) + .def("set_holder", [](SharedPtrRef &, std::shared_ptr) { return true; }); + + // test_shared_ptr_from_this_and_references + struct SharedFromThisRef { + struct B : std::enable_shared_from_this { + B() { print_created(this); } + B(const B &) : std::enable_shared_from_this() { print_copy_created(this); } + B(B &&) : std::enable_shared_from_this() { print_move_created(this); } + ~B() { print_destroyed(this); } + }; + + B value = {}; + std::shared_ptr shared = std::make_shared(); + }; + using B = SharedFromThisRef::B; + py::class_>(m, "B"); + py::class_(m, "SharedFromThisRef") + .def(py::init<>()) + .def_readonly("bad_wp", &SharedFromThisRef::value) + .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) + .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; }, + py::return_value_policy::copy) + .def_readonly("holder_ref", &SharedFromThisRef::shared) + .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; }, + py::return_value_policy::copy) + .def("set_ref", [](SharedFromThisRef &, const B &) { return true; }) + .def("set_holder", [](SharedFromThisRef &, std::shared_ptr) { return true; }); + + // Issue #865: shared_from_this doesn't work with virtual inheritance + struct SharedFromThisVBase : std::enable_shared_from_this { + virtual ~SharedFromThisVBase() = default; + }; + struct SharedFromThisVirt : virtual SharedFromThisVBase {}; + static std::shared_ptr sft(new SharedFromThisVirt()); + py::class_>(m, "SharedFromThisVirt") + .def_static("get", []() { return sft.get(); }); + + // test_move_only_holder + struct C { + C() { print_created(this); } + ~C() { print_destroyed(this); } + }; + py::class_>(m, "TypeWithMoveOnlyHolder") + .def_static("make", []() { return custom_unique_ptr(new C); }); + + // test_smart_ptr_from_default + struct HeldByDefaultHolder { }; + py::class_(m, "HeldByDefaultHolder") + .def(py::init<>()) + .def_static("load_shared_ptr", [](std::shared_ptr) {}); + + // test_shared_ptr_gc + // #187: issue involving std::shared_ptr<> return value policy & garbage collection + struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ }; + py::class_>(m, "ElementBase"); + + struct ElementA : ElementBase { + ElementA(int v) : v(v) { } + int value() { return v; } + int v; + }; + py::class_>(m, "ElementA") + .def(py::init()) + .def("value", &ElementA::value); + + struct ElementList { + void add(std::shared_ptr e) { l.push_back(e); } + std::vector> l; + }; + py::class_>(m, "ElementList") + .def(py::init<>()) + .def("add", &ElementList::add) + .def("get", [](ElementList &el) { + py::list list; + for (auto &e : el.l) + list.append(py::cast(e)); + return list; + }); +} diff --git a/external/pybind11/tests/test_smart_ptr.py b/external/pybind11/tests/test_smart_ptr.py new file mode 100644 index 0000000..4dfe003 --- /dev/null +++ b/external/pybind11/tests/test_smart_ptr.py @@ -0,0 +1,220 @@ +import pytest +from pybind11_tests import smart_ptr as m +from pybind11_tests import ConstructorStats + + +def test_smart_ptr(capture): + # Object1 + for i, o in enumerate([m.make_object_1(), m.make_object_2(), m.MyObject1(3)], start=1): + assert o.getRefCount() == 1 + with capture: + m.print_object_1(o) + m.print_object_2(o) + m.print_object_3(o) + m.print_object_4(o) + assert capture == "MyObject1[{i}]\n".format(i=i) * 4 + + for i, o in enumerate([m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], + start=4): + print(o) + with capture: + if not isinstance(o, int): + m.print_object_1(o) + m.print_object_2(o) + m.print_object_3(o) + m.print_object_4(o) + m.print_myobject1_1(o) + m.print_myobject1_2(o) + m.print_myobject1_3(o) + m.print_myobject1_4(o) + assert capture == "MyObject1[{i}]\n".format(i=i) * (4 if isinstance(o, int) else 8) + + cstats = ConstructorStats.get(m.MyObject1) + assert cstats.alive() == 0 + expected_values = ['MyObject1[{}]'.format(i) for i in range(1, 7)] + ['MyObject1[7]'] * 4 + assert cstats.values() == expected_values + assert cstats.default_constructions == 0 + assert cstats.copy_constructions == 0 + # assert cstats.move_constructions >= 0 # Doesn't invoke any + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + # Object2 + for i, o in zip([8, 6, 7], [m.MyObject2(8), m.make_myobject2_1(), m.make_myobject2_2()]): + print(o) + with capture: + m.print_myobject2_1(o) + m.print_myobject2_2(o) + m.print_myobject2_3(o) + m.print_myobject2_4(o) + assert capture == "MyObject2[{i}]\n".format(i=i) * 4 + + cstats = ConstructorStats.get(m.MyObject2) + assert cstats.alive() == 1 + o = None + assert cstats.alive() == 0 + assert cstats.values() == ['MyObject2[8]', 'MyObject2[6]', 'MyObject2[7]'] + assert cstats.default_constructions == 0 + assert cstats.copy_constructions == 0 + # assert cstats.move_constructions >= 0 # Doesn't invoke any + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + # Object3 + for i, o in zip([9, 8, 9], [m.MyObject3(9), m.make_myobject3_1(), m.make_myobject3_2()]): + print(o) + with capture: + m.print_myobject3_1(o) + m.print_myobject3_2(o) + m.print_myobject3_3(o) + m.print_myobject3_4(o) + assert capture == "MyObject3[{i}]\n".format(i=i) * 4 + + cstats = ConstructorStats.get(m.MyObject3) + assert cstats.alive() == 1 + o = None + assert cstats.alive() == 0 + assert cstats.values() == ['MyObject3[9]', 'MyObject3[8]', 'MyObject3[9]'] + assert cstats.default_constructions == 0 + assert cstats.copy_constructions == 0 + # assert cstats.move_constructions >= 0 # Doesn't invoke any + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + # Object + cstats = ConstructorStats.get(m.Object) + assert cstats.alive() == 0 + assert cstats.values() == [] + assert cstats.default_constructions == 10 + assert cstats.copy_constructions == 0 + # assert cstats.move_constructions >= 0 # Doesn't invoke any + assert cstats.copy_assignments == 0 + assert cstats.move_assignments == 0 + + # ref<> + cstats = m.cstats_ref() + assert cstats.alive() == 0 + assert cstats.values() == ['from pointer'] * 10 + assert cstats.default_constructions == 30 + assert cstats.copy_constructions == 12 + # assert cstats.move_constructions >= 0 # Doesn't invoke any + assert cstats.copy_assignments == 30 + assert cstats.move_assignments == 0 + + +def test_smart_ptr_refcounting(): + assert m.test_object1_refcounting() + + +def test_unique_nodelete(): + o = m.MyObject4(23) + assert o.value == 23 + cstats = ConstructorStats.get(m.MyObject4) + assert cstats.alive() == 1 + del o + assert cstats.alive() == 1 # Leak, but that's intentional + + +def test_large_holder(): + o = m.MyObject5(5) + assert o.value == 5 + cstats = ConstructorStats.get(m.MyObject5) + assert cstats.alive() == 1 + del o + assert cstats.alive() == 0 + + +def test_shared_ptr_and_references(): + s = m.SharedPtrRef() + stats = ConstructorStats.get(m.A) + assert stats.alive() == 2 + + ref = s.ref # init_holder_helper(holder_ptr=false, owned=false) + assert stats.alive() == 2 + assert s.set_ref(ref) + with pytest.raises(RuntimeError) as excinfo: + assert s.set_holder(ref) + assert "Unable to cast from non-held to held instance" in str(excinfo.value) + + copy = s.copy # init_holder_helper(holder_ptr=false, owned=true) + assert stats.alive() == 3 + assert s.set_ref(copy) + assert s.set_holder(copy) + + holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false) + assert stats.alive() == 3 + assert s.set_ref(holder_ref) + assert s.set_holder(holder_ref) + + holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true) + assert stats.alive() == 3 + assert s.set_ref(holder_copy) + assert s.set_holder(holder_copy) + + del ref, copy, holder_ref, holder_copy, s + assert stats.alive() == 0 + + +def test_shared_ptr_from_this_and_references(): + s = m.SharedFromThisRef() + stats = ConstructorStats.get(m.B) + assert stats.alive() == 2 + + ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false) + assert stats.alive() == 2 + assert s.set_ref(ref) + assert s.set_holder(ref) # std::enable_shared_from_this can create a holder from a reference + + bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true) + assert stats.alive() == 2 + assert s.set_ref(bad_wp) + with pytest.raises(RuntimeError) as excinfo: + assert s.set_holder(bad_wp) + assert "Unable to cast from non-held to held instance" in str(excinfo.value) + + copy = s.copy # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false) + assert stats.alive() == 3 + assert s.set_ref(copy) + assert s.set_holder(copy) + + holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false) + assert stats.alive() == 3 + assert s.set_ref(holder_ref) + assert s.set_holder(holder_ref) + + holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false) + assert stats.alive() == 3 + assert s.set_ref(holder_copy) + assert s.set_holder(holder_copy) + + del ref, bad_wp, copy, holder_ref, holder_copy, s + assert stats.alive() == 0 + + z = m.SharedFromThisVirt.get() + y = m.SharedFromThisVirt.get() + assert y is z + + +def test_move_only_holder(): + a = m.TypeWithMoveOnlyHolder.make() + stats = ConstructorStats.get(m.TypeWithMoveOnlyHolder) + assert stats.alive() == 1 + del a + assert stats.alive() == 0 + + +def test_smart_ptr_from_default(): + instance = m.HeldByDefaultHolder() + with pytest.raises(RuntimeError) as excinfo: + m.HeldByDefaultHolder.load_shared_ptr(instance) + assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo) + + +def test_shared_ptr_gc(): + """#187: issue involving std::shared_ptr<> return value policy & garbage collection""" + el = m.ElementList() + for i in range(10): + el.add(m.ElementA(i)) + pytest.gc_collect() + for i, v in enumerate(el.get()): + assert i == v.value() diff --git a/external/pybind11/tests/test_stl.cpp b/external/pybind11/tests/test_stl.cpp new file mode 100644 index 0000000..7d53e9c --- /dev/null +++ b/external/pybind11/tests/test_stl.cpp @@ -0,0 +1,238 @@ +/* + tests/test_stl.cpp -- STL type casters + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 +#if PYBIND11_HAS_VARIANT +using std::variant; +#elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +# include +# define PYBIND11_HAS_VARIANT 1 +using boost::variant; + +namespace pybind11 { namespace detail { +template +struct type_caster> : variant_caster> {}; + +template <> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) { + return boost::apply_visitor(args...); + } +}; +}} // namespace pybind11::detail +#endif + +/// Issue #528: templated constructor +struct TplCtorClass { + template TplCtorClass(const T &) { } + bool operator==(const TplCtorClass &) const { return true; } +}; + +namespace std { + template <> + struct hash { size_t operator()(const TplCtorClass &) const { return 0; } }; +} + + +TEST_SUBMODULE(stl, m) { + // test_vector + m.def("cast_vector", []() { return std::vector{1}; }); + m.def("load_vector", [](const std::vector &v) { return v.at(0) == 1 && v.at(1) == 2; }); + // `std::vector` is special because it returns proxy objects instead of references + m.def("cast_bool_vector", []() { return std::vector{true, false}; }); + m.def("load_bool_vector", [](const std::vector &v) { + return v.at(0) == true && v.at(1) == false; + }); + // Unnumbered regression (caused by #936): pointers to stl containers aren't castable + static std::vector lvv{2}; + m.def("cast_ptr_vector", []() { return &lvv; }); + + // test_array + m.def("cast_array", []() { return std::array {{1 , 2}}; }); + m.def("load_array", [](const std::array &a) { return a[0] == 1 && a[1] == 2; }); + + // test_valarray + m.def("cast_valarray", []() { return std::valarray{1, 4, 9}; }); + m.def("load_valarray", [](const std::valarray& v) { + return v.size() == 3 && v[0] == 1 && v[1] == 4 && v[2] == 9; + }); + + // test_map + m.def("cast_map", []() { return std::map{{"key", "value"}}; }); + m.def("load_map", [](const std::map &map) { + return map.at("key") == "value" && map.at("key2") == "value2"; + }); + + // test_set + m.def("cast_set", []() { return std::set{"key1", "key2"}; }); + m.def("load_set", [](const std::set &set) { + return set.count("key1") && set.count("key2") && set.count("key3"); + }); + + // test_recursive_casting + m.def("cast_rv_vector", []() { return std::vector{2}; }); + m.def("cast_rv_array", []() { return std::array(); }); + // NB: map and set keys are `const`, so while we technically do move them (as `const Type &&`), + // casters don't typically do anything with that, which means they fall to the `const Type &` + // caster. + m.def("cast_rv_map", []() { return std::unordered_map{{"a", RValueCaster{}}}; }); + m.def("cast_rv_nested", []() { + std::vector>, 2>> v; + v.emplace_back(); // add an array + v.back()[0].emplace_back(); // add a map to the array + v.back()[0].back().emplace("b", RValueCaster{}); + v.back()[0].back().emplace("c", RValueCaster{}); + v.back()[1].emplace_back(); // add a map to the array + v.back()[1].back().emplace("a", RValueCaster{}); + return v; + }); + static std::array lva; + static std::unordered_map lvm{{"a", RValueCaster{}}, {"b", RValueCaster{}}}; + static std::unordered_map>>> lvn; + lvn["a"].emplace_back(); // add a list + lvn["a"].back().emplace_back(); // add an array + lvn["a"].emplace_back(); // another list + lvn["a"].back().emplace_back(); // add an array + lvn["b"].emplace_back(); // add a list + lvn["b"].back().emplace_back(); // add an array + lvn["b"].back().emplace_back(); // add another array + m.def("cast_lv_vector", []() -> const decltype(lvv) & { return lvv; }); + m.def("cast_lv_array", []() -> const decltype(lva) & { return lva; }); + m.def("cast_lv_map", []() -> const decltype(lvm) & { return lvm; }); + m.def("cast_lv_nested", []() -> const decltype(lvn) & { return lvn; }); + // #853: + m.def("cast_unique_ptr_vector", []() { + std::vector> v; + v.emplace_back(new UserType{7}); + v.emplace_back(new UserType{42}); + return v; + }); + + // test_move_out_container + struct MoveOutContainer { + struct Value { int value; }; + std::list move_list() const { return {{0}, {1}, {2}}; } + }; + py::class_(m, "MoveOutContainerValue") + .def_readonly("value", &MoveOutContainer::Value::value); + py::class_(m, "MoveOutContainer") + .def(py::init<>()) + .def_property_readonly("move_list", &MoveOutContainer::move_list); + + // Class that can be move- and copy-constructed, but not assigned + struct NoAssign { + int value; + + explicit NoAssign(int value = 0) : value(value) { } + NoAssign(const NoAssign &) = default; + NoAssign(NoAssign &&) = default; + + NoAssign &operator=(const NoAssign &) = delete; + NoAssign &operator=(NoAssign &&) = delete; + }; + py::class_(m, "NoAssign", "Class with no C++ assignment operators") + .def(py::init<>()) + .def(py::init()); + +#ifdef PYBIND11_HAS_OPTIONAL + // test_optional + m.attr("has_optional") = true; + + using opt_int = std::optional; + using opt_no_assign = std::optional; + m.def("double_or_zero", [](const opt_int& x) -> int { + return x.value_or(0) * 2; + }); + m.def("half_or_none", [](int x) -> opt_int { + return x ? opt_int(x / 2) : opt_int(); + }); + m.def("test_nullopt", [](opt_int x) { + return x.value_or(42); + }, py::arg_v("x", std::nullopt, "None")); + m.def("test_no_assign", [](const opt_no_assign &x) { + return x ? x->value : 42; + }, py::arg_v("x", std::nullopt, "None")); + + m.def("nodefer_none_optional", [](std::optional) { return true; }); + m.def("nodefer_none_optional", [](py::none) { return false; }); +#endif + +#ifdef PYBIND11_HAS_EXP_OPTIONAL + // test_exp_optional + m.attr("has_exp_optional") = true; + + using exp_opt_int = std::experimental::optional; + using exp_opt_no_assign = std::experimental::optional; + m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int { + return x.value_or(0) * 2; + }); + m.def("half_or_none_exp", [](int x) -> exp_opt_int { + return x ? exp_opt_int(x / 2) : exp_opt_int(); + }); + m.def("test_nullopt_exp", [](exp_opt_int x) { + return x.value_or(42); + }, py::arg_v("x", std::experimental::nullopt, "None")); + m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) { + return x ? x->value : 42; + }, py::arg_v("x", std::experimental::nullopt, "None")); +#endif + +#ifdef PYBIND11_HAS_VARIANT + static_assert(std::is_same::value, + "visitor::result_type is required by boost::variant in C++11 mode"); + + struct visitor { + using result_type = const char *; + + result_type operator()(int) { return "int"; } + result_type operator()(std::string) { return "std::string"; } + result_type operator()(double) { return "double"; } + result_type operator()(std::nullptr_t) { return "std::nullptr_t"; } + }; + + // test_variant + m.def("load_variant", [](variant v) { + return py::detail::visit_helper::call(visitor(), v); + }); + m.def("load_variant_2pass", [](variant v) { + return py::detail::visit_helper::call(visitor(), v); + }); + m.def("cast_variant", []() { + using V = variant; + return py::make_tuple(V(5), V("Hello")); + }); +#endif + + // #528: templated constructor + // (no python tests: the test here is that this compiles) + m.def("tpl_ctor_vector", [](std::vector &) {}); + m.def("tpl_ctor_map", [](std::unordered_map &) {}); + m.def("tpl_ctor_set", [](std::unordered_set &) {}); +#if defined(PYBIND11_HAS_OPTIONAL) + m.def("tpl_constr_optional", [](std::optional &) {}); +#elif defined(PYBIND11_HAS_EXP_OPTIONAL) + m.def("tpl_constr_optional", [](std::experimental::optional &) {}); +#endif + + // test_vec_of_reference_wrapper + // #171: Can't return STL structures containing reference wrapper + m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper p4) { + static UserType p1{1}, p2{2}, p3{3}; + return std::vector> { + std::ref(p1), std::ref(p2), std::ref(p3), p4 + }; + }); + + // test_stl_pass_by_pointer + m.def("stl_pass_by_pointer", [](std::vector* v) { return *v; }, "v"_a=nullptr); +} diff --git a/external/pybind11/tests/test_stl.py b/external/pybind11/tests/test_stl.py new file mode 100644 index 0000000..db8515e --- /dev/null +++ b/external/pybind11/tests/test_stl.py @@ -0,0 +1,200 @@ +import pytest + +from pybind11_tests import stl as m +from pybind11_tests import UserType + + +def test_vector(doc): + """std::vector <-> list""" + l = m.cast_vector() + assert l == [1] + l.append(2) + assert m.load_vector(l) + assert m.load_vector(tuple(l)) + + assert m.cast_bool_vector() == [True, False] + assert m.load_bool_vector([True, False]) + + assert doc(m.cast_vector) == "cast_vector() -> List[int]" + assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool" + + # Test regression caused by 936: pointers to stl containers weren't castable + assert m.cast_ptr_vector() == ["lvalue", "lvalue"] + + +def test_array(doc): + """std::array <-> list""" + l = m.cast_array() + assert l == [1, 2] + assert m.load_array(l) + + assert doc(m.cast_array) == "cast_array() -> List[int[2]]" + assert doc(m.load_array) == "load_array(arg0: List[int[2]]) -> bool" + + +def test_valarray(doc): + """std::valarray <-> list""" + l = m.cast_valarray() + assert l == [1, 4, 9] + assert m.load_valarray(l) + + assert doc(m.cast_valarray) == "cast_valarray() -> List[int]" + assert doc(m.load_valarray) == "load_valarray(arg0: List[int]) -> bool" + + +def test_map(doc): + """std::map <-> dict""" + d = m.cast_map() + assert d == {"key": "value"} + d["key2"] = "value2" + assert m.load_map(d) + + assert doc(m.cast_map) == "cast_map() -> Dict[str, str]" + assert doc(m.load_map) == "load_map(arg0: Dict[str, str]) -> bool" + + +def test_set(doc): + """std::set <-> set""" + s = m.cast_set() + assert s == {"key1", "key2"} + s.add("key3") + assert m.load_set(s) + + assert doc(m.cast_set) == "cast_set() -> Set[str]" + assert doc(m.load_set) == "load_set(arg0: Set[str]) -> bool" + + +def test_recursive_casting(): + """Tests that stl casters preserve lvalue/rvalue context for container values""" + assert m.cast_rv_vector() == ["rvalue", "rvalue"] + assert m.cast_lv_vector() == ["lvalue", "lvalue"] + assert m.cast_rv_array() == ["rvalue", "rvalue", "rvalue"] + assert m.cast_lv_array() == ["lvalue", "lvalue"] + assert m.cast_rv_map() == {"a": "rvalue"} + assert m.cast_lv_map() == {"a": "lvalue", "b": "lvalue"} + assert m.cast_rv_nested() == [[[{"b": "rvalue", "c": "rvalue"}], [{"a": "rvalue"}]]] + assert m.cast_lv_nested() == { + "a": [[["lvalue", "lvalue"]], [["lvalue", "lvalue"]]], + "b": [[["lvalue", "lvalue"], ["lvalue", "lvalue"]]] + } + + # Issue #853 test case: + z = m.cast_unique_ptr_vector() + assert z[0].value == 7 and z[1].value == 42 + + +def test_move_out_container(): + """Properties use the `reference_internal` policy by default. If the underlying function + returns an rvalue, the policy is automatically changed to `move` to avoid referencing + a temporary. In case the return value is a container of user-defined types, the policy + also needs to be applied to the elements, not just the container.""" + c = m.MoveOutContainer() + moved_out_list = c.move_list + assert [x.value for x in moved_out_list] == [0, 1, 2] + + +@pytest.mark.skipif(not hasattr(m, "has_optional"), reason='no ') +def test_optional(): + assert m.double_or_zero(None) == 0 + assert m.double_or_zero(42) == 84 + pytest.raises(TypeError, m.double_or_zero, 'foo') + + assert m.half_or_none(0) is None + assert m.half_or_none(42) == 21 + pytest.raises(TypeError, m.half_or_none, 'foo') + + assert m.test_nullopt() == 42 + assert m.test_nullopt(None) == 42 + assert m.test_nullopt(42) == 42 + assert m.test_nullopt(43) == 43 + + assert m.test_no_assign() == 42 + assert m.test_no_assign(None) == 42 + assert m.test_no_assign(m.NoAssign(43)) == 43 + pytest.raises(TypeError, m.test_no_assign, 43) + + assert m.nodefer_none_optional(None) + + +@pytest.mark.skipif(not hasattr(m, "has_exp_optional"), reason='no ') +def test_exp_optional(): + assert m.double_or_zero_exp(None) == 0 + assert m.double_or_zero_exp(42) == 84 + pytest.raises(TypeError, m.double_or_zero_exp, 'foo') + + assert m.half_or_none_exp(0) is None + assert m.half_or_none_exp(42) == 21 + pytest.raises(TypeError, m.half_or_none_exp, 'foo') + + assert m.test_nullopt_exp() == 42 + assert m.test_nullopt_exp(None) == 42 + assert m.test_nullopt_exp(42) == 42 + assert m.test_nullopt_exp(43) == 43 + + assert m.test_no_assign_exp() == 42 + assert m.test_no_assign_exp(None) == 42 + assert m.test_no_assign_exp(m.NoAssign(43)) == 43 + pytest.raises(TypeError, m.test_no_assign_exp, 43) + + +@pytest.mark.skipif(not hasattr(m, "load_variant"), reason='no ') +def test_variant(doc): + assert m.load_variant(1) == "int" + assert m.load_variant("1") == "std::string" + assert m.load_variant(1.0) == "double" + assert m.load_variant(None) == "std::nullptr_t" + + assert m.load_variant_2pass(1) == "int" + assert m.load_variant_2pass(1.0) == "double" + + assert m.cast_variant() == (5, "Hello") + + assert doc(m.load_variant) == "load_variant(arg0: Union[int, str, float, None]) -> str" + + +def test_vec_of_reference_wrapper(): + """#171: Can't return reference wrappers (or STL structures containing them)""" + assert str(m.return_vec_of_reference_wrapper(UserType(4))) == \ + "[UserType(1), UserType(2), UserType(3), UserType(4)]" + + +def test_stl_pass_by_pointer(msg): + """Passing nullptr or None to an STL container pointer is not expected to work""" + with pytest.raises(TypeError) as excinfo: + m.stl_pass_by_pointer() # default value is `nullptr` + assert msg(excinfo.value) == """ + stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported: + 1. (v: List[int]=None) -> List[int] + + Invoked with: + """ # noqa: E501 line too long + + with pytest.raises(TypeError) as excinfo: + m.stl_pass_by_pointer(None) + assert msg(excinfo.value) == """ + stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported: + 1. (v: List[int]=None) -> List[int] + + Invoked with: None + """ # noqa: E501 line too long + + assert m.stl_pass_by_pointer([1, 2, 3]) == [1, 2, 3] + + +def test_missing_header_message(): + """Trying convert `list` to a `std::vector`, or vice versa, without including + should result in a helpful suggestion in the error message""" + import pybind11_cross_module_tests as cm + + expected_message = ("Did you forget to `#include `? Or ,\n" + ", , etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module.") + + with pytest.raises(TypeError) as excinfo: + cm.missing_header_arg([1.0, 2.0, 3.0]) + assert expected_message in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + cm.missing_header_return() + assert expected_message in str(excinfo.value) diff --git a/external/pybind11/tests/test_stl_binders.cpp b/external/pybind11/tests/test_stl_binders.cpp new file mode 100644 index 0000000..a88b589 --- /dev/null +++ b/external/pybind11/tests/test_stl_binders.cpp @@ -0,0 +1,107 @@ +/* + tests/test_stl_binders.cpp -- Usage of stl_binders functions + + Copyright (c) 2016 Sergey Lyskov + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + +#include +#include +#include +#include +#include + +class El { +public: + El() = delete; + El(int v) : a(v) { } + + int a; +}; + +std::ostream & operator<<(std::ostream &s, El const&v) { + s << "El{" << v.a << '}'; + return s; +} + +/// Issue #487: binding std::vector with E non-copyable +class E_nc { +public: + explicit E_nc(int i) : value{i} {} + E_nc(const E_nc &) = delete; + E_nc &operator=(const E_nc &) = delete; + E_nc(E_nc &&) = default; + E_nc &operator=(E_nc &&) = default; + + int value; +}; + +template Container *one_to_n(int n) { + auto v = new Container(); + for (int i = 1; i <= n; i++) + v->emplace_back(i); + return v; +} + +template Map *times_ten(int n) { + auto m = new Map(); + for (int i = 1; i <= n; i++) + m->emplace(int(i), E_nc(10*i)); + return m; +} + +TEST_SUBMODULE(stl_binders, m) { + // test_vector_int + py::bind_vector>(m, "VectorInt", py::buffer_protocol()); + + // test_vector_custom + py::class_(m, "El") + .def(py::init()); + py::bind_vector>(m, "VectorEl"); + py::bind_vector>>(m, "VectorVectorEl"); + + // test_map_string_double + py::bind_map>(m, "MapStringDouble"); + py::bind_map>(m, "UnorderedMapStringDouble"); + + // test_map_string_double_const + py::bind_map>(m, "MapStringDoubleConst"); + py::bind_map>(m, "UnorderedMapStringDoubleConst"); + + py::class_(m, "ENC") + .def(py::init()) + .def_readwrite("value", &E_nc::value); + + // test_noncopyable_containers + py::bind_vector>(m, "VectorENC"); + m.def("get_vnc", &one_to_n>, py::return_value_policy::reference); + py::bind_vector>(m, "DequeENC"); + m.def("get_dnc", &one_to_n>, py::return_value_policy::reference); + py::bind_map>(m, "MapENC"); + m.def("get_mnc", ×_ten>, py::return_value_policy::reference); + py::bind_map>(m, "UmapENC"); + m.def("get_umnc", ×_ten>, py::return_value_policy::reference); + + // test_vector_buffer + py::bind_vector>(m, "VectorUChar", py::buffer_protocol()); + // no dtype declared for this version: + struct VUndeclStruct { bool w; uint32_t x; double y; bool z; }; + m.def("create_undeclstruct", [m] () mutable { + py::bind_vector>(m, "VectorUndeclStruct", py::buffer_protocol()); + }); + + // The rest depends on numpy: + try { py::module::import("numpy"); } + catch (...) { return; } + + // test_vector_buffer_numpy + struct VStruct { bool w; uint32_t x; double y; bool z; }; + PYBIND11_NUMPY_DTYPE(VStruct, w, x, y, z); + py::class_(m, "VStruct").def_readwrite("x", &VStruct::x); + py::bind_vector>(m, "VectorStruct", py::buffer_protocol()); + m.def("get_vectorstruct", [] {return std::vector {{0, 5, 3.0, 1}, {1, 30, -1e4, 0}};}); +} diff --git a/external/pybind11/tests/test_stl_binders.py b/external/pybind11/tests/test_stl_binders.py new file mode 100644 index 0000000..bf1aa67 --- /dev/null +++ b/external/pybind11/tests/test_stl_binders.py @@ -0,0 +1,183 @@ +import pytest +import sys +from pybind11_tests import stl_binders as m + +with pytest.suppress(ImportError): + import numpy as np + + +def test_vector_int(): + v_int = m.VectorInt([0, 0]) + assert len(v_int) == 2 + assert bool(v_int) is True + + v_int2 = m.VectorInt([0, 0]) + assert v_int == v_int2 + v_int2[1] = 1 + assert v_int != v_int2 + + v_int2.append(2) + v_int2.insert(0, 1) + v_int2.insert(0, 2) + v_int2.insert(0, 3) + v_int2.insert(6, 3) + assert str(v_int2) == "VectorInt[3, 2, 1, 0, 1, 2, 3]" + with pytest.raises(IndexError): + v_int2.insert(8, 4) + + v_int.append(99) + v_int2[2:-2] = v_int + assert v_int2 == m.VectorInt([3, 2, 0, 0, 99, 2, 3]) + del v_int2[1:3] + assert v_int2 == m.VectorInt([3, 0, 99, 2, 3]) + del v_int2[0] + assert v_int2 == m.VectorInt([0, 99, 2, 3]) + + +# related to the PyPy's buffer protocol. +@pytest.unsupported_on_pypy +def test_vector_buffer(): + b = bytearray([1, 2, 3, 4]) + v = m.VectorUChar(b) + assert v[1] == 2 + v[2] = 5 + mv = memoryview(v) # We expose the buffer interface + if sys.version_info.major > 2: + assert mv[2] == 5 + mv[2] = 6 + else: + assert mv[2] == '\x05' + mv[2] = '\x06' + assert v[2] == 6 + + with pytest.raises(RuntimeError) as excinfo: + m.create_undeclstruct() # Undeclared struct contents, no buffer interface + assert "NumPy type info missing for " in str(excinfo.value) + + +@pytest.unsupported_on_pypy +@pytest.requires_numpy +def test_vector_buffer_numpy(): + a = np.array([1, 2, 3, 4], dtype=np.int32) + with pytest.raises(TypeError): + m.VectorInt(a) + + a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]], dtype=np.uintc) + v = m.VectorInt(a[0, :]) + assert len(v) == 4 + assert v[2] == 3 + ma = np.asarray(v) + ma[2] = 5 + assert v[2] == 5 + + v = m.VectorInt(a[:, 1]) + assert len(v) == 3 + assert v[2] == 10 + + v = m.get_vectorstruct() + assert v[0].x == 5 + ma = np.asarray(v) + ma[1]['x'] = 99 + assert v[1].x == 99 + + v = m.VectorStruct(np.zeros(3, dtype=np.dtype([('w', 'bool'), ('x', 'I'), + ('y', 'float64'), ('z', 'bool')], align=True))) + assert len(v) == 3 + + +def test_vector_bool(): + import pybind11_cross_module_tests as cm + + vv_c = cm.VectorBool() + for i in range(10): + vv_c.append(i % 2 == 0) + for i in range(10): + assert vv_c[i] == (i % 2 == 0) + assert str(vv_c) == "VectorBool[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]" + + +def test_vector_custom(): + v_a = m.VectorEl() + v_a.append(m.El(1)) + v_a.append(m.El(2)) + assert str(v_a) == "VectorEl[El{1}, El{2}]" + + vv_a = m.VectorVectorEl() + vv_a.append(v_a) + vv_b = vv_a[0] + assert str(vv_b) == "VectorEl[El{1}, El{2}]" + + +def test_map_string_double(): + mm = m.MapStringDouble() + mm['a'] = 1 + mm['b'] = 2.5 + + assert list(mm) == ['a', 'b'] + assert list(mm.items()) == [('a', 1), ('b', 2.5)] + assert str(mm) == "MapStringDouble{a: 1, b: 2.5}" + + um = m.UnorderedMapStringDouble() + um['ua'] = 1.1 + um['ub'] = 2.6 + + assert sorted(list(um)) == ['ua', 'ub'] + assert sorted(list(um.items())) == [('ua', 1.1), ('ub', 2.6)] + assert "UnorderedMapStringDouble" in str(um) + + +def test_map_string_double_const(): + mc = m.MapStringDoubleConst() + mc['a'] = 10 + mc['b'] = 20.5 + assert str(mc) == "MapStringDoubleConst{a: 10, b: 20.5}" + + umc = m.UnorderedMapStringDoubleConst() + umc['a'] = 11 + umc['b'] = 21.5 + + str(umc) + + +def test_noncopyable_containers(): + # std::vector + vnc = m.get_vnc(5) + for i in range(0, 5): + assert vnc[i].value == i + 1 + + for i, j in enumerate(vnc, start=1): + assert j.value == i + + # std::deque + dnc = m.get_dnc(5) + for i in range(0, 5): + assert dnc[i].value == i + 1 + + i = 1 + for j in dnc: + assert(j.value == i) + i += 1 + + # std::map + mnc = m.get_mnc(5) + for i in range(1, 6): + assert mnc[i].value == 10 * i + + vsum = 0 + for k, v in mnc.items(): + assert v.value == 10 * k + vsum += v.value + + assert vsum == 150 + + # std::unordered_map + mnc = m.get_umnc(5) + for i in range(1, 6): + assert mnc[i].value == 10 * i + + vsum = 0 + for k, v in mnc.items(): + assert v.value == 10 * k + vsum += v.value + + assert vsum == 150 diff --git a/external/pybind11/tests/test_virtual_functions.cpp b/external/pybind11/tests/test_virtual_functions.cpp new file mode 100644 index 0000000..953b390 --- /dev/null +++ b/external/pybind11/tests/test_virtual_functions.cpp @@ -0,0 +1,450 @@ +/* + tests/test_virtual_functions.cpp -- overriding virtual functions from Python + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include "constructor_stats.h" +#include + +/* This is an example class that we'll want to be able to extend from Python */ +class ExampleVirt { +public: + ExampleVirt(int state) : state(state) { print_created(this, state); } + ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); } + ExampleVirt(ExampleVirt &&e) : state(e.state) { print_move_created(this); e.state = 0; } + ~ExampleVirt() { print_destroyed(this); } + + virtual int run(int value) { + py::print("Original implementation of " + "ExampleVirt::run(state={}, value={}, str1={}, str2={})"_s.format(state, value, get_string1(), *get_string2())); + return state + value; + } + + virtual bool run_bool() = 0; + virtual void pure_virtual() = 0; + + // Returning a reference/pointer to a type converted from python (numbers, strings, etc.) is a + // bit trickier, because the actual int& or std::string& or whatever only exists temporarily, so + // we have to handle it specially in the trampoline class (see below). + virtual const std::string &get_string1() { return str1; } + virtual const std::string *get_string2() { return &str2; } + +private: + int state; + const std::string str1{"default1"}, str2{"default2"}; +}; + +/* This is a wrapper class that must be generated */ +class PyExampleVirt : public ExampleVirt { +public: + using ExampleVirt::ExampleVirt; /* Inherit constructors */ + + int run(int value) override { + /* Generate wrapping code that enables native function overloading */ + PYBIND11_OVERLOAD( + int, /* Return type */ + ExampleVirt, /* Parent class */ + run, /* Name of function */ + value /* Argument(s) */ + ); + } + + bool run_bool() override { + PYBIND11_OVERLOAD_PURE( + bool, /* Return type */ + ExampleVirt, /* Parent class */ + run_bool, /* Name of function */ + /* This function has no arguments. The trailing comma + in the previous line is needed for some compilers */ + ); + } + + void pure_virtual() override { + PYBIND11_OVERLOAD_PURE( + void, /* Return type */ + ExampleVirt, /* Parent class */ + pure_virtual, /* Name of function */ + /* This function has no arguments. The trailing comma + in the previous line is needed for some compilers */ + ); + } + + // We can return reference types for compatibility with C++ virtual interfaces that do so, but + // note they have some significant limitations (see the documentation). + const std::string &get_string1() override { + PYBIND11_OVERLOAD( + const std::string &, /* Return type */ + ExampleVirt, /* Parent class */ + get_string1, /* Name of function */ + /* (no arguments) */ + ); + } + + const std::string *get_string2() override { + PYBIND11_OVERLOAD( + const std::string *, /* Return type */ + ExampleVirt, /* Parent class */ + get_string2, /* Name of function */ + /* (no arguments) */ + ); + } + +}; + +class NonCopyable { +public: + NonCopyable(int a, int b) : value{new int(a*b)} { print_created(this, a, b); } + NonCopyable(NonCopyable &&o) { value = std::move(o.value); print_move_created(this); } + NonCopyable(const NonCopyable &) = delete; + NonCopyable() = delete; + void operator=(const NonCopyable &) = delete; + void operator=(NonCopyable &&) = delete; + std::string get_value() const { + if (value) return std::to_string(*value); else return "(null)"; + } + ~NonCopyable() { print_destroyed(this); } + +private: + std::unique_ptr value; +}; + +// This is like the above, but is both copy and movable. In effect this means it should get moved +// when it is not referenced elsewhere, but copied if it is still referenced. +class Movable { +public: + Movable(int a, int b) : value{a+b} { print_created(this, a, b); } + Movable(const Movable &m) { value = m.value; print_copy_created(this); } + Movable(Movable &&m) { value = std::move(m.value); print_move_created(this); } + std::string get_value() const { return std::to_string(value); } + ~Movable() { print_destroyed(this); } +private: + int value; +}; + +class NCVirt { +public: + virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); } + virtual Movable get_movable(int a, int b) = 0; + + std::string print_nc(int a, int b) { return get_noncopyable(a, b).get_value(); } + std::string print_movable(int a, int b) { return get_movable(a, b).get_value(); } +}; +class NCVirtTrampoline : public NCVirt { +#if !defined(__INTEL_COMPILER) + NonCopyable get_noncopyable(int a, int b) override { + PYBIND11_OVERLOAD(NonCopyable, NCVirt, get_noncopyable, a, b); + } +#endif + Movable get_movable(int a, int b) override { + PYBIND11_OVERLOAD_PURE(Movable, NCVirt, get_movable, a, b); + } +}; + +struct Base { + /* for some reason MSVC2015 can't compile this if the function is pure virtual */ + virtual std::string dispatch() const { return {}; }; + virtual ~Base() = default; +}; + +struct DispatchIssue : Base { + virtual std::string dispatch() const { + PYBIND11_OVERLOAD_PURE(std::string, Base, dispatch, /* no arguments */); + } +}; + +// Forward declaration (so that we can put the main tests here; the inherited virtual approaches are +// rather long). +void initialize_inherited_virtuals(py::module &m); + +TEST_SUBMODULE(virtual_functions, m) { + // test_override + py::class_(m, "ExampleVirt") + .def(py::init()) + /* Reference original class in function definitions */ + .def("run", &ExampleVirt::run) + .def("run_bool", &ExampleVirt::run_bool) + .def("pure_virtual", &ExampleVirt::pure_virtual); + + py::class_(m, "NonCopyable") + .def(py::init()); + + py::class_(m, "Movable") + .def(py::init()); + + // test_move_support +#if !defined(__INTEL_COMPILER) + py::class_(m, "NCVirt") + .def(py::init<>()) + .def("get_noncopyable", &NCVirt::get_noncopyable) + .def("get_movable", &NCVirt::get_movable) + .def("print_nc", &NCVirt::print_nc) + .def("print_movable", &NCVirt::print_movable); +#endif + + m.def("runExampleVirt", [](ExampleVirt *ex, int value) { return ex->run(value); }); + m.def("runExampleVirtBool", [](ExampleVirt* ex) { return ex->run_bool(); }); + m.def("runExampleVirtVirtual", [](ExampleVirt *ex) { ex->pure_virtual(); }); + + m.def("cstats_debug", &ConstructorStats::get); + initialize_inherited_virtuals(m); + + // test_alias_delay_initialization1 + // don't invoke Python dispatch classes by default when instantiating C++ classes + // that were not extended on the Python side + struct A { + virtual ~A() {} + virtual void f() { py::print("A.f()"); } + }; + + struct PyA : A { + PyA() { py::print("PyA.PyA()"); } + ~PyA() { py::print("PyA.~PyA()"); } + + void f() override { + py::print("PyA.f()"); + PYBIND11_OVERLOAD(void, A, f); + } + }; + + py::class_(m, "A") + .def(py::init<>()) + .def("f", &A::f); + + m.def("call_f", [](A *a) { a->f(); }); + + // test_alias_delay_initialization2 + // ... unless we explicitly request it, as in this example: + struct A2 { + virtual ~A2() {} + virtual void f() { py::print("A2.f()"); } + }; + + struct PyA2 : A2 { + PyA2() { py::print("PyA2.PyA2()"); } + ~PyA2() { py::print("PyA2.~PyA2()"); } + void f() override { + py::print("PyA2.f()"); + PYBIND11_OVERLOAD(void, A2, f); + } + }; + + py::class_(m, "A2") + .def(py::init_alias<>()) + .def(py::init([](int) { return new PyA2(); })) + .def("f", &A2::f); + + m.def("call_f", [](A2 *a2) { a2->f(); }); + + // test_dispatch_issue + // #159: virtual function dispatch has problems with similar-named functions + py::class_(m, "DispatchIssue") + .def(py::init<>()) + .def("dispatch", &Base::dispatch); + + m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); }); + + // test_override_ref + // #392/397: overridding reference-returning functions + class OverrideTest { + public: + struct A { std::string value = "hi"; }; + std::string v; + A a; + explicit OverrideTest(const std::string &v) : v{v} {} + virtual std::string str_value() { return v; } + virtual std::string &str_ref() { return v; } + virtual A A_value() { return a; } + virtual A &A_ref() { return a; } + virtual ~OverrideTest() = default; + }; + + class PyOverrideTest : public OverrideTest { + public: + using OverrideTest::OverrideTest; + std::string str_value() override { PYBIND11_OVERLOAD(std::string, OverrideTest, str_value); } + // Not allowed (uncommenting should hit a static_assert failure): we can't get a reference + // to a python numeric value, since we only copy values in the numeric type caster: +// std::string &str_ref() override { PYBIND11_OVERLOAD(std::string &, OverrideTest, str_ref); } + // But we can work around it like this: + private: + std::string _tmp; + std::string str_ref_helper() { PYBIND11_OVERLOAD(std::string, OverrideTest, str_ref); } + public: + std::string &str_ref() override { return _tmp = str_ref_helper(); } + + A A_value() override { PYBIND11_OVERLOAD(A, OverrideTest, A_value); } + A &A_ref() override { PYBIND11_OVERLOAD(A &, OverrideTest, A_ref); } + }; + + py::class_(m, "OverrideTest_A") + .def_readwrite("value", &OverrideTest::A::value); + py::class_(m, "OverrideTest") + .def(py::init()) + .def("str_value", &OverrideTest::str_value) +// .def("str_ref", &OverrideTest::str_ref) + .def("A_value", &OverrideTest::A_value) + .def("A_ref", &OverrideTest::A_ref); +} + + +// Inheriting virtual methods. We do two versions here: the repeat-everything version and the +// templated trampoline versions mentioned in docs/advanced.rst. +// +// These base classes are exactly the same, but we technically need distinct +// classes for this example code because we need to be able to bind them +// properly (pybind11, sensibly, doesn't allow us to bind the same C++ class to +// multiple python classes). +class A_Repeat { +#define A_METHODS \ +public: \ + virtual int unlucky_number() = 0; \ + virtual std::string say_something(unsigned times) { \ + std::string s = ""; \ + for (unsigned i = 0; i < times; ++i) \ + s += "hi"; \ + return s; \ + } \ + std::string say_everything() { \ + return say_something(1) + " " + std::to_string(unlucky_number()); \ + } +A_METHODS + virtual ~A_Repeat() = default; +}; +class B_Repeat : public A_Repeat { +#define B_METHODS \ +public: \ + int unlucky_number() override { return 13; } \ + std::string say_something(unsigned times) override { \ + return "B says hi " + std::to_string(times) + " times"; \ + } \ + virtual double lucky_number() { return 7.0; } +B_METHODS +}; +class C_Repeat : public B_Repeat { +#define C_METHODS \ +public: \ + int unlucky_number() override { return 4444; } \ + double lucky_number() override { return 888; } +C_METHODS +}; +class D_Repeat : public C_Repeat { +#define D_METHODS // Nothing overridden. +D_METHODS +}; + +// Base classes for templated inheritance trampolines. Identical to the repeat-everything version: +class A_Tpl { A_METHODS; virtual ~A_Tpl() = default; }; +class B_Tpl : public A_Tpl { B_METHODS }; +class C_Tpl : public B_Tpl { C_METHODS }; +class D_Tpl : public C_Tpl { D_METHODS }; + + +// Inheritance approach 1: each trampoline gets every virtual method (11 in total) +class PyA_Repeat : public A_Repeat { +public: + using A_Repeat::A_Repeat; + int unlucky_number() override { PYBIND11_OVERLOAD_PURE(int, A_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, A_Repeat, say_something, times); } +}; +class PyB_Repeat : public B_Repeat { +public: + using B_Repeat::B_Repeat; + int unlucky_number() override { PYBIND11_OVERLOAD(int, B_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, B_Repeat, say_something, times); } + double lucky_number() override { PYBIND11_OVERLOAD(double, B_Repeat, lucky_number, ); } +}; +class PyC_Repeat : public C_Repeat { +public: + using C_Repeat::C_Repeat; + int unlucky_number() override { PYBIND11_OVERLOAD(int, C_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, C_Repeat, say_something, times); } + double lucky_number() override { PYBIND11_OVERLOAD(double, C_Repeat, lucky_number, ); } +}; +class PyD_Repeat : public D_Repeat { +public: + using D_Repeat::D_Repeat; + int unlucky_number() override { PYBIND11_OVERLOAD(int, D_Repeat, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, D_Repeat, say_something, times); } + double lucky_number() override { PYBIND11_OVERLOAD(double, D_Repeat, lucky_number, ); } +}; + +// Inheritance approach 2: templated trampoline classes. +// +// Advantages: +// - we have only 2 (template) class and 4 method declarations (one per virtual method, plus one for +// any override of a pure virtual method), versus 4 classes and 6 methods (MI) or 4 classes and 11 +// methods (repeat). +// - Compared to MI, we also don't have to change the non-trampoline inheritance to virtual, and can +// properly inherit constructors. +// +// Disadvantage: +// - the compiler must still generate and compile 14 different methods (more, even, than the 11 +// required for the repeat approach) instead of the 6 required for MI. (If there was no pure +// method (or no pure method override), the number would drop down to the same 11 as the repeat +// approach). +template +class PyA_Tpl : public Base { +public: + using Base::Base; // Inherit constructors + int unlucky_number() override { PYBIND11_OVERLOAD_PURE(int, Base, unlucky_number, ); } + std::string say_something(unsigned times) override { PYBIND11_OVERLOAD(std::string, Base, say_something, times); } +}; +template +class PyB_Tpl : public PyA_Tpl { +public: + using PyA_Tpl::PyA_Tpl; // Inherit constructors (via PyA_Tpl's inherited constructors) + int unlucky_number() override { PYBIND11_OVERLOAD(int, Base, unlucky_number, ); } + double lucky_number() override { PYBIND11_OVERLOAD(double, Base, lucky_number, ); } +}; +// Since C_Tpl and D_Tpl don't declare any new virtual methods, we don't actually need these (we can +// use PyB_Tpl and PyB_Tpl for the trampoline classes instead): +/* +template class PyC_Tpl : public PyB_Tpl { +public: + using PyB_Tpl::PyB_Tpl; +}; +template class PyD_Tpl : public PyC_Tpl { +public: + using PyC_Tpl::PyC_Tpl; +}; +*/ + + +void initialize_inherited_virtuals(py::module &m) { + // test_inherited_virtuals + + // Method 1: repeat + py::class_(m, "A_Repeat") + .def(py::init<>()) + .def("unlucky_number", &A_Repeat::unlucky_number) + .def("say_something", &A_Repeat::say_something) + .def("say_everything", &A_Repeat::say_everything); + py::class_(m, "B_Repeat") + .def(py::init<>()) + .def("lucky_number", &B_Repeat::lucky_number); + py::class_(m, "C_Repeat") + .def(py::init<>()); + py::class_(m, "D_Repeat") + .def(py::init<>()); + + // test_ + // Method 2: Templated trampolines + py::class_>(m, "A_Tpl") + .def(py::init<>()) + .def("unlucky_number", &A_Tpl::unlucky_number) + .def("say_something", &A_Tpl::say_something) + .def("say_everything", &A_Tpl::say_everything); + py::class_>(m, "B_Tpl") + .def(py::init<>()) + .def("lucky_number", &B_Tpl::lucky_number); + py::class_>(m, "C_Tpl") + .def(py::init<>()); + py::class_>(m, "D_Tpl") + .def(py::init<>()); + +}; diff --git a/external/pybind11/tests/test_virtual_functions.py b/external/pybind11/tests/test_virtual_functions.py new file mode 100644 index 0000000..b91ebfa --- /dev/null +++ b/external/pybind11/tests/test_virtual_functions.py @@ -0,0 +1,371 @@ +import pytest + +from pybind11_tests import virtual_functions as m +from pybind11_tests import ConstructorStats + + +def test_override(capture, msg): + class ExtendedExampleVirt(m.ExampleVirt): + def __init__(self, state): + super(ExtendedExampleVirt, self).__init__(state + 1) + self.data = "Hello world" + + def run(self, value): + print('ExtendedExampleVirt::run(%i), calling parent..' % value) + return super(ExtendedExampleVirt, self).run(value + 1) + + def run_bool(self): + print('ExtendedExampleVirt::run_bool()') + return False + + def get_string1(self): + return "override1" + + def pure_virtual(self): + print('ExtendedExampleVirt::pure_virtual(): %s' % self.data) + + class ExtendedExampleVirt2(ExtendedExampleVirt): + def __init__(self, state): + super(ExtendedExampleVirt2, self).__init__(state + 1) + + def get_string2(self): + return "override2" + + ex12 = m.ExampleVirt(10) + with capture: + assert m.runExampleVirt(ex12, 20) == 30 + assert capture == """ + Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2) + """ # noqa: E501 line too long + + with pytest.raises(RuntimeError) as excinfo: + m.runExampleVirtVirtual(ex12) + assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"' + + ex12p = ExtendedExampleVirt(10) + with capture: + assert m.runExampleVirt(ex12p, 20) == 32 + assert capture == """ + ExtendedExampleVirt::run(20), calling parent.. + Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2) + """ # noqa: E501 line too long + with capture: + assert m.runExampleVirtBool(ex12p) is False + assert capture == "ExtendedExampleVirt::run_bool()" + with capture: + m.runExampleVirtVirtual(ex12p) + assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world" + + ex12p2 = ExtendedExampleVirt2(15) + with capture: + assert m.runExampleVirt(ex12p2, 50) == 68 + assert capture == """ + ExtendedExampleVirt::run(50), calling parent.. + Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2) + """ # noqa: E501 line too long + + cstats = ConstructorStats.get(m.ExampleVirt) + assert cstats.alive() == 3 + del ex12, ex12p, ex12p2 + assert cstats.alive() == 0 + assert cstats.values() == ['10', '11', '17'] + assert cstats.copy_constructions == 0 + assert cstats.move_constructions >= 0 + + +def test_alias_delay_initialization1(capture): + """`A` only initializes its trampoline class when we inherit from it + + If we just create and use an A instance directly, the trampoline initialization is + bypassed and we only initialize an A() instead (for performance reasons). + """ + class B(m.A): + def __init__(self): + super(B, self).__init__() + + def f(self): + print("In python f()") + + # C++ version + with capture: + a = m.A() + m.call_f(a) + del a + pytest.gc_collect() + assert capture == "A.f()" + + # Python version + with capture: + b = B() + m.call_f(b) + del b + pytest.gc_collect() + assert capture == """ + PyA.PyA() + PyA.f() + In python f() + PyA.~PyA() + """ + + +def test_alias_delay_initialization2(capture): + """`A2`, unlike the above, is configured to always initialize the alias + + While the extra initialization and extra class layer has small virtual dispatch + performance penalty, it also allows us to do more things with the trampoline + class such as defining local variables and performing construction/destruction. + """ + class B2(m.A2): + def __init__(self): + super(B2, self).__init__() + + def f(self): + print("In python B2.f()") + + # No python subclass version + with capture: + a2 = m.A2() + m.call_f(a2) + del a2 + pytest.gc_collect() + a3 = m.A2(1) + m.call_f(a3) + del a3 + pytest.gc_collect() + assert capture == """ + PyA2.PyA2() + PyA2.f() + A2.f() + PyA2.~PyA2() + PyA2.PyA2() + PyA2.f() + A2.f() + PyA2.~PyA2() + """ + + # Python subclass version + with capture: + b2 = B2() + m.call_f(b2) + del b2 + pytest.gc_collect() + assert capture == """ + PyA2.PyA2() + PyA2.f() + In python B2.f() + PyA2.~PyA2() + """ + + +# PyPy: Reference count > 1 causes call with noncopyable instance +# to fail in ncv1.print_nc() +@pytest.unsupported_on_pypy +@pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") +def test_move_support(): + class NCVirtExt(m.NCVirt): + def get_noncopyable(self, a, b): + # Constructs and returns a new instance: + nc = m.NonCopyable(a * a, b * b) + return nc + + def get_movable(self, a, b): + # Return a referenced copy + self.movable = m.Movable(a, b) + return self.movable + + class NCVirtExt2(m.NCVirt): + def get_noncopyable(self, a, b): + # Keep a reference: this is going to throw an exception + self.nc = m.NonCopyable(a, b) + return self.nc + + def get_movable(self, a, b): + # Return a new instance without storing it + return m.Movable(a, b) + + ncv1 = NCVirtExt() + assert ncv1.print_nc(2, 3) == "36" + assert ncv1.print_movable(4, 5) == "9" + ncv2 = NCVirtExt2() + assert ncv2.print_movable(7, 7) == "14" + # Don't check the exception message here because it differs under debug/non-debug mode + with pytest.raises(RuntimeError): + ncv2.print_nc(9, 9) + + nc_stats = ConstructorStats.get(m.NonCopyable) + mv_stats = ConstructorStats.get(m.Movable) + assert nc_stats.alive() == 1 + assert mv_stats.alive() == 1 + del ncv1, ncv2 + assert nc_stats.alive() == 0 + assert mv_stats.alive() == 0 + assert nc_stats.values() == ['4', '9', '9', '9'] + assert mv_stats.values() == ['4', '5', '7', '7'] + assert nc_stats.copy_constructions == 0 + assert mv_stats.copy_constructions == 1 + assert nc_stats.move_constructions >= 0 + assert mv_stats.move_constructions >= 0 + + +def test_dispatch_issue(msg): + """#159: virtual function dispatch has problems with similar-named functions""" + class PyClass1(m.DispatchIssue): + def dispatch(self): + return "Yay.." + + class PyClass2(m.DispatchIssue): + def dispatch(self): + with pytest.raises(RuntimeError) as excinfo: + super(PyClass2, self).dispatch() + assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' + + p = PyClass1() + return m.dispatch_issue_go(p) + + b = PyClass2() + assert m.dispatch_issue_go(b) == "Yay.." + + +def test_override_ref(): + """#392/397: overridding reference-returning functions""" + o = m.OverrideTest("asdf") + + # Not allowed (see associated .cpp comment) + # i = o.str_ref() + # assert o.str_ref() == "asdf" + assert o.str_value() == "asdf" + + assert o.A_value().value == "hi" + a = o.A_ref() + assert a.value == "hi" + a.value = "bye" + assert a.value == "bye" + + +def test_inherited_virtuals(): + class AR(m.A_Repeat): + def unlucky_number(self): + return 99 + + class AT(m.A_Tpl): + def unlucky_number(self): + return 999 + + obj = AR() + assert obj.say_something(3) == "hihihi" + assert obj.unlucky_number() == 99 + assert obj.say_everything() == "hi 99" + + obj = AT() + assert obj.say_something(3) == "hihihi" + assert obj.unlucky_number() == 999 + assert obj.say_everything() == "hi 999" + + for obj in [m.B_Repeat(), m.B_Tpl()]: + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 13 + assert obj.lucky_number() == 7.0 + assert obj.say_everything() == "B says hi 1 times 13" + + for obj in [m.C_Repeat(), m.C_Tpl()]: + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 4444 + assert obj.lucky_number() == 888.0 + assert obj.say_everything() == "B says hi 1 times 4444" + + class CR(m.C_Repeat): + def lucky_number(self): + return m.C_Repeat.lucky_number(self) + 1.25 + + obj = CR() + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 4444 + assert obj.lucky_number() == 889.25 + assert obj.say_everything() == "B says hi 1 times 4444" + + class CT(m.C_Tpl): + pass + + obj = CT() + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 4444 + assert obj.lucky_number() == 888.0 + assert obj.say_everything() == "B says hi 1 times 4444" + + class CCR(CR): + def lucky_number(self): + return CR.lucky_number(self) * 10 + + obj = CCR() + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 4444 + assert obj.lucky_number() == 8892.5 + assert obj.say_everything() == "B says hi 1 times 4444" + + class CCT(CT): + def lucky_number(self): + return CT.lucky_number(self) * 1000 + + obj = CCT() + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 4444 + assert obj.lucky_number() == 888000.0 + assert obj.say_everything() == "B says hi 1 times 4444" + + class DR(m.D_Repeat): + def unlucky_number(self): + return 123 + + def lucky_number(self): + return 42.0 + + for obj in [m.D_Repeat(), m.D_Tpl()]: + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 4444 + assert obj.lucky_number() == 888.0 + assert obj.say_everything() == "B says hi 1 times 4444" + + obj = DR() + assert obj.say_something(3) == "B says hi 3 times" + assert obj.unlucky_number() == 123 + assert obj.lucky_number() == 42.0 + assert obj.say_everything() == "B says hi 1 times 123" + + class DT(m.D_Tpl): + def say_something(self, times): + return "DT says:" + (' quack' * times) + + def unlucky_number(self): + return 1234 + + def lucky_number(self): + return -4.25 + + obj = DT() + assert obj.say_something(3) == "DT says: quack quack quack" + assert obj.unlucky_number() == 1234 + assert obj.lucky_number() == -4.25 + assert obj.say_everything() == "DT says: quack 1234" + + class DT2(DT): + def say_something(self, times): + return "DT2: " + ('QUACK' * times) + + def unlucky_number(self): + return -3 + + class BT(m.B_Tpl): + def say_something(self, times): + return "BT" * times + + def unlucky_number(self): + return -7 + + def lucky_number(self): + return -1.375 + + obj = BT() + assert obj.say_something(3) == "BTBTBT" + assert obj.unlucky_number() == -7 + assert obj.lucky_number() == -1.375 + assert obj.say_everything() == "BT -7" diff --git a/external/pybind11/tools/FindCatch.cmake b/external/pybind11/tools/FindCatch.cmake new file mode 100644 index 0000000..9d490c5 --- /dev/null +++ b/external/pybind11/tools/FindCatch.cmake @@ -0,0 +1,57 @@ +# - Find the Catch test framework or download it (single header) +# +# This is a quick module for internal use. It assumes that Catch is +# REQUIRED and that a minimum version is provided (not EXACT). If +# a suitable version isn't found locally, the single header file +# will be downloaded and placed in the build dir: PROJECT_BINARY_DIR. +# +# This code sets the following variables: +# CATCH_INCLUDE_DIR - path to catch.hpp +# CATCH_VERSION - version number + +if(NOT Catch_FIND_VERSION) + message(FATAL_ERROR "A version number must be specified.") +elseif(Catch_FIND_REQUIRED) + message(FATAL_ERROR "This module assumes Catch is not required.") +elseif(Catch_FIND_VERSION_EXACT) + message(FATAL_ERROR "Exact version numbers are not supported, only minimum.") +endif() + +# Extract the version number from catch.hpp +function(_get_catch_version) + file(STRINGS "${CATCH_INCLUDE_DIR}/catch.hpp" version_line REGEX "Catch v.*" LIMIT_COUNT 1) + if(version_line MATCHES "Catch v([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(CATCH_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) + endif() +endfunction() + +# Download the single-header version of Catch +function(_download_catch version destination_dir) + message(STATUS "Downloading catch v${version}...") + set(url https://github.com/philsquared/Catch/releases/download/v${version}/catch.hpp) + file(DOWNLOAD ${url} "${destination_dir}/catch.hpp" STATUS status) + list(GET status 0 error) + if(error) + message(FATAL_ERROR "Could not download ${url}") + endif() + set(CATCH_INCLUDE_DIR "${destination_dir}" CACHE INTERNAL "") +endfunction() + +# Look for catch locally +find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATH_SUFFIXES catch) +if(CATCH_INCLUDE_DIR) + _get_catch_version() +endif() + +# Download the header if it wasn't found or if it's outdated +if(NOT CATCH_VERSION OR CATCH_VERSION VERSION_LESS ${Catch_FIND_VERSION}) + if(DOWNLOAD_CATCH) + _download_catch(${Catch_FIND_VERSION} "${PROJECT_BINARY_DIR}/catch/") + _get_catch_version() + else() + set(CATCH_FOUND FALSE) + return() + endif() +endif() + +set(CATCH_FOUND TRUE) diff --git a/external/pybind11/tools/FindEigen3.cmake b/external/pybind11/tools/FindEigen3.cmake new file mode 100644 index 0000000..9c546a0 --- /dev/null +++ b/external/pybind11/tools/FindEigen3.cmake @@ -0,0 +1,81 @@ +# - Try to find Eigen3 lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen3 3.1.2) +# to require version 3.1.2 or newer of Eigen3. +# +# Once done this will define +# +# EIGEN3_FOUND - system has eigen lib with correct version +# EIGEN3_INCLUDE_DIR - the eigen include directory +# EIGEN3_VERSION - eigen version + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen3_FIND_VERSION) + if(NOT Eigen3_FIND_VERSION_MAJOR) + set(Eigen3_FIND_VERSION_MAJOR 2) + endif(NOT Eigen3_FIND_VERSION_MAJOR) + if(NOT Eigen3_FIND_VERSION_MINOR) + set(Eigen3_FIND_VERSION_MINOR 91) + endif(NOT Eigen3_FIND_VERSION_MINOR) + if(NOT Eigen3_FIND_VERSION_PATCH) + set(Eigen3_FIND_VERSION_PATCH 0) + endif(NOT Eigen3_FIND_VERSION_PATCH) + + set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") +endif(NOT Eigen3_FIND_VERSION) + +macro(_eigen3_check_version) + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") + set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") + set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") + set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) + if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK FALSE) + else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK TRUE) + endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + + if(NOT EIGEN3_VERSION_OK) + + message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " + "but at least version ${Eigen3_FIND_VERSION} is required") + endif(NOT EIGEN3_VERSION_OK) +endmacro(_eigen3_check_version) + +if (EIGEN3_INCLUDE_DIR) + + # in cache already + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + +else (EIGEN3_INCLUDE_DIR) + + find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + PATHS + ${CMAKE_INSTALL_PREFIX}/include + ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen + ) + + if(EIGEN3_INCLUDE_DIR) + _eigen3_check_version() + endif(EIGEN3_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) + + mark_as_advanced(EIGEN3_INCLUDE_DIR) + +endif(EIGEN3_INCLUDE_DIR) + diff --git a/external/pybind11/tools/FindPythonLibsNew.cmake b/external/pybind11/tools/FindPythonLibsNew.cmake new file mode 100644 index 0000000..ad3ed48 --- /dev/null +++ b/external/pybind11/tools/FindPythonLibsNew.cmake @@ -0,0 +1,195 @@ +# - Find python libraries +# This module finds the libraries corresponding to the Python interpeter +# FindPythonInterp provides. +# This code sets the following variables: +# +# PYTHONLIBS_FOUND - have the Python libs been found +# PYTHON_PREFIX - path to the Python installation +# PYTHON_LIBRARIES - path to the python library +# PYTHON_INCLUDE_DIRS - path to where Python.h is found +# PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' +# PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string +# PYTHON_SITE_PACKAGES - path to installation site-packages +# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build +# +# Thanks to talljimbo for the patch adding the 'LDVERSION' config +# variable usage. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# Copyright 2012 Continuum Analytics, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. +if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) + return() +endif() + +# Use the Python interpreter to find the libs. +if(PythonLibsNew_FIND_REQUIRED) + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} REQUIRED) +else() + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION}) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(PYTHONLIBS_FOUND FALSE) + return() +endif() + +# According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter +# testing whether sys has the gettotalrefcount function is a reliable, cross-platform +# way to detect a CPython debug interpreter. +# +# The library suffix is from the config var LDVERSION sometimes, otherwise +# VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(sys.prefix); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +print(s.get_config_var('LIBDIR') or ''); +print(s.get_config_var('MULTIARCH') or ''); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) + +if(NOT _PYTHON_SUCCESS MATCHES 0) + if(PythonLibsNew_FIND_REQUIRED) + message(FATAL_ERROR + "Python config failure:\n${_PYTHON_ERROR_VALUE}") + endif() + set(PYTHONLIBS_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) +list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) +list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) +list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) + +# Make sure the Python has the same pointer-size as the chosen compiler +# Skip if CMAKE_SIZEOF_VOID_P is not defined +if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) + if(PythonLibsNew_FIND_REQUIRED) + math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") + math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") + message(FATAL_ERROR + "Python config failure: Python is ${_PYTHON_BITS}-bit, " + "chosen compiler is ${_CMAKE_BITS}-bit") + endif() + set(PYTHONLIBS_FOUND FALSE) + return() +endif() + +# The built-in FindPython didn't always give the version numbers +string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) +list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) +list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) +list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX}) +string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR}) +string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES}) + +if(CMAKE_HOST_WIN32) + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + + # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the + # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. + if(NOT EXISTS "${PYTHON_LIBRARY}") + get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) + set(PYTHON_LIBRARY + "${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + endif() + + # raise an error if the python libs are still not found. + if(NOT EXISTS "${PYTHON_LIBRARY}") + message(FATAL_ERROR "Python libraries not found") + endif() + +else() + if(PYTHON_MULTIARCH) + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") + endif() + #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") + # Probably this needs to be more involved. It would be nice if the config + # information the python interpreter itself gave us were more complete. + find_library(PYTHON_LIBRARY + NAMES "python${PYTHON_LIBRARY_SUFFIX}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_DEFAULT_PATH) + + # If all else fails, just set the name/version and let the linker figure out the path. + if(NOT PYTHON_LIBRARY) + set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) + endif() +endif() + +MARK_AS_ADVANCED( + PYTHON_LIBRARY + PYTHON_INCLUDE_DIR +) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + +find_package_message(PYTHON + "Found PythonLibs: ${PYTHON_LIBRARY}" + "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") + +set(PYTHONLIBS_FOUND TRUE) diff --git a/external/pybind11/tools/check-style.sh b/external/pybind11/tools/check-style.sh new file mode 100755 index 0000000..a9eeb17 --- /dev/null +++ b/external/pybind11/tools/check-style.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# +# Script to check include/test code for common pybind11 code style errors. +# +# This script currently checks for +# +# 1. use of tabs instead of spaces +# 2. MSDOS-style CRLF endings +# 3. trailing spaces +# 4. missing space between keyword and parenthesis, e.g.: for(, if(, while( +# 5. Missing space between right parenthesis and brace, e.g. 'for (...){' +# 6. opening brace on its own line. It should always be on the same line as the +# if/while/for/do statment. +# +# Invoke as: tools/check-style.sh +# + +check_style_errors=0 +IFS=$'\n' + +found="$( GREP_COLORS='mt=41' GREP_COLOR='41' grep $'\t' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always )" +if [ -n "$found" ]; then + # The mt=41 sets a red background for matched tabs: + echo -e '\033[31;01mError: found tab characters in the following files:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + + +found="$( grep -IUlr $'\r' include tests/*.{cpp,py,h} docs/*.rst --color=always )" +if [ -n "$found" ]; then + echo -e '\033[31;01mError: found CRLF characters in the following files:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(GREP_COLORS='mt=41' GREP_COLOR='41' grep '[[:blank:]]\+$' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always )" +if [ -n "$found" ]; then + # The mt=41 sets a red background for matched trailing spaces + echo -e '\033[31;01mError: found trailing spaces in the following files:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(grep '\<\(if\|for\|while\|catch\)(\|){' include tests/*.{cpp,h} -rn --color=always)" +if [ -n "$found" ]; then + echo -e '\033[31;01mError: found the following coding style problems:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(awk ' +function prefix(filename, lineno) { + return " \033[35m" filename "\033[36m:\033[32m" lineno "\033[36m:\033[0m" +} +function mark(pattern, string) { sub(pattern, "\033[01;31m&\033[0m", string); return string } +last && /^\s*{/ { + print prefix(FILENAME, FNR-1) mark("\\)\\s*$", last) + print prefix(FILENAME, FNR) mark("^\\s*{", $0) + last="" +} +{ last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" } +' $(find include -type f) tests/*.{cpp,h} docs/*.rst)" +if [ -n "$found" ]; then + check_style_errors=1 + echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m' + echo "$found" +fi + +exit $check_style_errors diff --git a/external/pybind11/tools/libsize.py b/external/pybind11/tools/libsize.py new file mode 100644 index 0000000..5dcb8b0 --- /dev/null +++ b/external/pybind11/tools/libsize.py @@ -0,0 +1,38 @@ +from __future__ import print_function, division +import os +import sys + +# Internal build script for generating debugging test .so size. +# Usage: +# python libsize.py file.so save.txt -- displays the size of file.so and, if save.txt exists, compares it to the +# size in it, then overwrites save.txt with the new size for future runs. + +if len(sys.argv) != 3: + sys.exit("Invalid arguments: usage: python libsize.py file.so save.txt") + +lib = sys.argv[1] +save = sys.argv[2] + +if not os.path.exists(lib): + sys.exit("Error: requested file ({}) does not exist".format(lib)) + +libsize = os.path.getsize(lib) + +print("------", os.path.basename(lib), "file size:", libsize, end='') + +if os.path.exists(save): + with open(save) as sf: + oldsize = int(sf.readline()) + + if oldsize > 0: + change = libsize - oldsize + if change == 0: + print(" (no change)") + else: + print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize)) +else: + print() + +with open(save, 'w') as sf: + sf.write(str(libsize)) + diff --git a/external/pybind11/tools/mkdoc.py b/external/pybind11/tools/mkdoc.py new file mode 100644 index 0000000..1fd8cce --- /dev/null +++ b/external/pybind11/tools/mkdoc.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# +# Syntax: mkdoc.py [-I ..] [.. a list of header files ..] +# +# Extract documentation from C++ header files to use it in Python bindings +# + +import os +import sys +import platform +import re +import textwrap + +from clang import cindex +from clang.cindex import CursorKind +from collections import OrderedDict +from threading import Thread, Semaphore +from multiprocessing import cpu_count + +RECURSE_LIST = [ + CursorKind.TRANSLATION_UNIT, + CursorKind.NAMESPACE, + CursorKind.CLASS_DECL, + CursorKind.STRUCT_DECL, + CursorKind.ENUM_DECL, + CursorKind.CLASS_TEMPLATE +] + +PRINT_LIST = [ + CursorKind.CLASS_DECL, + CursorKind.STRUCT_DECL, + CursorKind.ENUM_DECL, + CursorKind.ENUM_CONSTANT_DECL, + CursorKind.CLASS_TEMPLATE, + CursorKind.FUNCTION_DECL, + CursorKind.FUNCTION_TEMPLATE, + CursorKind.CONVERSION_FUNCTION, + CursorKind.CXX_METHOD, + CursorKind.CONSTRUCTOR, + CursorKind.FIELD_DECL +] + +CPP_OPERATORS = { + '<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array', + '+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=': + 'imod', '&=': 'iand', '|=': 'ior', '^=': 'ixor', '<<=': 'ilshift', + '>>=': 'irshift', '++': 'inc', '--': 'dec', '<<': 'lshift', '>>': + 'rshift', '&&': 'land', '||': 'lor', '!': 'lnot', '~': 'bnot', + '&': 'band', '|': 'bor', '+': 'add', '-': 'sub', '*': 'mul', '/': + 'div', '%': 'mod', '<': 'lt', '>': 'gt', '=': 'assign', '()': 'call' +} + +CPP_OPERATORS = OrderedDict( + sorted(CPP_OPERATORS.items(), key=lambda t: -len(t[0]))) + +job_count = cpu_count() +job_semaphore = Semaphore(job_count) + +output = [] + +def d(s): + return s.decode('utf8') + + +def sanitize_name(name): + name = re.sub(r'type-parameter-0-([0-9]+)', r'T\1', name) + for k, v in CPP_OPERATORS.items(): + name = name.replace('operator%s' % k, 'operator_%s' % v) + name = re.sub('<.*>', '', name) + name = ''.join([ch if ch.isalnum() else '_' for ch in name]) + name = re.sub('_$', '', re.sub('_+', '_', name)) + return '__doc_' + name + + +def process_comment(comment): + result = '' + + # Remove C++ comment syntax + leading_spaces = float('inf') + for s in comment.expandtabs(tabsize=4).splitlines(): + s = s.strip() + if s.startswith('/*'): + s = s[2:].lstrip('*') + elif s.endswith('*/'): + s = s[:-2].rstrip('*') + elif s.startswith('///'): + s = s[3:] + if s.startswith('*'): + s = s[1:] + if len(s) > 0: + leading_spaces = min(leading_spaces, len(s) - len(s.lstrip())) + result += s + '\n' + + if leading_spaces != float('inf'): + result2 = "" + for s in result.splitlines(): + result2 += s[leading_spaces:] + '\n' + result = result2 + + # Doxygen tags + cpp_group = '([\w:]+)' + param_group = '([\[\w:\]]+)' + + s = result + s = re.sub(r'\\c\s+%s' % cpp_group, r'``\1``', s) + s = re.sub(r'\\a\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\e\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\em\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\b\s+%s' % cpp_group, r'**\1**', s) + s = re.sub(r'\\ingroup\s+%s' % cpp_group, r'', s) + s = re.sub(r'\\param%s?\s+%s' % (param_group, cpp_group), + r'\n\n$Parameter ``\2``:\n\n', s) + s = re.sub(r'\\tparam%s?\s+%s' % (param_group, cpp_group), + r'\n\n$Template parameter ``\2``:\n\n', s) + + for in_, out_ in { + 'return': 'Returns', + 'author': 'Author', + 'authors': 'Authors', + 'copyright': 'Copyright', + 'date': 'Date', + 'remark': 'Remark', + 'sa': 'See also', + 'see': 'See also', + 'extends': 'Extends', + 'throw': 'Throws', + 'throws': 'Throws' + }.items(): + s = re.sub(r'\\%s\s*' % in_, r'\n\n$%s:\n\n' % out_, s) + + s = re.sub(r'\\details\s*', r'\n\n', s) + s = re.sub(r'\\brief\s*', r'', s) + s = re.sub(r'\\short\s*', r'', s) + s = re.sub(r'\\ref\s*', r'', s) + + s = re.sub(r'\\code\s?(.*?)\s?\\endcode', + r"```\n\1\n```\n", s, flags=re.DOTALL) + + # HTML/TeX tags + s = re.sub(r'(.*?)', r'``\1``', s, flags=re.DOTALL) + s = re.sub(r'
(.*?)
', r"```\n\1\n```\n", s, flags=re.DOTALL) + s = re.sub(r'(.*?)', r'*\1*', s, flags=re.DOTALL) + s = re.sub(r'(.*?)', r'**\1**', s, flags=re.DOTALL) + s = re.sub(r'\\f\$(.*?)\\f\$', r'$\1$', s, flags=re.DOTALL) + s = re.sub(r'
  • ', r'\n\n* ', s) + s = re.sub(r'', r'', s) + s = re.sub(r'
  • ', r'\n\n', s) + + s = s.replace('``true``', '``True``') + s = s.replace('``false``', '``False``') + + # Re-flow text + wrapper = textwrap.TextWrapper() + wrapper.expand_tabs = True + wrapper.replace_whitespace = True + wrapper.drop_whitespace = True + wrapper.width = 70 + wrapper.initial_indent = wrapper.subsequent_indent = '' + + result = '' + in_code_segment = False + for x in re.split(r'(```)', s): + if x == '```': + if not in_code_segment: + result += '```\n' + else: + result += '\n```\n\n' + in_code_segment = not in_code_segment + elif in_code_segment: + result += x.strip() + else: + for y in re.split(r'(?: *\n *){2,}', x): + wrapped = wrapper.fill(re.sub(r'\s+', ' ', y).strip()) + if len(wrapped) > 0 and wrapped[0] == '$': + result += wrapped[1:] + '\n' + wrapper.initial_indent = \ + wrapper.subsequent_indent = ' ' * 4 + else: + if len(wrapped) > 0: + result += wrapped + '\n\n' + wrapper.initial_indent = wrapper.subsequent_indent = '' + return result.rstrip().lstrip('\n') + + +def extract(filename, node, prefix): + if not (node.location.file is None or + os.path.samefile(d(node.location.file.name), filename)): + return 0 + if node.kind in RECURSE_LIST: + sub_prefix = prefix + if node.kind != CursorKind.TRANSLATION_UNIT: + if len(sub_prefix) > 0: + sub_prefix += '_' + sub_prefix += d(node.spelling) + for i in node.get_children(): + extract(filename, i, sub_prefix) + if node.kind in PRINT_LIST: + comment = d(node.raw_comment) if node.raw_comment is not None else '' + comment = process_comment(comment) + sub_prefix = prefix + if len(sub_prefix) > 0: + sub_prefix += '_' + if len(node.spelling) > 0: + name = sanitize_name(sub_prefix + d(node.spelling)) + global output + output.append((name, filename, comment)) + + +class ExtractionThread(Thread): + def __init__(self, filename, parameters): + Thread.__init__(self) + self.filename = filename + self.parameters = parameters + job_semaphore.acquire() + + def run(self): + print('Processing "%s" ..' % self.filename, file=sys.stderr) + try: + index = cindex.Index( + cindex.conf.lib.clang_createIndex(False, True)) + tu = index.parse(self.filename, self.parameters) + extract(self.filename, tu.cursor, '') + finally: + job_semaphore.release() + +if __name__ == '__main__': + parameters = ['-x', 'c++', '-std=c++11'] + filenames = [] + + if platform.system() == 'Darwin': + dev_path = '/Applications/Xcode.app/Contents/Developer/' + lib_dir = dev_path + 'Toolchains/XcodeDefault.xctoolchain/usr/lib/' + sdk_dir = dev_path + 'Platforms/MacOSX.platform/Developer/SDKs' + libclang = lib_dir + 'libclang.dylib' + + if os.path.exists(libclang): + cindex.Config.set_library_path(os.path.dirname(libclang)) + + if os.path.exists(sdk_dir): + sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0]) + parameters.append('-isysroot') + parameters.append(sysroot_dir) + + for item in sys.argv[1:]: + if item.startswith('-'): + parameters.append(item) + else: + filenames.append(item) + + if len(filenames) == 0: + print('Syntax: %s [.. a list of header files ..]' % sys.argv[0]) + exit(-1) + + print('''/* + This file contains docstrings for the Python bindings. + Do not edit! These were automatically extracted by mkdoc.py + */ + +#define __EXPAND(x) x +#define __COUNT(_1, _2, _3, _4, _5, _6, _7, COUNT, ...) COUNT +#define __VA_SIZE(...) __EXPAND(__COUNT(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1)) +#define __CAT1(a, b) a ## b +#define __CAT2(a, b) __CAT1(a, b) +#define __DOC1(n1) __doc_##n1 +#define __DOC2(n1, n2) __doc_##n1##_##n2 +#define __DOC3(n1, n2, n3) __doc_##n1##_##n2##_##n3 +#define __DOC4(n1, n2, n3, n4) __doc_##n1##_##n2##_##n3##_##n4 +#define __DOC5(n1, n2, n3, n4, n5) __doc_##n1##_##n2##_##n3##_##n4##_##n5 +#define __DOC6(n1, n2, n3, n4, n5, n6) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6 +#define __DOC7(n1, n2, n3, n4, n5, n6, n7) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6##_##n7 +#define DOC(...) __EXPAND(__EXPAND(__CAT2(__DOC, __VA_SIZE(__VA_ARGS__)))(__VA_ARGS__)) + +#if defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif +''') + + output.clear() + for filename in filenames: + thr = ExtractionThread(filename, parameters) + thr.start() + + print('Waiting for jobs to finish ..', file=sys.stderr) + for i in range(job_count): + job_semaphore.acquire() + + name_ctr = 1 + name_prev = None + for name, _, comment in list(sorted(output, key=lambda x: (x[0], x[1]))): + if name == name_prev: + name_ctr += 1 + name = name + "_%i" % name_ctr + else: + name_prev = name + name_ctr = 1 + print('\nstatic const char *%s =%sR"doc(%s)doc";' % + (name, '\n' if '\n' in comment else ' ', comment)) + + print(''' +#if defined(__GNUG__) +#pragma GCC diagnostic pop +#endif +''') diff --git a/external/pybind11/tools/pybind11Config.cmake.in b/external/pybind11/tools/pybind11Config.cmake.in new file mode 100644 index 0000000..3dd1b2c --- /dev/null +++ b/external/pybind11/tools/pybind11Config.cmake.in @@ -0,0 +1,100 @@ +# pybind11Config.cmake +# -------------------- +# +# PYBIND11 cmake module. +# This module sets the following variables in your project:: +# +# pybind11_FOUND - true if pybind11 and all required components found on the system +# pybind11_VERSION - pybind11 version in format Major.Minor.Release +# pybind11_INCLUDE_DIRS - Directories where pybind11 and python headers are located. +# pybind11_INCLUDE_DIR - Directory where pybind11 headers are located. +# pybind11_DEFINITIONS - Definitions necessary to use pybind11, namely USING_pybind11. +# pybind11_LIBRARIES - compile flags and python libraries (as needed) to link against. +# pybind11_LIBRARY - empty. +# CMAKE_MODULE_PATH - appends location of accompanying FindPythonLibsNew.cmake and +# pybind11Tools.cmake modules. +# +# +# Available components: None +# +# +# Exported targets:: +# +# If pybind11 is found, this module defines the following :prop_tgt:`IMPORTED` +# interface library targets:: +# +# pybind11::module - for extension modules +# pybind11::embed - for embedding the Python interpreter +# +# Python headers, libraries (as needed by platform), and the C++ standard +# are attached to the target. Set PythonLibsNew variables to influence +# python detection and PYBIND11_CPP_STANDARD (-std=c++11 or -std=c++14) to +# influence standard setting. :: +# +# find_package(pybind11 CONFIG REQUIRED) +# message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") +# +# # Create an extension module +# add_library(mylib MODULE main.cpp) +# target_link_libraries(mylib pybind11::module) +# +# # Or embed the Python interpreter into an executable +# add_executable(myexe main.cpp) +# target_link_libraries(myexe pybind11::embed) +# +# Suggested usage:: +# +# find_package with version info is not recommended except for release versions. :: +# +# find_package(pybind11 CONFIG) +# find_package(pybind11 2.0 EXACT CONFIG REQUIRED) +# +# +# The following variables can be set to guide the search for this package:: +# +# pybind11_DIR - CMake variable, set to directory containing this Config file +# CMAKE_PREFIX_PATH - CMake variable, set to root directory of this package +# PATH - environment variable, set to bin directory of this package +# CMAKE_DISABLE_FIND_PACKAGE_pybind11 - CMake variable, disables +# find_package(pybind11) when not REQUIRED, perhaps to force internal build + +@PACKAGE_INIT@ + +set(PN pybind11) + +# location of pybind11/pybind11.h +set(${PN}_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") + +set(${PN}_LIBRARY "") +set(${PN}_DEFINITIONS USING_${PN}) + +check_required_components(${PN}) + +# make detectable the FindPythonLibsNew.cmake module +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +include(pybind11Tools) + +if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) +#----------------------------------------------------------------------------- +# Don't include targets if this file is being picked up by another +# project which has already built this as a subproject +#----------------------------------------------------------------------------- +if(NOT TARGET ${PN}::pybind11) + include("${CMAKE_CURRENT_LIST_DIR}/${PN}Targets.cmake") + + find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} MODULE REQUIRED) + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS}) + set_property(TARGET ${PN}::embed APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) + if(WIN32 OR CYGWIN) + set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) + endif() + + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") + + get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES) + set(${PN}_INCLUDE_DIRS ${_iid}) + set(${PN}_LIBRARIES ${_ico} ${_ill}) +endif() +endif() diff --git a/external/pybind11/tools/pybind11Tools.cmake b/external/pybind11/tools/pybind11Tools.cmake new file mode 100644 index 0000000..a7c471a --- /dev/null +++ b/external/pybind11/tools/pybind11Tools.cmake @@ -0,0 +1,202 @@ +# tools/pybind11Tools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +# Add a CMake parameter for choosing a desired Python version +if(NOT PYBIND11_PYTHON_VERSION) + set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") +endif() + +set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4) +find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) + +include(CheckCXXCompilerFlag) +include(CMakeParseArguments) + +if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) + if(NOT MSVC) + check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) + + if (HAS_CPP14_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++14) + else() + check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) + if (HAS_CPP11_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++11) + else() + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() + endif() + elseif(MSVC) + set(PYBIND11_CPP_STANDARD /std:c++14) + endif() + + set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING + "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) +endif() + +# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and +# linkerflags are lists of flags to use. The result variable is a unique variable name for each set +# of flags: the compilation result will be cached base on the result variable. If the flags work, +# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). +function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) + set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) + check_cxx_compiler_flag("${cxxflags}" ${result}) + if (${result}) + set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) + set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) + endif() +endfunction() + +# Internal: find the appropriate link time optimization flags for this compiler +function(_pybind11_add_lto_flags target_name prefer_thin_lto) + if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) + set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") + set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(cxx_append "") + set(linker_append "") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it + set(linker_append ";$<$:-O3>") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(cxx_append ";-fno-fat-lto-objects") + endif() + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN + "-flto=thin${cxx_append}" "-flto=thin${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (NOT HAS_FLTO_THIN) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO + "-flto${cxx_append}" "-flto${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Intel equivalent to LTO is called IPO + _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO + "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + elseif(MSVC) + # cmake only interprets libraries as linker flags when they start with a - (otherwise it + # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags + # with - instead of /, even if it is a bit non-standard: + _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG + "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (PYBIND11_LTO_CXX_FLAGS) + message(STATUS "LTO enabled") + else() + message(STATUS "LTO disabled (not supported by the compiler and/or linker)") + endif() + endif() + + # Enable LTO flags if found, except for Debug builds + if (PYBIND11_LTO_CXX_FLAGS) + target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") + endif() + if (PYBIND11_LTO_LINKER_FLAGS) + target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") + endif() +endfunction() + +# Build a Python extension module: +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] +# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) +# +function(pybind11_add_module target_name) + set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS THIN_LTO) + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + if(ARG_MODULE AND ARG_SHARED) + message(FATAL_ERROR "Can't be both MODULE and SHARED") + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() + + if(ARG_EXCLUDE_FROM_ALL) + set(exclude_from_all EXCLUDE_FROM_ALL) + endif() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) + + target_include_directories(${target_name} + PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt + PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config + PRIVATE ${PYTHON_INCLUDE_DIRS}) + + # The prefix and extension are provided by FindPythonLibsNew.cmake + set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + + if(WIN32 OR CYGWIN) + # Link against the Python shared library on Windows + target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) + elseif(APPLE) + # It's quite common to have multiple copies of the same Python version + # installed on one's system. E.g.: one copy from the OS and another copy + # that's statically linked into an application like Blender or Maya. + # If we link our plugin library against the OS Python here and import it + # into Blender or Maya later on, this will cause segfaults when multiple + # conflicting Python instances are active at the same time (even when they + # are of the same version). + + # Windows is not affected by this issue since it handles DLL imports + # differently. The solution for Linux and Mac OS is simple: we just don't + # link against the Python library. The resulting shared library will have + # missing symbols, but that's perfectly fine -- they will be resolved at + # import time. + + target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") + + if(ARG_SHARED) + # Suppress CMake >= 3.0 warning for shared libraries + set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) + endif() + endif() + + # Make sure C++11/14 are enabled + target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + + if(ARG_NO_EXTRAS) + return() + endif() + + _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) + + if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug) + # Strip unnecessary sections of the binary on Linux/Mac OS + if(CMAKE_STRIP) + if(APPLE) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} -x $) + else() + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} $) + endif() + endif() + endif() + + if(MSVC) + # /MP enables multithreaded builds (relevant when there are many files), /bigobj is + # needed for bigger binding projects due to the limit to 64k addressable sections + target_compile_options(${target_name} PRIVATE /MP /bigobj) + endif() +endfunction() diff --git a/include/pangolin/compat/glconsole.h b/include/pangolin/compat/glconsole.h new file mode 100644 index 0000000..8440236 --- /dev/null +++ b/include/pangolin/compat/glconsole.h @@ -0,0 +1,76 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +// If we don't have GLUT, coerce GLConsole to work with our own font rendering. +#ifdef HAVE_GLES +#include +#include + +// Define our own GLFont class, preventing GLConsoles from being included. +#define __GL_FONT_H__ + +class GLFont +{ +public: + void glPrintf(int x, int y, const char *fmt, ...) + { + glPushMatrix(); + glTranslatef(x,y,1.0f); + pangolin::GlFont::I().Text(fmt).Draw(); + glPopMatrix(); + } + void glPrintf(int x, int y, const std::string fmt, ...){ glPrintf(x,y, fmt.c_str()); } + void glPrintfFast(int x, int y, const char *fmt, ...) { glPrintf(x,y,fmt); } + void glPrintfFast(int x, int y, const std::string fmt, ...){ glPrintfFast(x,y, fmt.c_str()); } + unsigned int CharHeight() { return 10; } + unsigned int CharWidth() { return 10; } +}; + +// TODO: We should implement these or something... +#define glPushAttrib(x) +#define glPopAttrib(x) +#define glutGetModifiers(x) (0) + +#define GLUT_ACTIVE_SHIFT 0 +#define GLUT_ACTIVE_CTRL 1 +#define GLUT_ACTIVE_ALT 2 +#define GLUT_KEY_UP 3 +#define GLUT_KEY_DOWN 4 +#define GLUT_KEY_LEFT 5 +#define GLUT_KEY_RIGHT 6 +#define GLUT_KEY_PAGE_UP 7 +#define GLUT_KEY_PAGE_DOWN 8 +#define GLUT_KEY_HOME 9 +#define GLUT_KEY_END 10 + +#endif // HAVE_GLES + +#include diff --git a/include/pangolin/compat/glutbitmap.h b/include/pangolin/compat/glutbitmap.h new file mode 100644 index 0000000..bf56635 --- /dev/null +++ b/include/pangolin/compat/glutbitmap.h @@ -0,0 +1,96 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#ifndef HAVE_GLUT +#include + +#ifdef HAVE_GLES +GLfloat g_raster_pos[4]; + +inline void glRasterPos3f(GLfloat x, GLfloat y, GLfloat z) +{ + // find object point (x,y,z)' in pixel coords + GLdouble projection[16]; + GLdouble modelview[16]; + GLint view[4]; + +#ifdef HAVE_GLES_2 + std::copy(pangolin::glEngine().projection.top().m, pangolin::glEngine().projection.top().m+16, projection); + std::copy(pangolin::glEngine().modelview.top().m, pangolin::glEngine().modelview.top().m+16, modelview); +#else + glGetDoublev(GL_PROJECTION_MATRIX, projection ); + glGetDoublev(GL_MODELVIEW_MATRIX, modelview ); +#endif + glGetIntegerv(GL_VIEWPORT, view ); + + pangolin::glProject(x, y, z, modelview, projection, view, + g_raster_pos, g_raster_pos + 1, g_raster_pos + 2); +} + +inline void glRasterPos2f(GLfloat x, GLfloat y) +{ + glRasterPos3f(x,y,1.0f); +} + +inline void glRasterPos2i(GLint x, GLint y) +{ + glRasterPos3f((GLfloat)x, (GLfloat)y, 1.0f ); +} + +inline void glRasterPos3fv(const GLfloat *v){ + glRasterPos3f(v[0],v[1],v[2]); +} + +inline void glRasterPos2fv(const GLfloat *v){ + glRasterPos3f(v[0],v[1],1.0f); +} +#endif // HAVE_GLES + +inline void glutBitmapString(void * /*font*/, const unsigned char *str) +{ +#ifndef HAVE_GLES + float g_raster_pos[4]; + glGetFloatv(GL_CURRENT_RASTER_POSITION, g_raster_pos); +#endif + + pangolin::GlFont::I().Text( (const char *)str ).DrawWindow( + g_raster_pos[0], g_raster_pos[1], g_raster_pos[2] + ); +} + +inline int glutBitmapLength(void * /*font*/, const unsigned char *str) +{ + return (int)(pangolin::GlFont::I().Text((const char *)str).Width()); +} + +#define GLUT_BITMAP_HELVETICA_12 0; + +#endif // HAVE_GLUT diff --git a/include/pangolin/compat/ovr.h b/include/pangolin/compat/ovr.h new file mode 100644 index 0000000..c42dbe5 --- /dev/null +++ b/include/pangolin/compat/ovr.h @@ -0,0 +1,37 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#ifndef _MSVC_ +// Suppress warnings in OVR library headers +#pragma GCC system_header +#endif + +#include diff --git a/include/pangolin/compat/type_traits.h b/include/pangolin/compat/type_traits.h new file mode 100644 index 0000000..30bec5b --- /dev/null +++ b/include/pangolin/compat/type_traits.h @@ -0,0 +1,49 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include + +#include + +// enable_if From Boost +namespace pangolin +{ + template + struct enable_if_c { + typedef T type; + }; + + template + struct enable_if_c {}; + + template + struct enable_if : public enable_if_c {}; +} diff --git a/include/pangolin/config.h b/include/pangolin/config.h new file mode 100644 index 0000000..5e3e467 --- /dev/null +++ b/include/pangolin/config.h @@ -0,0 +1,74 @@ +#ifndef PANGOLIN_CONFIG_H +#define PANGOLIN_CONFIG_H + +/* + * Configuration Header for Pangolin + */ + +/// Version +#define PANGOLIN_VERSION_MAJOR 0 +#define PANGOLIN_VERSION_MINOR 5 +#define PANGOLIN_VERSION_STRING "0.5" + +/// Pangolin options +#define BUILD_PANGOLIN_GUI +#define BUILD_PANGOLIN_VARS +#define BUILD_PANGOLIN_VIDEO + +/// Configured libraries +/* #undef HAVE_CUDA */ +#define HAVE_PYTHON + +#define HAVE_EIGEN +/* #undef HAVE_TOON */ + +#define HAVE_DC1394 +#define HAVE_V4L +#define HAVE_OPENNI +/* #undef HAVE_LIBREALSENSE */ +#define HAVE_OPENNI2 +/* #undef HAVE_UVC */ +/* #undef HAVE_DEPTHSENSE */ +/* #undef HAVE_TELICAM */ +/* #undef HAVE_PLEORA */ + +#define HAVE_FFMPEG +/* #undef HAVE_FFMPEG_MAX_ANALYZE_DURATION2 */ +#define HAVE_FFMPEG_AVFORMAT_ALLOC_OUTPUT_CONTEXT2 +#define HAVE_FFMPEG_AVPIXELFORMAT + +#define HAVE_GLEW +/* #undef GLEW_STATIC */ + +/* #undef HAVE_GLUT */ +/* #undef HAVE_FREEGLUT */ +/* #undef HAVE_APPLE_OPENGL_FRAMEWORK */ +/* #undef HAVE_MODIFIED_OSXGLUT */ +/* #undef HAVE_GLES */ +/* #undef HAVE_GLES_2 */ +/* #undef HAVE_OCULUS */ + +#define HAVE_PNG +#define HAVE_JPEG +#define HAVE_TIFF +#define HAVE_OPENEXR +/* #undef HAVE_ZSTD */ + +/// Platform +#define _UNIX_ +/* #undef _WIN_ */ +/* #undef _OSX_ */ +#define _LINUX_ +/* #undef _ANDROID_ */ +/* #undef _IOS_ */ + +/// Compiler +#define _GCC_ +/* #undef _CLANG_ */ +/* #undef _MSVC_ */ + +#if (__cplusplus > 199711L) || (_MSC_VER >= 1800) +#define CALLEE_HAS_VARIADIC_TEMPLATES +#endif + +#endif //PANGOLIN_CONFIG_H diff --git a/include/pangolin/console/ConsoleInterpreter.h b/include/pangolin/console/ConsoleInterpreter.h new file mode 100644 index 0000000..31d1e10 --- /dev/null +++ b/include/pangolin/console/ConsoleInterpreter.h @@ -0,0 +1,80 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +enum ConsoleLineType +{ + ConsoleLineTypeCmd, + ConsoleLineTypeCmdOptions, + ConsoleLineTypeStdout, + ConsoleLineTypeStderr, + ConsoleLineTypeOutput, + ConsoleLineTypeHelp, +}; + +class ConsoleLine +{ +public: + inline ConsoleLine() + : linetype(ConsoleLineTypeCmd) + { + } + + inline ConsoleLine(std::string text, ConsoleLineType linetype = ConsoleLineTypeOutput) + : text(text), linetype(linetype) + { + } + + std::string text; + ConsoleLineType linetype; +}; + +class ConsoleInterpreter +{ +public: + inline virtual ~ConsoleInterpreter() + { + } + + virtual void PushCommand(const std::string& cmd) = 0; + + virtual bool PullLine(ConsoleLine& line) = 0; + + virtual std::vector Complete( + const std::string& cmd, int max_options = 20 + ) = 0; + +}; + +} diff --git a/include/pangolin/console/ConsoleView.h b/include/pangolin/console/ConsoleView.h new file mode 100644 index 0000000..b21a0eb --- /dev/null +++ b/include/pangolin/console/ConsoleView.h @@ -0,0 +1,109 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace pangolin +{ + +class ConsoleView : public pangolin::View, pangolin::Handler +{ +public: + struct Line + { + Line() + { + } + + Line(const GlText& text, ConsoleLineType linetype = ConsoleLineTypeCmd ) + : text(text), linetype(linetype) + { + } + + GlText text; + ConsoleLineType linetype; + }; + + + // Construct with interpreter (and take ownership) + ConsoleView(ConsoleInterpreter* interpreter); + + ~ConsoleView(); + + View& ShowWithoutAnimation(bool show=true); + + // Replace implementation in View to account for hiding animation + View& Show(bool show=true); + + // Replace implementation in View to account for hiding animation + void ToggleShow(); + + // Replace implementation in View to account for hiding animation + bool IsShown() const; + + void Render() override; + + void Keyboard(View&, unsigned char key, int x, int y, bool pressed) override; + +private: + void DrawLine(const ConsoleView::Line& l); + + void ProcessOutputLines(); + + void AddLine(const std::string& text, ConsoleLineType linetype = ConsoleLineTypeCmd); + + Line* GetLine(int id, ConsoleLineType line_type, const std::string& prefix = ""); + + ConsoleInterpreter* interpreter; + + GlFont& font; + + std::map prompts; + + Line current_line; + std::deque line_buffer; + + bool hiding; + GLfloat bottom; + + Colour background_colour; + std::map line_colours; + float animation_speed; +}; + +} diff --git a/include/pangolin/display/attach.h b/include/pangolin/display/attach.h new file mode 100644 index 0000000..871c61e --- /dev/null +++ b/include/pangolin/display/attach.h @@ -0,0 +1,87 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace pangolin +{ + +/// Units for measuring screen distances. +enum Unit { + Fraction, + Pixel, + ReversePixel +}; + +/// Defines absolute or relative position from parent viewport edge. +/// Constructors distinguised by whole pixels, or floating +/// fraction in interval [0,1] +struct PANGOLIN_EXPORT Attach { + /// Attach to Left/Bottom edge + Attach() : unit(Fraction), p(0) {} + + /// General constructor + Attach(Unit unit, GLfloat p) : unit(unit), p(p) {} + + /// Specify relative position in range [0,1]. + /// 0 represents leftmost / bottom-most edge, + /// 1 represents rightmost / topmost edge + Attach(GLfloat p) : unit(Fraction), p(p) { + // Allow for numerical imprecision when checking usage. + if( p < -1E-3 || 1.001 < p ) { + std::cerr << "Pangolin API Change: Display::SetBounds must be used with Attach::Pix or Attach::ReversePix to specify pixel bounds relative to an edge. See the code samples for details." << std::endl; + throw std::exception(); + } + } + + /// Specify absolute position from leftmost / bottom-most edge. + static Attach Pix(int p) { + return Attach(p >=0 ? Pixel : ReversePixel, std::abs((float)p)); + } + + /// Specify absolute position from rightmost / topmost edge. + static Attach ReversePix(int p) { + return Attach(ReversePixel, (GLfloat)p); + } + + /// Specify relative position in range [0,1]. + /// 0 represents leftmost / bottom-most edge, + /// 1 represents rightmost / topmost edge + static Attach Frac(float frac) { + return Attach(frac); + } + + Unit unit; + GLfloat p; +}; + +} // namespace pangolin diff --git a/include/pangolin/display/device/OsxWindow.h b/include/pangolin/display/device/OsxWindow.h new file mode 100644 index 0000000..01c14db --- /dev/null +++ b/include/pangolin/display/device/OsxWindow.h @@ -0,0 +1,66 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin +{ + +struct OsxWindow : public PangolinGl +{ + OsxWindow(const std::string& title, int width, int height, bool USE_RETINA); + + ~OsxWindow(); + + void StartFullScreen(); + + void StopFullScreen(); + + void ToggleFullscreen() override; + + void Move(int x, int y) override; + + void Resize(unsigned int w, unsigned int h) override; + + void MakeCurrent() override; + + void SwapBuffers() override; + + void ProcessEvents() override; + +private: + NSWindow* _window; + PangolinNSGLView *view; +}; + +} diff --git a/include/pangolin/display/device/PangolinNSApplication.h b/include/pangolin/display/device/PangolinNSApplication.h new file mode 100644 index 0000000..4b69e05 --- /dev/null +++ b/include/pangolin/display/device/PangolinNSApplication.h @@ -0,0 +1,59 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#import +#import + +//////////////////////////////////////////////////////////////////// +// PangolinNSApplication +//////////////////////////////////////////////////////////////////// + +@interface PangolinNSApplication : NSApplication { +} + +- (void)run_pre; +- (void)run_step; + +@end + +//////////////////////////////////////////////////////////////////// +// PangolinWindowDelegate +//////////////////////////////////////////////////////////////////// + +@interface PangolinWindowDelegate : NSObject + +@end + +//////////////////////////////////////////////////////////////////// +// PangolinAppDelegate +//////////////////////////////////////////////////////////////////// + +@interface PangolinAppDelegate : NSObject + +@end diff --git a/include/pangolin/display/device/PangolinNSGLView.h b/include/pangolin/display/device/PangolinNSGLView.h new file mode 100644 index 0000000..5b8cef5 --- /dev/null +++ b/include/pangolin/display/device/PangolinNSGLView.h @@ -0,0 +1,45 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#import +#import + +#include + +//////////////////////////////////////////////////////////////////// +// PangolinNSGLView +//////////////////////////////////////////////////////////////////// + +@interface PangolinNSGLView : NSOpenGLView +{ + pangolin::PangolinGl* context; + float backing_scale; +} +@end + diff --git a/include/pangolin/display/device/WinWindow.h b/include/pangolin/display/device/WinWindow.h new file mode 100644 index 0000000..f99206a --- /dev/null +++ b/include/pangolin/display/device/WinWindow.h @@ -0,0 +1,87 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace pangolin +{ + +struct WinWindow : public PangolinGl +{ + WinWindow( + const std::string& title, int width, int height + ); + + ~WinWindow(); + + void StartFullScreen(); + + void StopFullScreen(); + + void ToggleFullscreen() override; + + void Move(int x, int y) override; + + void Resize(unsigned int w, unsigned int h) override; + + void MakeCurrent() override; + + void SwapBuffers() override; + + void ProcessEvents() override; + + HGLRC GetGLRenderContext() + { + return hGLRC; + } + +private: + static LRESULT APIENTRY WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + + LRESULT HandleWinMessages(UINT message, WPARAM wParam, LPARAM lParam); + + void RegisterThisClass(HMODULE hCurrentInst); + + void SetupPixelFormat(HDC hdc); + + void SetupPalette(HDC hDC); + + // Owns the Window + HWND hWnd; + HDC hDC; + HGLRC hGLRC; + HPALETTE hPalette; +}; + +} diff --git a/include/pangolin/display/device/X11GlContext.h b/include/pangolin/display/device/X11GlContext.h new file mode 100644 index 0000000..e69de29 diff --git a/include/pangolin/display/device/X11Window.h b/include/pangolin/display/device/X11Window.h new file mode 100644 index 0000000..499acd9 --- /dev/null +++ b/include/pangolin/display/device/X11Window.h @@ -0,0 +1,107 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace pangolin +{ + +struct X11Display +{ + X11Display(const char* name = 0) { + XInitThreads(); + display = XOpenDisplay(name); + if (!display) { + throw std::runtime_error("Pangolin X11: Failed to open X display"); + } + } + + ~X11Display() { + XCloseDisplay(display); + } + + // Owns the display + ::Display* display; +}; + +struct X11GlContext : public GlContextInterface +{ + X11GlContext(std::shared_ptr &d, ::GLXFBConfig chosenFbc, std::shared_ptr shared_context = std::shared_ptr() ); + ~X11GlContext(); + + std::shared_ptr display; + + std::shared_ptr shared_context; + + // Owns the OpenGl Context + ::GLXContext glcontext; +}; + +struct X11Window : public PangolinGl +{ + X11Window( + const std::string& title, int width, int height, + std::shared_ptr& display, ::GLXFBConfig chosenFbc + ); + + ~X11Window(); + + void ToggleFullscreen() override; + + void Move(int x, int y) override; + + void Resize(unsigned int w, unsigned int h) override; + + void MakeCurrent(GLXContext ctx); + + void MakeCurrent() override; + + void SwapBuffers() override; + + void ProcessEvents() override; + + // References the X11 display and context. + std::shared_ptr display; + std::shared_ptr glcontext; + + // Owns the X11 Window and Colourmap + ::Window win; + ::Colormap cmap; + + Atom delete_message; +}; + +} diff --git a/include/pangolin/display/device/display_android.h b/include/pangolin/display/device/display_android.h new file mode 100644 index 0000000..b98a9b8 --- /dev/null +++ b/include/pangolin/display/device/display_android.h @@ -0,0 +1,333 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "pango", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "pango", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "pango", __VA_ARGS__)) + +/* For debug builds, always enable the debug traces in this library */ +#undef NDEBUG +#ifndef NDEBUG +# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "pango", __VA_ARGS__)) +#else +# define LOGV(...) ((void)0) +#endif + +template inline +void Log(T v) { + const std::string sv = pangolin::Convert::Do(v); + LOGI(sv.c_str()); +} + +template inline +void Log(const std::string& str, T v) +{ + const std::string sv = pangolin::Convert::Do(v); + LOGI((str + ":" + sv).c_str()); +} + +namespace pangolin +{ + void CreateAndroidWindowAndBind(std::string name); + void ProcessAndroidEvents(); + void FinishAndroidFrame(); +} + + +#ifdef __cplusplus +extern "C" { +#endif + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; + + const char* application_so; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +#ifdef __cplusplus +} +#endif diff --git a/include/pangolin/display/device/display_glut.h b/include/pangolin/display/device/display_glut.h new file mode 100644 index 0000000..43bfde7 --- /dev/null +++ b/include/pangolin/display/device/display_glut.h @@ -0,0 +1,75 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +#ifndef GLUT_DOUBLE +// Avoid pulling in all of glut just for these defines. +#define GLUT_RGB 0x0000 +#define GLUT_RGBA 0x0000 +#define GLUT_INDEX 0x0001 +#define GLUT_SINGLE 0x0000 +#define GLUT_DOUBLE 0x0002 +#define GLUT_ACCUM 0x0004 +#define GLUT_ALPHA 0x0008 +#define GLUT_DEPTH 0x0010 +#define GLUT_STENCIL 0x0020 +#define GLUT_MULTISAMPLE 0x0080 +#define GLUT_STEREO 0x0100 +#define GLUT_LUMINANCE 0x0200 +#endif + +namespace pangolin +{ + +/// Create GLUT window and bind Pangolin to it. +/// All GLUT initialisation is taken care of. This prevents you +/// from needing to call BindToContext() and TakeGlutCallbacks(). +PANGOLIN_EXPORT +void CreateGlutWindowAndBind(std::string window_title, int w = 640, int h = 480, unsigned int mode = GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH ); + +/// Applies any post-render events if they are defined, +/// swaps buffers and processes events. Also resets viewport to +/// entire window and disables scissoring. +PANGOLIN_EXPORT +void FinishGlutFrame(); + +/// Swaps OpenGL Buffers and processes input events. +PANGOLIN_EXPORT +void SwapGlutBuffersProcessGlutEvents(); + +/// Allow Pangolin to take GLUT callbacks for its own uses. +/// Not needed if you instantiated a window through CreateWindowAndBind(). +PANGOLIN_EXPORT +void TakeGlutCallbacks(); + +} diff --git a/include/pangolin/display/display.h b/include/pangolin/display/display.h new file mode 100644 index 0000000..57a15f4 --- /dev/null +++ b/include/pangolin/display/display.h @@ -0,0 +1,219 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove, Richard Newcombe + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +/*! \file display.h + * This file contains a number of global methods for creating and + * querying window state as well as handling user input. + */ + +namespace pangolin +{ + + // CreateWindowAndBind parameter key names. + // X11 Window options: + extern const char* PARAM_DISPLAYNAME; // std::string + extern const char* PARAM_DOUBLEBUFFER; // bool + extern const char* PARAM_SAMPLE_BUFFERS; // int + extern const char* PARAM_SAMPLES; // int + extern const char* PARAM_HIGHRES; // bool - Apple Retina screens only + + // Forward Declarations + struct View; + struct Viewport; + class UserApp; + + /// Give this OpenGL context a name or switch contexts. + /// This is required to initialise Pangolin for use with an + /// externally defined OpenGL context. You needn't call it + /// if you have used CreateWindowAndBind() to create a window + /// or launched a pangolin::UserApp + PANGOLIN_EXPORT + WindowInterface& BindToContext(std::string name); + + /// Initialise OpenGL window (determined by platform) and bind context. + /// This method will choose an available windowing system if one is present. + PANGOLIN_EXPORT + WindowInterface& CreateWindowAndBind(std::string window_title, int w = 640, int h = 480, const Params& params = Params()); + + /// Return pointer to current Pangolin Window context, or nullptr if none bound. + PANGOLIN_EXPORT + WindowInterface* GetBoundWindow(); + + PANGOLIN_EXPORT + void DestroyWindow(const std::string& window_title); + + /// Launch users derived UserApp, controlling OpenGL event loop. + /// This method will block until the application exits, calling app's + /// Init() method to start and Render() method subsequently to draw each frame. + /// @return exit code for use when returning from main. Currently always 0. + PANGOLIN_EXPORT + int LaunchUserApp(UserApp& app); + + /// Perform any post rendering, event processing and frame swapping. + PANGOLIN_EXPORT + void FinishFrame(); + + /// Request that the window close. + PANGOLIN_EXPORT + void Quit(); + + /// Request that all windows close. + PANGOLIN_EXPORT + void QuitAll(); + + /// Returns true if user has requested to close OpenGL window. + PANGOLIN_EXPORT + bool ShouldQuit(); + + /// Returns true if user has interacted with the window since this was last called. + PANGOLIN_EXPORT + bool HadInput(); + + /// Returns true if user has resized the window. + PANGOLIN_EXPORT + bool HasResized(); + + /// Renders any views with default draw methods. + PANGOLIN_EXPORT + void RenderViews(); + + /// Perform any post render events, such as screen recording. + PANGOLIN_EXPORT + void PostRender(); + + /// Request to be notified via functor when key is pressed. + /// Functor may take one parameter which will equal the key pressed + PANGOLIN_EXPORT + void RegisterKeyPressCallback(int key, std::function func); + + /// Save window contents to image. + PANGOLIN_EXPORT + void SaveWindowOnRender(std::string filename_prefix); + + PANGOLIN_EXPORT + void SaveFramebuffer(std::string prefix, const Viewport& v); + + namespace process + { + /// Tell pangolin to process input to drive display. + /// You will need to call this manually if you haven't let + /// Pangolin register callbacks from your windowing system + PANGOLIN_EXPORT + void Keyboard( unsigned char key, int x, int y); + + PANGOLIN_EXPORT + void KeyboardUp(unsigned char key, int x, int y); + + PANGOLIN_EXPORT + void SpecialFunc(int key, int x, int y); + + PANGOLIN_EXPORT + void SpecialFuncUp(int key, int x, int y); + + /// Tell pangolin base window size has changed + /// You will need to call this manually if you haven't let + /// Pangolin register callbacks from your windowing system + PANGOLIN_EXPORT + void Resize(int width, int height); + + /// Event based rendering entry point (from e.g. + /// glutMainLoop). Not currently supported. + PANGOLIN_EXPORT + void Display(); + + PANGOLIN_EXPORT + void Mouse( int button, int state, int x, int y); + + PANGOLIN_EXPORT + void MouseMotion( int x, int y); + + PANGOLIN_EXPORT + void PassiveMouseMotion(int x, int y); + + PANGOLIN_EXPORT + void Scroll(float x, float y); + + PANGOLIN_EXPORT + void Zoom(float m); + + PANGOLIN_EXPORT + void Rotate(float r); + + PANGOLIN_EXPORT + void SubpixMotion(float x, float y, float pressure, float rotation, float tiltx, float tilty); + + PANGOLIN_EXPORT + void SpecialInput(InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4); + + } + + /// Retrieve 'base' display, corresponding to entire window. + PANGOLIN_EXPORT + View& DisplayBase(); + + /// Create or retrieve named display managed by pangolin (automatically deleted). + PANGOLIN_EXPORT + View& Display(const std::string& name); + + /// Create unnamed display managed by pangolin (automatically deleted). + PANGOLIN_EXPORT + View& CreateDisplay(); + + /// Switch between windowed and fullscreen mode. + PANGOLIN_EXPORT + void ToggleFullscreen(); + + /// Switch windows/fullscreenmode = fullscreen. + PANGOLIN_EXPORT + void SetFullscreen(bool fullscreen = true); + + /// Toggle display of Pangolin console + PANGOLIN_EXPORT + void ToggleConsole(); + + /// Convenience functor for toggling pangolin::View. + /// Use with RegisterKeyPressCallback for example + struct ToggleViewFunctor { + inline ToggleViewFunctor(View& view); + inline ToggleViewFunctor(const std::string& name); + void operator()(); + View& view; + }; + +} + diff --git a/include/pangolin/display/display_internal.h b/include/pangolin/display/display_internal.h new file mode 100644 index 0000000..9e981e2 --- /dev/null +++ b/include/pangolin/display/display_internal.h @@ -0,0 +1,134 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef BUILD_PANGOLIN_VIDEO +# include +#endif // BUILD_PANGOLIN_VIDEO + + +namespace pangolin +{ + +// Forward Declarations +#ifdef HAVE_PYTHON +class ConsoleView; +#endif // HAVE_PYTHON +class GlFont; + +typedef std::map ViewMap; +typedef std::map > KeyhookMap; + +struct PANGOLIN_EXPORT PangolinGl : public WindowInterface +{ + PangolinGl(); + ~PangolinGl(); + + // Base container for displays + View base; + + // Named views which are managed by pangolin (i.e. created / deleted by pangolin) + ViewMap named_managed_views; + + // Optional user app + UserApp* user_app; + + // Global keypress hooks + KeyhookMap keypress_hooks; + + // Manage fullscreen (ToggleFullscreen is quite new) + bool is_double_buffered; + bool is_fullscreen; + GLint windowed_size[2]; + bool is_high_res; + + // State relating to interactivity + bool quit; + int had_input; + int has_resized; + int mouse_state; + View* activeDisplay; + + std::queue > screen_capture; + +#ifdef BUILD_PANGOLIN_VIDEO + View* record_view; + VideoOutput recorder; +#endif + +#ifdef HAVE_PYTHON + ConsoleView* console_view; +#endif + + std::shared_ptr font; + + virtual void ToggleFullscreen() override { + pango_print_warn("ToggleFullscreen: Not available with non-pangolin window.\n"); + } + + virtual void ProcessEvents() override { + pango_print_warn("ProcessEvents: Not available with non-pangolin window.\n"); + } + + virtual void SwapBuffers() override { + pango_print_warn("SwapBuffers: Not available with non-pangolin window.\n"); + } + + virtual void MakeCurrent() override { + pango_print_warn("MakeCurrent: Not available with non-pangolin window.\n"); + } + + virtual void Move(int /*x*/, int /*y*/) override { + pango_print_warn("Move: Not available with non-pangolin window.\n"); + } + + virtual void Resize(unsigned int /*w*/, unsigned int /*h*/) override { + pango_print_warn("Resize: Not available with non-pangolin window.\n"); + } + + +}; + +PangolinGl* GetCurrentContext(); +void AddNewContext(const std::string& name, std::shared_ptr newcontext); +void DeleteContext(const std::string& name); +PangolinGl *FindContext(const std::string& name); + +} + diff --git a/include/pangolin/display/image_view.h b/include/pangolin/display/image_view.h new file mode 100644 index 0000000..c7263b0 --- /dev/null +++ b/include/pangolin/display/image_view.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace pangolin +{ + +class ImageView : public pangolin::View, public pangolin::ImageViewHandler +{ + public: + ImageView(); + + ~ImageView(); + + void Render() override; + + void Mouse(View& view, pangolin::MouseButton button, int x, int y, bool pressed, int button_state) override; + + void Keyboard(View& view, unsigned char key, int x, int y, bool pressed) override; + + pangolin::GlTexture& Tex(); + + ImageView& SetImage(void* ptr, size_t w, size_t h, size_t pitch, pangolin::GlPixFormat img_fmt, bool delayed_upload = false); + + ImageView& SetImage(const pangolin::Image& img, const pangolin::GlPixFormat& glfmt, bool delayed_upload = false); + + ImageView& SetImage(const pangolin::TypedImage& img, bool delayed_upload = false); + + ImageView& SetImage(const pangolin::GlTexture& texture); + + void LoadPending(); + + ImageView& Clear(); + + std::pair& GetOffsetScale(); + + bool MouseReleased() const; + + bool MousePressed() const; + + void SetRenderOverlay(const bool& val); + +// private: + // img_to_load contains image data that should be uploaded to the texture on + // the next render cycle. The data is owned by this object and should be + // freed after use. + pangolin::ManagedImage img_to_load; + pangolin::GlPixFormat img_fmt_to_load; + + std::pair offset_scale; + pangolin::GlPixFormat fmt; + pangolin::GlTexture tex; + bool lastPressed; + bool mouseReleased; + bool mousePressed; + bool overlayRender; + + std::mutex texlock; +}; + +} diff --git a/include/pangolin/display/opengl_render_state.h b/include/pangolin/display/opengl_render_state.h new file mode 100644 index 0000000..7414762 --- /dev/null +++ b/include/pangolin/display/opengl_render_state.h @@ -0,0 +1,425 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) //prevent including Eigen in cuda files +#define USE_EIGEN +#endif + +#ifdef USE_EIGEN +#include +#endif + +#ifdef HAVE_TOON +#include +#include +#include +#endif + +#ifdef HAVE_OCULUS +#include +#endif + +namespace pangolin { + +#ifdef HAVE_GLES + typedef float GLprecision; +#else + typedef double GLprecision; +#endif + +/// Capture OpenGL matrix types in enum to typing. +enum OpenGlStack { + GlModelViewStack = 0x1700, // GL_MODELVIEW + GlProjectionStack = 0x1701, // GL_PROJECTION + GlTextureStack = 0x1702 // GL_TEXTURE +}; + +enum AxisDirection +{ + AxisNone, + AxisNegX, AxisX, + AxisNegY, AxisY, + AxisNegZ, AxisZ +}; + +struct CameraSpec { + GLprecision forward[3]; + GLprecision up[3]; + GLprecision right[3]; + GLprecision img_up[2]; + GLprecision img_right[2]; +}; + +const static CameraSpec CameraSpecOpenGl = {{0,0,-1},{0,1,0},{1,0,0},{0,1},{1,0}}; + +const static CameraSpec CameraSpecYDownZForward = {{0,0,1},{0,-1,0},{1,0,0},{0,-1},{1,0}}; + +/// Direction vector for each AxisDirection enum +const static GLprecision AxisDirectionVector[7][3] = { + {0,0,0}, + {-1,0,0}, {1,0,0}, + {0,-1,0}, {0,1,0}, + {0,0,-1}, {0,0,1} +}; + +/// Object representing OpenGl Matrix. +struct PANGOLIN_EXPORT OpenGlMatrix { + static OpenGlMatrix Translate(GLprecision x, GLprecision y, GLprecision z); + static OpenGlMatrix Scale(GLprecision x, GLprecision y, GLprecision z); + static OpenGlMatrix RotateX(GLprecision theta_rad); + static OpenGlMatrix RotateY(GLprecision theta_rad); + static OpenGlMatrix RotateZ(GLprecision theta_rad); + + + template + static OpenGlMatrix ColMajor4x4(const P* col_major_4x4); + + OpenGlMatrix(); + +#ifdef USE_EIGEN + template + OpenGlMatrix(const Eigen::Matrix& mat); + + template + operator Eigen::Matrix() const; +#endif // USE_EIGEN + +#ifdef HAVE_TOON + OpenGlMatrix(const TooN::SE3<>& T); + OpenGlMatrix(const TooN::Matrix<4,4>& M); + operator const TooN::SE3<>() const; + operator const TooN::Matrix<4,4>() const; +#endif // HAVE_TOON + +#ifdef HAVE_OCULUS + OpenGlMatrix(const OVR::Matrix4f& M); + operator const OVR::Matrix4f() const; +#endif // HAVE_OCULUS + + // Load matrix on to OpenGl stack + void Load() const; + + void Multiply() const; + + void SetIdentity(); + + OpenGlMatrix Transpose() const; + + OpenGlMatrix Inverse() const; + + GLprecision& operator()(int r, int c) { + return m[4*c +r]; + } + + GLprecision operator()(int r, int c) const { + return m[4 * c + r]; + } + + // Column major Internal buffer + GLprecision m[16]; +}; + +PANGOLIN_EXPORT +OpenGlMatrix operator*(const OpenGlMatrix& lhs, const OpenGlMatrix& rhs); + +PANGOLIN_EXPORT +std::ostream& operator<<(std::ostream& os, const OpenGlMatrix& mat); + +/// Deprecated. +struct PANGOLIN_EXPORT OpenGlMatrixSpec : public OpenGlMatrix { + // Specify which stack this refers to + OpenGlStack type; +}; + +/// Object representing attached OpenGl Matrices / transforms. +class PANGOLIN_EXPORT OpenGlRenderState +{ +public: + OpenGlRenderState(); + OpenGlRenderState(const OpenGlMatrix& projection_matrix); + OpenGlRenderState(const OpenGlMatrix& projection_matrix, const OpenGlMatrix& modelview_matrix); + + static void ApplyIdentity(); + + void Apply() const; + OpenGlRenderState& SetProjectionMatrix(OpenGlMatrix m); + OpenGlRenderState& SetModelViewMatrix(OpenGlMatrix m); + + OpenGlMatrix& GetProjectionMatrix(); + OpenGlMatrix GetProjectionMatrix() const; + + OpenGlMatrix& GetModelViewMatrix(); + OpenGlMatrix GetModelViewMatrix() const; + + OpenGlMatrix GetProjectionModelViewMatrix() const; + OpenGlMatrix GetProjectiveTextureMatrix() const; + + void EnableProjectiveTexturing() const; + void DisableProjectiveTexturing() const; + + //! Seemlessly move OpenGl camera relative to changes in T_wc, + //! whilst still enabling interaction + void Follow(const OpenGlMatrix& T_wc, bool follow = true); + void Unfollow(); + + // Experimental - subject to change + OpenGlMatrix& GetProjectionMatrix(unsigned int view); + OpenGlMatrix GetProjectionMatrix(unsigned int view) const; + OpenGlMatrix& GetViewOffset(unsigned int view); + OpenGlMatrix GetViewOffset(unsigned int view) const; + OpenGlMatrix GetModelViewMatrix(int i) const; + void ApplyNView(int view) const; + + PANGOLIN_DEPRECATED + OpenGlRenderState& Set(OpenGlMatrixSpec spec); + +protected: + OpenGlMatrix modelview; + std::vector projection; + std::vector modelview_premult; + OpenGlMatrix T_cw; + bool follow; +}; + +PANGOLIN_EXPORT +OpenGlMatrixSpec ProjectionMatrixRUB_BottomLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar ); + +PANGOLIN_EXPORT +OpenGlMatrixSpec ProjectionMatrixRDF_TopLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar ); + +PANGOLIN_EXPORT +OpenGlMatrixSpec ProjectionMatrixRDF_BottomLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar ); + + +//! Use OpenGl's default frame RUB_BottomLeft +PANGOLIN_EXPORT +OpenGlMatrixSpec ProjectionMatrix(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar ); + +PANGOLIN_EXPORT +OpenGlMatrixSpec ProjectionMatrixOrthographic(GLprecision l, GLprecision r, GLprecision b, GLprecision t, GLprecision n, GLprecision f ); + + +//! Generate glulookat style model view matrix, looking at (lx,ly,lz) +//! X-Right, Y-Up, Z-Back +PANGOLIN_EXPORT +OpenGlMatrix ModelViewLookAtRUB(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, GLprecision ux, GLprecision uy, GLprecision uz); + +//! Generate glulookat style model view matrix, looking at (lx,ly,lz) +//! X-Right, Y-Down, Z-Forward +PANGOLIN_EXPORT +OpenGlMatrix ModelViewLookAtRDF(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, GLprecision ux, GLprecision uy, GLprecision uz); + +//! Generate glulookat style model view matrix, OpenGL Default camera convention (XYZ=RUB), looking at (lx,ly,lz) +PANGOLIN_EXPORT +OpenGlMatrix ModelViewLookAt(GLprecision x, GLprecision y, GLprecision z, GLprecision lx, GLprecision ly, GLprecision lz, AxisDirection up); + +PANGOLIN_EXPORT +OpenGlMatrix ModelViewLookAt(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, GLprecision ux, GLprecision uy, GLprecision uz); + + +PANGOLIN_EXPORT +OpenGlMatrix IdentityMatrix(); + +PANGOLIN_EXPORT +OpenGlMatrixSpec IdentityMatrix(OpenGlStack type); + +PANGOLIN_EXPORT +OpenGlMatrixSpec negIdentityMatrix(OpenGlStack type); + +#ifdef HAVE_TOON +OpenGlMatrixSpec FromTooN(const TooN::SE3<>& T_cw); +OpenGlMatrixSpec FromTooN(OpenGlStack type, const TooN::Matrix<4,4>& M); +TooN::Matrix<4,4> ToTooN(const OpenGlMatrixSpec& ms); +TooN::SE3<> ToTooN_SE3(const OpenGlMatrixSpec& ms); +#endif + +#ifdef HAVE_EIGEN +template +Eigen::Matrix ToEigen(const OpenGlMatrix& ms); +#endif + +} + +// Inline definitions +namespace pangolin +{ + +template +inline OpenGlMatrix OpenGlMatrix::ColMajor4x4(const P* col_major_4x4) +{ + OpenGlMatrix mat; + std::copy(col_major_4x4, col_major_4x4 + 16, mat.m); + return mat; +} + +inline OpenGlMatrix::OpenGlMatrix() { +} + +#ifdef USE_EIGEN +template inline +OpenGlMatrix::OpenGlMatrix(const Eigen::Matrix& mat) +{ + for(int r=0; r<4; ++r ) { + for(int c=0; c<4; ++c ) { + m[c*4+r] = mat(r,c); + } + } +} + +template +OpenGlMatrix::operator Eigen::Matrix() const +{ + return ToEigen

    (*this); +} + +template inline +Eigen::Matrix ToEigen(const OpenGlMatrix& ms) +{ + Eigen::Matrix mat; + for(int r=0; r<4; ++r ) { + for(int c=0; c<4; ++c ) { + mat(r,c) = (P)ms.m[c*4+r]; + } + } + return mat; +} + +#endif // USE_EIGEN + +#ifdef HAVE_TOON +inline OpenGlMatrix::OpenGlMatrix(const TooN::SE3<>& T) +{ + TooN::Matrix<4,4,GLprecision,TooN::ColMajor> M; + M.slice<0,0,3,3>() = T.get_rotation().get_matrix(); + M.T()[3].slice<0,3>() = T.get_translation(); + M[3] = TooN::makeVector(0,0,0,1); + std::memcpy(m, &(M[0][0]),16*sizeof(GLprecision)); +} + +inline OpenGlMatrix::OpenGlMatrix(const TooN::Matrix<4,4>& M) +{ + // Read in remembering col-major convension for our matrices + int el = 0; + for(int c=0; c<4; ++c) + for(int r=0; r<4; ++r) + m[el++] = M[r][c]; +} + +inline OpenGlMatrix::operator const TooN::SE3<>() const +{ + const TooN::Matrix<4,4> m = *this; + const TooN::SO3<> R(m.slice<0,0,3,3>()); + const TooN::Vector<3> t = m.T()[3].slice<0,3>(); + return TooN::SE3<>(R,t); +} + +inline OpenGlMatrix::operator const TooN::Matrix<4,4>() const +{ + TooN::Matrix<4,4> M; + int el = 0; + for( int c=0; c<4; ++c ) + for( int r=0; r<4; ++r ) + M(r,c) = m[el++]; + return M; +} + +PANGOLIN_DEPRECATED +inline OpenGlMatrixSpec FromTooN(const TooN::SE3<>& T_cw) +{ + TooN::Matrix<4,4,GLprecision,TooN::ColMajor> M; + M.slice<0,0,3,3>() = T_cw.get_rotation().get_matrix(); + M.T()[3].slice<0,3>() = T_cw.get_translation(); + M[3] = TooN::makeVector(0,0,0,1); + + OpenGlMatrixSpec P; + P.type = GlModelViewStack; + std::memcpy(P.m, &(M[0][0]),16*sizeof(GLprecision)); + return P; +} + +PANGOLIN_DEPRECATED +inline OpenGlMatrixSpec FromTooN(OpenGlStack type, const TooN::Matrix<4,4>& M) +{ + // Read in remembering col-major convension for our matrices + OpenGlMatrixSpec P; + P.type = type; + int el = 0; + for(int c=0; c<4; ++c) + for(int r=0; r<4; ++r) + P.m[el++] = M[r][c]; + return P; +} + +PANGOLIN_DEPRECATED +inline TooN::Matrix<4,4> ToTooN(const OpenGlMatrix& ms) +{ + TooN::Matrix<4,4> m; + int el = 0; + for( int c=0; c<4; ++c ) + for( int r=0; r<4; ++r ) + m(r,c) = ms.m[el++]; + return m; +} + +PANGOLIN_DEPRECATED +inline TooN::SE3<> ToTooN_SE3(const OpenGlMatrix& ms) +{ + TooN::Matrix<4,4> m = ms; + const TooN::SO3<> R(m.slice<0,0,3,3>()); + const TooN::Vector<3> t = m.T()[3].slice<0,3>(); + return TooN::SE3<>(R,t); +} + +#endif // HAVE_TOON + +#ifdef HAVE_OCULUS +inline OpenGlMatrix::OpenGlMatrix(const OVR::Matrix4f& mat) +{ + for(int r=0; r<4; ++r ) + for(int c=0; c<4; ++c ) + m[c*4+r] = mat.M[r][c]; +} + +inline OpenGlMatrix::operator const OVR::Matrix4f() const +{ + OVR::Matrix4f mat; + for(int r=0; r<4; ++r ) + for(int c=0; c<4; ++c ) + mat.M[r][c] = m[c*4+r]; + return mat; +} +#endif // HAVE_OCULUS + + +} diff --git a/include/pangolin/display/user_app.h b/include/pangolin/display/user_app.h new file mode 100644 index 0000000..013ad26 --- /dev/null +++ b/include/pangolin/display/user_app.h @@ -0,0 +1,43 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT UserApp +{ +public: + virtual ~UserApp() {} + virtual void Init() {} + virtual void Render() = 0; +}; + +} diff --git a/include/pangolin/display/view.h b/include/pangolin/display/view.h new file mode 100644 index 0000000..7bc7a0d --- /dev/null +++ b/include/pangolin/display/view.h @@ -0,0 +1,233 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin +{ + +enum Layout +{ + LayoutOverlay, + LayoutVertical, + LayoutHorizontal, + LayoutEqual, + LayoutEqualVertical, + LayoutEqualHorizontal +}; + +enum Lock { + LockLeft = 0, + LockBottom = 0, + LockCenter = 1, + LockRight = 2, + LockTop = 2 +}; + +// Forward declarations +struct Handler; + +class OpenGlRenderState; + +/// A Display manages the location and resizing of an OpenGl viewport. +struct PANGOLIN_EXPORT View +{ + View(double aspect=0.0) + : aspect(aspect), top(1.0),left(0.0),right(1.0),bottom(0.0), hlock(LockCenter),vlock(LockCenter), + layout(LayoutOverlay), scroll_offset(0), show(1), zorder(0), handler(0) {} + + virtual ~View() {} + + //! Activate Displays viewport for drawing within this area + void Activate() const; + + //! Activate Displays and set State Matrices + void Activate(const OpenGlRenderState& state ) const; + + //! Activate Displays viewport and Scissor for drawing within this area + void ActivateAndScissor() const; + + //! Activate Displays viewport and Scissor for drawing within this area + void ActivateScissorAndClear() const; + + //! Activate Display and set State Matrices + void ActivateAndScissor(const OpenGlRenderState& state ) const; + + //! Activate Display and set State Matrices + void ActivateScissorAndClear(const OpenGlRenderState& state ) const; + + //! Activate Display and setup coordinate system for 2d pixel View coordinates + void ActivatePixelOrthographic() const; + + //! Activate Display and reset coordinate system to OpenGL default + void ActivateIdentity() const; + + //! Return closest depth buffer value within radius of window (winx,winy) + GLfloat GetClosestDepth(int winx, int winy, int radius) const; + + //! Obtain camera space coordinates of scene at pixel (winx, winy, winzdepth) + //! winzdepth can be obtained from GetClosestDepth + void GetCamCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const; + + //! Obtain object space coordinates of scene at pixel (winx, winy, winzdepth) + //! winzdepth can be obtained from GetClosestDepth + void GetObjectCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const; + + //! Given the specification of Display, compute viewport + virtual void Resize(const Viewport& parent); + + //! Instruct all children to resize + virtual void ResizeChildren(); + + //! Perform any automatic rendering for this View. + //! Default implementation simply instructs children to render themselves. + virtual void Render(); + + //! Instruct all children to render themselves if appropriate + virtual void RenderChildren(); + + //! Set this view as the active View to receive input + View& SetFocus(); + + //! Returns true iff this view currently has focus and will receive user input + bool HasFocus() const; + + //! Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates) + View& SetBounds(Attach bottom, Attach top, Attach left, Attach right); + + //! Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates) + View& SetBounds(Attach bottom, Attach top, Attach left, Attach right, bool keep_aspect); + + //! Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates) + View& SetBounds(Attach bottom, Attach top, Attach left, Attach right, double aspect); + + //! Designate handler for accepting mouse / keyboard input. + View& SetHandler(Handler* handler); + + //! Set drawFunc as the drawing function for this view + View& SetDrawFunction(const std::function& drawFunc); + + //! Force this view to have the given aspect, whilst fitting snuggly + //! within the parent. A negative value with 'over-draw', fitting the + //! smaller side of the parent. + View& SetAspect(double aspect); + + //! Set how this view should be positioned relative to its parent + View& SetLock(Lock horizontal, Lock vertical ); + + //! Set layout policy for this view + View& SetLayout(Layout layout); + + //! Add view as child + View& AddDisplay(View& view); + + //! Show / hide this view + View& Show(bool show=true); + + //! Toggle this views visibility + void ToggleShow(); + + //! Return whether this view should be shown. + //! This method should be checked if drawing manually + bool IsShown() const; + + //! Returns viewport reflecting space that will actually get drawn + //! The minimum of vp and v + Viewport GetBounds() const; + + //! Specify that this views region in the framebuffer should be saved to + //! a file just before the buffer is flipped. + void SaveOnRender(const std::string& filename_prefix); + + //! Specify that this views region in the framebuffer should be saved to + //! a video just before the buffer is flipped + void RecordOnRender(const std::string& record_uri); + + //! Uses the views default render method to draw into an FBO 'scale' times + //! the size of the view and save to a file. + void SaveRenderNow(const std::string& filename_prefix, float scale = 1); + + //! Return number of child views attached to this view + size_t NumChildren() const; + + //! Return (i)th child of this view + View& operator[](size_t i); + + //! Return number of visible child views attached to this view. + size_t NumVisibleChildren() const; + + //! Return visible child by index. + View& VisibleChild(size_t i); + + //! Return visible child at window coords x,y + View* FindChild(int x, int y); + + // Desired width / height aspect (0 if dynamic) + double aspect; + + // Bounds to fit display within + Attach top, left, right, bottom; + Lock hlock; + Lock vlock; + Layout layout; + + int scroll_offset; + + // Cached client area (space allocated from parent) + Viewport vp; + + // Cached absolute viewport (recomputed on resize - respects aspect) + Viewport v; + + // Should this view be displayed? + bool show; + + // Child views are rendered in order of low to high z-order + // Views default to 0 z-order + int zorder; + + // Input event handler (if any) + Handler* handler; + + // Map for sub-displays (if any) + std::vector views; + + // External draw function + std::function extern_draw_function; + +private: + // Private copy constructor + View(View&) { /* Do Not copy - take reference instead*/ } +}; + +} diff --git a/include/pangolin/display/viewport.h b/include/pangolin/display/viewport.h new file mode 100644 index 0000000..b70e93c --- /dev/null +++ b/include/pangolin/display/viewport.h @@ -0,0 +1,62 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin +{ + +/// Encapsulates OpenGl Viewport. +struct PANGOLIN_EXPORT Viewport +{ + Viewport() : l(0),b(0),w(0),h(0) {} + Viewport(GLint l,GLint b,GLint w,GLint h) : l(l),b(b),w(w),h(h) {} + + void Activate() const; + void ActivateIdentity() const; + void ActivatePixelOrthographic() const; + + void Scissor() const; + void ActivateAndScissor() const; + + bool Contains(int x, int y) const; + + Viewport Inset(int i) const; + Viewport Inset(int horiz, int vert) const; + Viewport Intersect(const Viewport& vp) const; + + static void DisableScissor(); + + GLint r() const { return l+w;} + GLint t() const { return b+h;} + GLfloat aspect() const { return (GLfloat)w / (GLfloat)h; } + GLint l,b,w,h; +}; + +} diff --git a/include/pangolin/display/widgets/widgets.h b/include/pangolin/display/widgets/widgets.h new file mode 100644 index 0000000..5f49a79 --- /dev/null +++ b/include/pangolin/display/widgets/widgets.h @@ -0,0 +1,140 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace pangolin +{ + +PANGOLIN_EXPORT +View& CreatePanel(const std::string& name); + +struct PANGOLIN_EXPORT Panel : public View +{ + Panel(); + Panel(const std::string& auto_register_var_prefix); + void Render(); + void ResizeChildren(); + static void AddVariable(void* data, const std::string& name, VarValueGeneric& var, bool brand_new); +}; + +template +struct Widget : public View, Handler, Var +{ + Widget(std::string title, VarValueGeneric& tv) + : Var(tv), title(title) + { + handler = this; + } + + std::string title; +}; + +struct PANGOLIN_EXPORT Button : public Widget +{ + Button(std::string title, VarValueGeneric& tv); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state); + void Render(); + + //Cache params on resize + void ResizeChildren(); + GlText gltext; + GLfloat raster[2]; + bool down; +}; + +struct PANGOLIN_EXPORT FunctionButton : public Widget > +{ + FunctionButton(std::string title, VarValueGeneric& tv); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state); + void Render(); + + //Cache params on resize + void ResizeChildren(); + GlText gltext; + GLfloat raster[2]; + bool down; +}; + +struct PANGOLIN_EXPORT Checkbox : public Widget +{ + Checkbox(std::string title, VarValueGeneric& tv); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state); + void Render(); + + //Cache params on resize + void ResizeChildren(); + GlText gltext; + GLfloat raster[2]; + Viewport vcb; +}; + +struct PANGOLIN_EXPORT Slider : public Widget +{ + Slider(std::string title, VarValueGeneric& tv); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state); + void MouseMotion(View&, int x, int y, int mouse_state); + void Keyboard(View&, unsigned char key, int x, int y, bool pressed); + void Render(); + + //Cache params on resize + void ResizeChildren(); + GlText gltext; + GLfloat raster[2]; + bool lock_bounds; + bool logscale; + bool is_integral_type; +}; + +struct PANGOLIN_EXPORT TextInput : public Widget +{ + TextInput(std::string title, VarValueGeneric& tv); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state); + void MouseMotion(View&, int x, int y, int mouse_state); + void Keyboard(View&, unsigned char key, int x, int y, bool pressed); + void Render(); + + std::string edit; + GlText gledit; + + //Cache params on resize + void ResizeChildren(); + GlText gltext; + GLfloat raster[2]; + bool do_edit; + int sel[2]; +}; + + +} diff --git a/include/pangolin/display/window.h b/include/pangolin/display/window.h new file mode 100644 index 0000000..bd02138 --- /dev/null +++ b/include/pangolin/display/window.h @@ -0,0 +1,52 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2016 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +namespace pangolin +{ + +class GlContextInterface +{ +public: + virtual ~GlContextInterface() {} +}; + +class WindowInterface +{ +public: + virtual ~WindowInterface() {} + + virtual void ToggleFullscreen() = 0; + virtual void Move(int x, int y) = 0; + virtual void Resize(unsigned int w, unsigned int h) = 0; + virtual void MakeCurrent() = 0; + virtual void ProcessEvents() = 0; + virtual void SwapBuffers() = 0; +}; + +} diff --git a/include/pangolin/factory/factory_registry.h b/include/pangolin/factory/factory_registry.h new file mode 100644 index 0000000..0f77738 --- /dev/null +++ b/include/pangolin/factory/factory_registry.h @@ -0,0 +1,113 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011-2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace pangolin +{ + +template +struct FactoryInterface +{ + typedef T FactoryItem; + + virtual std::unique_ptr Open(const Uri& uri) = 0; +}; + +template +class FactoryRegistry +{ +public: + // IMPORTANT: Implement for each templated instantiation within a seperate compilation unit. + static FactoryRegistry& I(); + + ~FactoryRegistry() + { + } + + void RegisterFactory(std::shared_ptr> factory, uint32_t precedence, const std::string& scheme_name ) + { + FactoryItem item = {precedence, scheme_name, factory}; + factories.push_back( item ); + std::sort(factories.begin(), factories.end()); + } + + void UnregisterFactory(FactoryInterface* factory) + { + for( auto i = factories.end()-1; i != factories.begin(); --i) + { + if( i->factory.get() == factory ) { + factories.erase(i); + } + } + } + + void UnregisterAllFactories() + { + factories.clear(); + } + + std::unique_ptr Open(const Uri& uri) + { + // Iterate over all registered factories in order of precedence. + for(auto& item : factories) { + if( item.scheme == uri.scheme) { + std::unique_ptr video = item.factory->Open(uri); + if(video) { + return video; + } + } + } + + return std::unique_ptr(); + } + +private: + struct FactoryItem + { + uint32_t precedence; + std::string scheme; + std::shared_ptr> factory; + + bool operator<(const FactoryItem& rhs) const { + return precedence < rhs.precedence; + } + }; + + // Priority, Factory tuple + std::vector factories; +}; + +#define PANGOLIN_REGISTER_FACTORY(x) void Register ## x ## Factory() + +} diff --git a/include/pangolin/gl/cg.h b/include/pangolin/gl/cg.h new file mode 100644 index 0000000..5659016 --- /dev/null +++ b/include/pangolin/gl/cg.h @@ -0,0 +1,283 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +// Cg includes +#include +#include + +#include "gl.h" + +#ifdef HAVE_TOON +#include +#endif // HAVE_TOON + +namespace pangolin +{ + +//////////////////////////////////////////////// +// Interface +//////////////////////////////////////////////// + +/// Lightweight object wrapper for NVidia Cg Shader program objects. +class CgProgram +{ + friend class CgLoader; +public: + void SetUniform(const std::string& name, GlTexture& tex); + void SetUniform(const std::string& name, float f); + void SetUniform(const std::string& name, float v0, float v1); + void SetUniform(const std::string& name, float v0, float v1, float v2, float v3); + +#ifdef HAVE_TOON + void SetUniform(const std::string& name, const TooN::Vector<2>& v ); + void SetUniform(const std::string& name, const TooN::Vector<3>& v ); + + template + void SetUniform(const std::string& name, const TooN::Matrix& M ); +#endif + + void UpdateParams(); + +protected: + CGprogram mProg; + CGcontext mContext; + CGprofile mProfile; +}; + +class CgLoader +{ +public: + CgLoader(); + ~CgLoader(); + + // Call AFTER glutInit (or similar) + void Initialise(); + + CgProgram LoadProgramFromFile(const std::string& file, const std::string& function, bool isVertexShader ); + + void EnableProgram(CgProgram program); + void DisablePrograms(); + + void RenderDummyQuad(); + void RenderDummyQuadWithTexCoords(int w, int h); + +protected: + CGcontext mContext; + CGprofile mFragmentProfile; + CGprofile mVertexProfile; +}; + + + + +//////////////////////////////////////////////// +// Implementation +//////////////////////////////////////////////// + +inline bool cgOkay() +{ + CGerror error; + const char *string = cgGetLastErrorString(&error); + + if (error != CG_NO_ERROR) { + std::cout << "CG Error: " << string << std::endl; + // assert(0); + return false; + } + return true; +} + +inline CgLoader::CgLoader() + :mContext(0) +{ +} + +inline CgLoader::~CgLoader() +{ + if(mContext) + { + // Destroying context destroys all programs associated with it + cgDestroyContext(mContext); + } +} + + +inline void CgLoader::Initialise() +{ + mContext = cgCreateContext(); + cgSetParameterSettingMode(mContext, CG_DEFERRED_PARAMETER_SETTING); + cgOkay(); + + mFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT); + cgGLSetOptimalOptions(mFragmentProfile); + cgOkay(); + + mVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX); + cgGLSetOptimalOptions(mVertexProfile); + cgOkay(); +} + +inline CgProgram CgLoader::LoadProgramFromFile(const std::string& file, const std::string& function, bool isVertexShader ) +{ + if( !mContext ) { + Initialise(); + } + + CgProgram prog; + + prog.mContext = mContext; + prog.mProfile = isVertexShader ? mVertexProfile : mFragmentProfile; + prog.mProg = cgCreateProgramFromFile( prog.mContext, CG_SOURCE, file.c_str(), prog.mProfile, function.c_str(), NULL); + + if( !cgOkay() ) + { + std::cout << cgGetLastListing(mContext) << std::endl; + assert(0); + } + + cgGLLoadProgram(prog.mProg); + if( !cgOkay() ) + { + const char* err = cgGetProgramString( prog.mProg, CG_COMPILED_PROGRAM ); + int pos; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); + std::cout << err << std::endl; + std::cout << "@ " << pos << std::endl; + assert(0); + } + return prog; +} + +inline void CgLoader::EnableProgram(CgProgram program) +{ + cgGLBindProgram(program.mProg); + cgGLEnableProfile(program.mProfile); + cgOkay(); +} + +inline void CgLoader::DisablePrograms() +{ + cgGLDisableProfile(mFragmentProfile); + cgGLDisableProfile(mVertexProfile); +} + +inline void CgLoader::RenderDummyQuad() +{ + glBegin(GL_QUADS); + glVertex2d(-1,1); + glVertex2d(1,1); + glVertex2d(1,-1); + glVertex2d(-1,-1); + glEnd(); +} + +inline void CgLoader::RenderDummyQuadWithTexCoords(int w, int h) +{ + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2d(-1,-1); + glTexCoord2f(w, 0); + glVertex2d(1,-1); + glTexCoord2f(w, h); + glVertex2d(1,1); + glTexCoord2f(0, h); + glVertex2d(-1,1); + glEnd(); +} + +void CgProgram::SetUniform(const std::string& name, float f) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + cgSetParameter1f( p, f ); + cgUpdateProgramParameters(mProg); +} + +void CgProgram::SetUniform(const std::string& name, GlTexture& tex) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + cgGLSetTextureParameter(p, tex.tid ); + cgGLEnableTextureParameter(p); + cgUpdateProgramParameters(mProg); +} + +void CgProgram::SetUniform(const std::string& name, float v0, float v1, float v2, float v3) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + cgGLSetParameter4f(p, v0,v1,v2,v3); + cgUpdateProgramParameters(mProg); +} + +void CgProgram::SetUniform(const std::string& name, float v0, float v1) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + cgGLSetParameter2f(p, v0,v1); + cgUpdateProgramParameters(mProg); +} + +#ifdef HAVE_TOON +void CgProgram::SetUniform(const std::string& name, const TooN::Vector<2>& v ) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + cgGLSetParameter2f(p, v[0],v[1] ); + cgUpdateProgramParameters(mProg); +} + +void CgProgram::SetUniform(const std::string& name, const TooN::Vector<3>& v ) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + cgGLSetParameter3f(p, v[0],v[1],v[2] ); + cgUpdateProgramParameters(mProg); +} + +template +void CgProgram::SetUniform(const std::string& name, const TooN::Matrix& M ) +{ + CGparameter p = cgGetNamedParameter( mProg, name.c_str()); + float Mdata[R*C]; + + int i=0; + for( int r=0; r + +#include + +namespace pangolin +{ + +/// Represent OpenGL floating point colour: Red, Green and Blue with alpha. +struct Colour +{ + inline static Colour White() { + return Colour(1.0f,1.0f,1.0f,1.0f); + } + inline static Colour Black() { + return Colour(0.0f,0.0f,0.0f,1.0f); + } + inline static Colour Red() { + return Colour(1.0f,0.0f,0.0f,1.0f); + } + inline static Colour Green() { + return Colour(0.0f,1.0f,0.0f,1.0f); + } + inline static Colour Blue() { + return Colour(0.0f,0.0f,1.0f,1.0f); + } + inline static Colour Unspecified() { + return Colour( + std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() + ); + } + + /// Default constructs white. + inline Colour() + : red(1.0f), green(1.0f), blue(1.0f), alpha(1.0f) + { + } + + /// Construct from component values + inline Colour(float red, float green, float blue, float alpha = 1.0f) + : red(red), green(green), blue(blue), alpha(alpha) + { + } + + /// Construct from rgba array. + inline Colour(float rgba[4]) + { + r = rgba[0]; + g = rgba[1]; + b = rgba[2]; + a = rgba[3]; + } + + /// Return pointer to OpenGL compatible RGBA array. + inline float* Get() + { + return c; + } + + /// Return this colour with alpha adjusted. + inline Colour WithAlpha(float alpha) + { + return Colour(r,g,b,alpha); + } + + /// Construct from HSV Colour + /// @param hue Colour hue in range [0,1] + /// @param sat Saturation in range [0,1] + /// @param val Value / Brightness in range [0,1]. + static inline Colour Hsv(float hue, float sat = 1.0f, float val = 1.0f, float alpha = 1.0f) + { + const float h = 6.0f * hue; + const int i = (int)floor(h); + const float f = (i%2 == 0) ? 1-(h-i) : h-i; + const float m = val * (1-sat); + const float n = val * (1-sat*f); + + switch(i) + { + case 0: return Colour(val,n,m,alpha); + case 1: return Colour(n,val,m,alpha); + case 2: return Colour(m,val,n,alpha); + case 3: return Colour(m,n,val,alpha); + case 4: return Colour(n,m,val,alpha); + case 5: return Colour(val,m,n,alpha); + default: + throw std::runtime_error("Found extra colour in rainbow."); + } + } + + union { + struct { + float red; + float green; + float blue; + float alpha; + }; + struct { + float r; + float g; + float b; + float a; + }; + float c[4]; + }; + +}; + +/// A ColourWheel is like a continuous colour palate that can be sampled. +/// In the future, different ColourWheels will be supported, but this one +/// is based on sampling hues in HSV colourspace. An indefinite number of +/// unique colours are sampled using the golden angle. +class ColourWheel +{ +public: + /// Construct ColourWheel with Saturation, Value and Alpha constant. + inline ColourWheel(float saturation = 0.5f, float value = 1.0f, float alpha = 1.0f) + : unique_colours(0), sat(saturation), val(value), alpha(alpha) + { + + } + + /// Use Golden ratio (/angle) to pick well spaced colours. + inline Colour GetColourBin(int i) const + { + float hue = i * 0.5f * (3.0f - sqrt(5.0f)); + hue -= (int)hue; + return Colour::Hsv(hue,sat,val,alpha); + } + + /// Return next unique colour from ColourWheel. + inline Colour GetUniqueColour() + { + return GetColourBin(unique_colours++); + } + +protected: + int unique_colours; + float sat; + float val; + float alpha; +}; + +} diff --git a/include/pangolin/gl/compat/gl2engine.h b/include/pangolin/gl/compat/gl2engine.h new file mode 100644 index 0000000..184aa60 --- /dev/null +++ b/include/pangolin/gl/compat/gl2engine.h @@ -0,0 +1,320 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include + +namespace pangolin { + +class GlEngine +{ +public: + const char* vert = + "attribute vec4 a_position;\n" + "attribute vec4 a_color;\n" + "attribute vec3 a_normal;\n" + "attribute vec2 a_texcoord;\n" + "uniform vec4 u_color;\n" + "uniform mat4 u_modelViewMatrix;\n" + "uniform mat4 u_modelViewProjectionMatrix;\n" + "varying vec4 v_frontColor;\n" + "varying vec2 v_texcoord;\n" + "void main() {\n" + " gl_Position = u_modelViewProjectionMatrix * a_position;\n" + " v_frontColor = u_color;\n" + " v_texcoord = a_texcoord;\n" + "}\n"; + + const char* frag = + #ifdef HAVE_GLES_2 + "precision mediump float;\n" + #endif // HAVE_GLES_2 + "varying vec4 v_frontColor;\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D u_texture;\n" + "uniform bool u_textureEnable;\n" + "void main() {\n" + " gl_FragColor = v_frontColor;\n" + " if(u_textureEnable) {\n" + " gl_FragColor *= texture2D(u_texture, v_texcoord);\n" + " }\n" + "}\n"; + + GlEngine() + { + // Initialise default state + projection.push(IdentityMatrix()); + modelview.push(IdentityMatrix()); + currentmatrix = &modelview; + + // Set GL_TEXTURE0 as default active texture + glActiveTexture(GL_TEXTURE0); + + // Compile and link shaders + prog_fixed.AddShader(GlSlVertexShader, vert); + prog_fixed.AddShader(GlSlFragmentShader, frag); + prog_fixed.BindPangolinDefaultAttribLocationsAndLink(); + + // Save locations of uniforms + u_color = prog_fixed.GetUniformHandle("u_color"); + u_modelViewMatrix = prog_fixed.GetUniformHandle("u_modelViewMatrix"); + u_modelViewProjectionMatrix = prog_fixed.GetUniformHandle("u_modelViewProjectionMatrix"); + u_texture = prog_fixed.GetUniformHandle("u_texture"); + u_textureEnable = prog_fixed.GetUniformHandle("u_textureEnable"); + + // Initialise default uniform values + UpdateMatrices(); + SetColor(1.0,1.0,1.0,1.0); + } + + void UpdateMatrices() + { + OpenGlMatrix pmv = projection.top() * modelview.top(); + prog_fixed.SaveBind(); + glUniformMatrix4fv( u_modelViewMatrix, 1, false, modelview.top().m ); + glUniformMatrix4fv( u_modelViewProjectionMatrix, 1, false, pmv.m ); + prog_fixed.Unbind(); + } + + void SetColor(float r, float g, float b, float a) + { + prog_fixed.SaveBind(); + glUniform4f( u_color, r, g, b, a); + prog_fixed.Unbind(); + } + + void EnableTexturing(GLboolean v) + { + prog_fixed.SaveBind(); + glUniform1i( u_textureEnable, v); + prog_fixed.Unbind(); + } + +//protected: + std::stack projection; + std::stack modelview; + std::stack* currentmatrix; + + GLenum matrixmode; + + float color[4]; + + GlSlProgram prog_fixed; + + GLint u_color; + GLint u_modelViewMatrix; + GLint u_modelViewProjectionMatrix; + GLint u_texture; + GLint u_textureEnable; +}; + +GlEngine& glEngine(); + +} + +/////////////////////////////////////////////////////////////////////////////// +// OpenGL 1.0 compatibility - Emulate fixed pipeline +/////////////////////////////////////////////////////////////////////////////// + +// Missing defines that we'll be using +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_SHADE_MODEL 0x0B54 +#define GL_POINT_SIZE 0x0B11 + +#define GL_MULTISAMPLE 0x809D + +#define GL_LIGHTING 0x0B50 +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_MATERIAL 0x0B57 + +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 + +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +#define GL_ADD 0x0104 +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 +#define GL_TEXTURE_ENV 0x2300 + +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_LINE_SMOOTH_HINT 0x0C52 + +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 + +inline void glEnableClientState(GLenum cap) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + if(cap == GL_VERTEX_ARRAY) { + glEnableVertexAttribArray(pangolin::DEFAULT_LOCATION_POSITION); + }else if(cap == GL_COLOR_ARRAY) { + glEnableVertexAttribArray(pangolin::DEFAULT_LOCATION_COLOUR); + }else if(cap == GL_NORMAL_ARRAY) { + glEnableVertexAttribArray(pangolin::DEFAULT_LOCATION_NORMAL); + }else if(cap == GL_TEXTURE_COORD_ARRAY) { + glEnableVertexAttribArray(pangolin::DEFAULT_LOCATION_TEXCOORD); + gl.EnableTexturing(true); + }else{ + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); + } +} + +inline void glDisableClientState(GLenum cap) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + if(cap == GL_VERTEX_ARRAY) { + glDisableVertexAttribArray(pangolin::DEFAULT_LOCATION_POSITION); + }else if(cap == GL_COLOR_ARRAY) { + glDisableVertexAttribArray(pangolin::DEFAULT_LOCATION_COLOUR); + }else if(cap == GL_NORMAL_ARRAY) { + glDisableVertexAttribArray(pangolin::DEFAULT_LOCATION_NORMAL); + }else if(cap == GL_TEXTURE_COORD_ARRAY) { + glDisableVertexAttribArray(pangolin::DEFAULT_LOCATION_TEXCOORD); + gl.EnableTexturing(false); + }else{ + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); + } +} + +inline void glVertexPointer( GLint size, GLenum type, GLsizei stride, const GLvoid * pointer) +{ + glVertexAttribPointer(pangolin::DEFAULT_LOCATION_POSITION, size, type, GL_FALSE, stride, pointer); +} + +inline void glTexCoordPointer( GLint size, GLenum type, GLsizei stride, const GLvoid * pointer) +{ + glVertexAttribPointer(pangolin::DEFAULT_LOCATION_TEXCOORD, size, type, GL_FALSE, stride, pointer); +} + +inline void glMatrixMode(GLenum mode) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + gl.currentmatrix = (mode == pangolin::GlProjectionStack) ? &gl.projection : &gl.modelview; +} + +inline void glLoadIdentity() +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + gl.currentmatrix->top() = pangolin::IdentityMatrix(); + gl.UpdateMatrices(); +} + +inline void glLoadMatrixf(const GLfloat* m) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + pangolin::GLprecision* cm = gl.currentmatrix->top().m; + for(int i=0; i<16; ++i) cm[i] = (pangolin::GLprecision)m[i]; + gl.UpdateMatrices(); +} + +inline void glLoadMatrixd(const GLdouble* m) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + pangolin::GLprecision* cm = gl.currentmatrix->top().m; + for(int i=0; i<16; ++i) cm[i] = (pangolin::GLprecision)m[i]; + gl.UpdateMatrices(); +} + +inline void glMultMatrixf(const GLfloat* m) +{ +// pangolin::GlEngine& gl = pangolin::glEngine(); +// float res[16]; +// pangolin::MatMul<4,4,4,float>(res, m, gl.currentmatrix->m ); +// std::memcpy(gl.currentmatrix->m, res, sizeof(float) * 16 ); + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); +} + +inline void glMultMatrixd(const GLdouble* m) +{ + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); +} + +inline void glPushMatrix(void) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + gl.currentmatrix->push(gl.currentmatrix->top()); +} + +inline void glPopMatrix(void) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + gl.currentmatrix->pop(); + gl.UpdateMatrices(); +} + +inline void glTranslatef(GLfloat x, GLfloat y, GLfloat z ) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + pangolin::GLprecision* cm = gl.currentmatrix->top().m; + cm[12] += x; + cm[13] += y; + cm[14] += z; + gl.UpdateMatrices(); +} + +inline void glOrtho( + GLdouble l, GLdouble r, + GLdouble b, GLdouble t, + GLdouble n, GLdouble f) +{ + pangolin::GlEngine& gl = pangolin::glEngine(); + gl.currentmatrix->top() = pangolin::ProjectionMatrixOrthographic(l,r,b,t,n,f); + gl.UpdateMatrices(); +} + +inline void glColor4f( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +{ + pangolin::glEngine().SetColor(red,green,blue,alpha); +} + +inline void glShadeModel( GLenum mode) +{ + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); +} + +inline void glPointSize(GLfloat size) +{ + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); +} + +inline void glTexEnvf( GLenum target, + GLenum pname, + GLfloat param) +{ + pango_print_error("Not Implemented: %s, %s, %d", __FUNCTION__, __FILE__, __LINE__); +} diff --git a/include/pangolin/gl/compat/gl_es_compat.h b/include/pangolin/gl/compat/gl_es_compat.h new file mode 100644 index 0000000..1c1fdb3 --- /dev/null +++ b/include/pangolin/gl/compat/gl_es_compat.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +#define GLdouble GLfloat +#define glClearDepth glClearDepthf +#define glFrustum glFrustumf + +#define glColor4fv(a) glColor4f(a[0], a[1], a[2], a[3]) +#define glColor3fv(a) glColor4f(a[0], a[1], a[2], 1.0f) +#define glColor3f(a,b,c) glColor4f(a, b, c, 1.0f) + +#define GL_CLAMP GL_CLAMP_TO_EDGE + +#ifdef HAVE_GLES_2 + #define glGenFramebuffersEXT glGenFramebuffers + #define glDeleteFramebuffersEXT glDeleteFramebuffers + #define glBindFramebufferEXT glBindFramebuffer + #define glDrawBuffers glDrawBuffers + #define glFramebufferTexture2DEXT glFramebufferTexture2D + #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER + #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT16 // <---- + #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0 + #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT +#else + #define glOrtho glOrthof + #define glGenFramebuffersEXT glGenFramebuffersOES + #define glDeleteFramebuffersEXT glDeleteFramebuffersOES + #define glBindFramebufferEXT glBindFramebufferOES + #define glDrawBuffers glDrawBuffersOES + #define glFramebufferTexture2DEXT glFramebufferTexture2DOES + #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES + #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES + #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES +#endif + +#define glGetDoublev glGetFloatv + +#ifdef HAVE_GLES_2 +#include +#endif + +inline void glRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) +{ + GLfloat verts[] = { x1,y1, x2,y1, x2,y2, x1,y2 }; + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, verts); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisableClientState(GL_VERTEX_ARRAY); +} + +inline void glRecti(int x1, int y1, int x2, int y2) +{ + GLfloat verts[] = { (float)x1,(float)y1, (float)x2,(float)y1, + (float)x2,(float)y2, (float)x1,(float)y2 }; + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, verts); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisableClientState(GL_VERTEX_ARRAY); +} diff --git a/include/pangolin/gl/gl.h b/include/pangolin/gl/gl.h new file mode 100644 index 0000000..e85e35c --- /dev/null +++ b/include/pangolin/gl/gl.h @@ -0,0 +1,246 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) //prevent including Eigen in cuda files +#define USE_EIGEN +#endif + +#ifdef USE_EIGEN +#include +#endif + +#include +#include +#include + +namespace pangolin +{ + +//////////////////////////////////////////////// +// Interface +//////////////////////////////////////////////// + +class PANGOLIN_EXPORT GlTexture +{ +public: + //! internal_format normally one of GL_RGBA8, GL_LUMINANCE8, GL_INTENSITY16 + GlTexture(GLint width, GLint height, GLint internal_format = GL_RGBA8, bool sampling_linear = true, int border = 0, GLenum glformat = GL_RGBA, GLenum gltype = GL_UNSIGNED_BYTE, GLvoid* data = NULL ); + + //! Move Constructor / asignment + GlTexture(GlTexture&& tex); + void operator=(GlTexture&& tex); + + //! Default constructor represents 'no texture' + GlTexture(); + virtual ~GlTexture(); + + bool IsValid() const; + + //! Delete OpenGL resources and fall back to representing 'no texture' + void Delete(); + + //! Reinitialise teture width / height / format + virtual void Reinitialise(GLsizei width, GLsizei height, GLint internal_format = GL_RGBA8, bool sampling_linear = true, int border = 0, GLenum glformat = GL_RGBA, GLenum gltype = GL_UNSIGNED_BYTE, GLvoid* data = NULL ); + + void Bind() const; + void Unbind() const; + + //! data_layout normally one of GL_LUMINANCE, GL_RGB, ... + //! data_type normally one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_FLOAT + void Upload(const void* image, GLenum data_format = GL_LUMINANCE, GLenum data_type = GL_FLOAT); + + //! Upload data to texture, overwriting a sub-region of it. + //! data ptr contains packed data_w x data_h of pixel data. + void Upload(const void* data, + GLsizei tex_x_offset, GLsizei tex_y_offset, + GLsizei data_w, GLsizei data_h, + GLenum data_format, GLenum data_type + ); + + void Load(const TypedImage& image, bool sampling_linear = true); + + void LoadFromFile(const std::string& filename, bool sampling_linear = true); + + void Download(void* image, GLenum data_layout = GL_LUMINANCE, GLenum data_type = GL_FLOAT) const; + + void Download(TypedImage& image) const; + + void Save(const std::string& filename, bool top_line_first = true); + + void SetLinear(); + void SetNearestNeighbour(); + + void RenderToViewport(const bool flip) const; + void RenderToViewport() const; + void RenderToViewport(Viewport tex_vp, bool flipx=false, bool flipy=false) const; + void RenderToViewportFlipY() const; + void RenderToViewportFlipXFlipY() const; + + GLint internal_format; + GLuint tid; + GLint width; + GLint height; + +private: + // Private copy constructor + GlTexture(const GlTexture&) {} +}; + +struct PANGOLIN_EXPORT GlRenderBuffer +{ + GlRenderBuffer(); + GlRenderBuffer(GLint width, GLint height, GLint internal_format = GL_DEPTH_COMPONENT24); + + void Reinitialise(GLint width, GLint height, GLint internal_format = GL_DEPTH_COMPONENT24); + + //! Move Constructor + GlRenderBuffer(GlRenderBuffer&& tex); + + ~GlRenderBuffer(); + + GLint width; + GLint height; + GLuint rbid; + +private: + // Private copy constructor + GlRenderBuffer(const GlRenderBuffer&) {} +}; + +struct PANGOLIN_EXPORT GlFramebuffer +{ + GlFramebuffer(); + ~GlFramebuffer(); + + GlFramebuffer(GlTexture& colour, GlRenderBuffer& depth); + GlFramebuffer(GlTexture& colour0, GlTexture& colour1, GlRenderBuffer& depth); + + void Bind() const; + void Unbind() const; + + void Reinitialise(); + + // Attach Colour texture to frame buffer + // Return attachment texture is bound to (e.g. GL_COLOR_ATTACHMENT0_EXT) + GLenum AttachColour(GlTexture& tex); + + // Attach Depth render buffer to frame buffer + void AttachDepth(GlRenderBuffer& rb); + + GLuint fbid; + unsigned attachments; +}; + +enum GlBufferType +{ + GlArrayBuffer = GL_ARRAY_BUFFER, // VBO's, CBO's, NBO's + GlElementArrayBuffer = GL_ELEMENT_ARRAY_BUFFER, // IBO's +#ifndef HAVE_GLES + GlPixelPackBuffer = GL_PIXEL_PACK_BUFFER, // PBO's + GlPixelUnpackBuffer = GL_PIXEL_UNPACK_BUFFER, + GlShaderStorageBuffer = GL_SHADER_STORAGE_BUFFER +#endif +}; + +struct PANGOLIN_EXPORT GlBuffer +{ + //! Default constructor represents 'no buffer' + GlBuffer(); + GlBuffer(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse = GL_DYNAMIC_DRAW ); + + //! Move Constructor / Assignment + GlBuffer(GlBuffer&& tex); + void operator=(GlBuffer&& tex); + + ~GlBuffer(); + + bool IsValid() const; + + size_t SizeBytes() const; + + void Reinitialise(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse ); + void Reinitialise(GlBuffer const& other ); + void Resize(GLuint num_elements); + + void Bind() const; + void Unbind() const; + void Upload(const GLvoid* data, GLsizeiptr size_bytes, GLintptr offset = 0); + void Download(GLvoid* ptr, GLsizeiptr size_bytes, GLintptr offset = 0) const; + + GLuint bo; + GlBufferType buffer_type; + GLenum gluse; + + GLenum datatype; + GLuint num_elements; + GLuint count_per_element; +private: + GlBuffer(const GlBuffer&) {} +}; + +class PANGOLIN_EXPORT GlSizeableBuffer + : public pangolin::GlBuffer +{ +public: + GlSizeableBuffer(pangolin::GlBufferType buffer_type, GLuint initial_num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse = GL_DYNAMIC_DRAW ); + + void Clear(); + +#ifdef USE_EIGEN + template + void Add(const Eigen::DenseBase& vec); + + template + void Update(const Eigen::DenseBase& vec, size_t position = 0); +#endif + + size_t start() const; + + size_t size() const; + +protected: + void CheckResize(size_t num_verts); + + size_t NextSize(size_t min_size) const; + + size_t m_num_verts; +}; + +size_t GlFormatChannels(GLenum data_layout); + +size_t GlDataTypeBytes(GLenum type); + +} + +// Include implementation +#include diff --git a/include/pangolin/gl/gl.hpp b/include/pangolin/gl/gl.hpp new file mode 100644 index 0000000..2cfeee9 --- /dev/null +++ b/include/pangolin/gl/gl.hpp @@ -0,0 +1,761 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace pangolin +{ + +//////////////////////////////////////////////// +// Implementation of gl.h +//////////////////////////////////////////////// + +#ifndef HAVE_GLES +const int MAX_ATTACHMENTS = 8; +const static GLuint attachment_buffers[] = { + GL_COLOR_ATTACHMENT0_EXT, + GL_COLOR_ATTACHMENT1_EXT, + GL_COLOR_ATTACHMENT2_EXT, + GL_COLOR_ATTACHMENT3_EXT, + GL_COLOR_ATTACHMENT4_EXT, + GL_COLOR_ATTACHMENT5_EXT, + GL_COLOR_ATTACHMENT6_EXT, + GL_COLOR_ATTACHMENT7_EXT +}; +#else // HAVE_GLES +const int MAX_ATTACHMENTS = 1; +const static GLuint attachment_buffers[] = { + GL_COLOR_ATTACHMENT0_EXT +}; +#endif // HAVE_GLES + +const static size_t datatype_bytes[] = { + 1, // #define GL_BYTE 0x1400 + 1, // #define GL_UNSIGNED_BYTE 0x1401 + 2, // #define GL_SHORT 0x1402 + 2, // #define GL_UNSIGNED_SHORT 0x1403 + 4, // #define GL_INT 0x1404 + 4, // #define GL_UNSIGNED_INT 0x1405 + 4, // #define GL_FLOAT 0x1406 + 2, // #define GL_2_BYTES 0x1407 + 3, // #define GL_3_BYTES 0x1408 + 4, // #define GL_4_BYTES 0x1409 + 8 // #define GL_DOUBLE 0x140A +}; + +const static size_t format_channels[] = { + 1, // #define GL_RED 0x1903 + 1, // #define GL_GREEN 0x1904 + 1, // #define GL_BLUE 0x1905 + 1, // #define GL_ALPHA 0x1906 + 3, // #define GL_RGB 0x1907 + 4, // #define GL_RGBA 0x1908 + 1, // #define GL_LUMINANCE 0x1909 + 2 // #define GL_LUMINANCE_ALPHA 0x190A +}; + +inline size_t GlDataTypeBytes(GLenum type) +{ + return datatype_bytes[type - GL_BYTE]; +} + +inline size_t GlFormatChannels(GLenum data_layout) +{ + return format_channels[data_layout - GL_RED]; +} + +//template +//struct GlDataTypeTrait {}; +//template<> struct GlDataTypeTrait{ static const GLenum type = GL_FLOAT; }; +//template<> struct GlDataTypeTrait{ static const GLenum type = GL_INT; }; +//template<> struct GlDataTypeTrait{ static const GLenum type = GL_UNSIGNED_BYTE; }; + +inline GlTexture::GlTexture() + : internal_format(0), tid(0), width(0), height(0) +{ + // Not a texture constructor +} + +inline GlTexture::GlTexture(GLint width, GLint height, GLint internal_format, bool sampling_linear, int border, GLenum glformat, GLenum gltype, GLvoid* data ) + : internal_format(0), tid(0) +{ + Reinitialise(width,height,internal_format,sampling_linear,border,glformat,gltype,data); +} + +inline GlTexture::GlTexture(GlTexture&& tex) +{ + *this = std::move(tex); +} +inline void GlTexture::operator=(GlTexture&& tex) +{ + internal_format = tex.internal_format; + tid = tex.tid; + + tex.internal_format = 0; + tex.tid = 0; +} + +inline bool GlTexture::IsValid() const +{ + return tid != 0; +} + +inline void GlTexture::Delete() +{ + // We have no GL context whilst exiting. + if(internal_format!=0 && !pangolin::ShouldQuit() ) { + glDeleteTextures(1,&tid); + internal_format = 0; + tid = 0; + width = 0; + height = 0; + } +} + +inline GlTexture::~GlTexture() +{ + // We have no GL context whilst exiting. + if(internal_format!=0 && !pangolin::ShouldQuit() ) { + glDeleteTextures(1,&tid); + } +} + +inline void GlTexture::Bind() const +{ + glBindTexture(GL_TEXTURE_2D, tid); +} + +inline void GlTexture::Unbind() const +{ + glBindTexture(GL_TEXTURE_2D, 0); +} + +inline void GlTexture::Reinitialise(GLsizei w, GLsizei h, GLint int_format, bool sampling_linear, int border, GLenum glformat, GLenum gltype, GLvoid* data ) +{ + if(tid!=0) { + glDeleteTextures(1,&tid); + } + + internal_format = int_format; + width = w; + height = h; + + glGenTextures(1,&tid); + Bind(); + + // GL_LUMINANCE and GL_FLOAT don't seem to actually affect buffer, but some values are required + // for call to succeed. + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, border, glformat, gltype, data); + + if(sampling_linear) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + }else{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + CheckGlDieOnError(); +} + +inline void GlTexture::Upload( + const void* data, + GLenum data_format, GLenum data_type +) { + Bind(); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,data_format,data_type,data); + CheckGlDieOnError(); +} + +inline void GlTexture::Upload( + const void* data, + GLsizei tex_x_offset, GLsizei tex_y_offset, + GLsizei data_w, GLsizei data_h, + GLenum data_format, GLenum data_type ) +{ + Bind(); + glTexSubImage2D(GL_TEXTURE_2D,0,tex_x_offset,tex_y_offset,data_w,data_h,data_format,data_type,data); + CheckGlDieOnError(); +} + +inline void GlTexture::Load(const TypedImage& image, bool sampling_linear) +{ + GlPixFormat fmt(image.fmt); + Reinitialise((GLint)image.w, (GLint)image.h, GL_RGBA32F, sampling_linear, 0, fmt.glformat, fmt.gltype, image.ptr ); +} + +inline void GlTexture::LoadFromFile(const std::string& filename, bool sampling_linear) +{ + TypedImage image = LoadImage(filename); + Load(image, sampling_linear); +} + +#ifndef HAVE_GLES +inline void GlTexture::Download(void* image, GLenum data_layout, GLenum data_type) const +{ + Bind(); + glGetTexImage(GL_TEXTURE_2D, 0, data_layout, data_type, image); + Unbind(); +} + +inline void GlTexture::Download(TypedImage& image) const +{ + switch (internal_format) + { + case GL_LUMINANCE8: + image.Reinitialise(width, height, PixelFormatFromString("GRAY8") ); + Download(image.ptr, GL_LUMINANCE, GL_UNSIGNED_BYTE); + break; + case GL_LUMINANCE16: + image.Reinitialise(width, height, PixelFormatFromString("GRAY16LE") ); + Download(image.ptr, GL_LUMINANCE, GL_UNSIGNED_SHORT); + break; + case GL_RGB8: + image.Reinitialise(width, height, PixelFormatFromString("RGB24")); + Download(image.ptr, GL_RGB, GL_UNSIGNED_BYTE); + break; + case GL_RGBA8: + image.Reinitialise(width, height, PixelFormatFromString("RGBA32")); + Download(image.ptr, GL_RGBA, GL_UNSIGNED_BYTE); + break; + case GL_LUMINANCE: + case GL_LUMINANCE32F_ARB: + image.Reinitialise(width, height, PixelFormatFromString("GRAY32F")); + Download(image.ptr, GL_LUMINANCE, GL_FLOAT); + break; + case GL_RGB: + case GL_RGB32F: + image.Reinitialise(width, height, PixelFormatFromString("RGB96F")); + Download(image.ptr, GL_RGB, GL_FLOAT); + break; + case GL_RGBA: + case GL_RGBA32F: + image.Reinitialise(width, height, PixelFormatFromString("RGBA128F")); + Download(image.ptr, GL_RGBA, GL_FLOAT); + break; + default: + throw std::runtime_error( + "GlTexture::Download - Unknown internal format (" + + pangolin::Convert::Do(internal_format) + + ")" + ); + } + +} + +inline void GlTexture::Save(const std::string& filename, bool top_line_first) +{ + TypedImage image; + Download(image); + pangolin::SaveImage(image, filename, top_line_first); +} +#endif // HAVE_GLES + +inline void GlTexture::SetLinear() +{ + Bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + Unbind(); +} + +inline void GlTexture::SetNearestNeighbour() +{ + Bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + Unbind(); +} + +inline void GlTexture::RenderToViewport(const bool flip) const +{ + if(flip) { + RenderToViewportFlipY(); + }else{ + RenderToViewport(); + } +} + +inline void GlTexture::RenderToViewport() const +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 }; + glVertexPointer(2, GL_FLOAT, 0, sq_vert); + glEnableClientState(GL_VERTEX_ARRAY); + + GLfloat sq_tex[] = { 0,0, 1,0, 1,1, 0,1 }; + glTexCoordPointer(2, GL_FLOAT, 0, sq_tex); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + Bind(); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(GL_TEXTURE_2D); +} + +inline void GlTexture::RenderToViewport(Viewport tex_vp, bool flipx, bool flipy) const +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 }; + glVertexPointer(2, GL_FLOAT, 0, sq_vert); + glEnableClientState(GL_VERTEX_ARRAY); + + GLfloat l = tex_vp.l / (float)(width); + GLfloat b = tex_vp.b / (float)(height); + GLfloat r = (tex_vp.l+tex_vp.w) / (float)(width); + GLfloat t = (tex_vp.b+tex_vp.h) / (float)(height); + + if(flipx) std::swap(l,r); + if(flipy) std::swap(b,t); + + GLfloat sq_tex[] = { l,b, r,b, r,t, l,t }; + glTexCoordPointer(2, GL_FLOAT, 0, sq_tex); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + Bind(); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(GL_TEXTURE_2D); +} + +inline void GlTexture::RenderToViewportFlipY() const +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 }; + glVertexPointer(2, GL_FLOAT, 0, sq_vert); + glEnableClientState(GL_VERTEX_ARRAY); + + GLfloat sq_tex[] = { 0,1, 1,1, 1,0, 0,0 }; + glTexCoordPointer(2, GL_FLOAT, 0, sq_tex); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + Bind(); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(GL_TEXTURE_2D); +} + +inline void GlTexture::RenderToViewportFlipXFlipY() const +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + GLfloat sq_vert[] = { 1,1, -1,1, -1,-1, 1,-1 }; + glVertexPointer(2, GL_FLOAT, 0, sq_vert); + glEnableClientState(GL_VERTEX_ARRAY); + + GLfloat sq_tex[] = { 0,0, 1,0, 1,1, 0,1 }; + glTexCoordPointer(2, GL_FLOAT, 0, sq_tex); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + Bind(); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(GL_TEXTURE_2D); +} + +//////////////////////////////////////////////////////////////////////////// + +inline GlRenderBuffer::GlRenderBuffer() + : width(0), height(0), rbid(0) +{ +} + +inline GlRenderBuffer::GlRenderBuffer(GLint width, GLint height, GLint internal_format ) + : width(0), height(0), rbid(0) +{ + Reinitialise(width,height,internal_format); +} + +#ifndef HAVE_GLES +inline void GlRenderBuffer::Reinitialise(GLint width, GLint height, GLint internal_format) +{ + if( this->width != 0 ) { + glDeleteRenderbuffersEXT(1, &rbid); + } + + this->width = width; + this->height = height; + glGenRenderbuffersEXT(1, &rbid); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rbid); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internal_format, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); +} + +inline GlRenderBuffer::~GlRenderBuffer() +{ + // We have no GL context whilst exiting. + if( width!=0 && !pangolin::ShouldQuit() ) { + glDeleteRenderbuffersEXT(1, &rbid); + } +} +#else +inline void GlRenderBuffer::Reinitialise(GLint width, GLint height, GLint internal_format) +{ + if( width!=0 ) { + glDeleteTextures(1, &rbid); + } + + // Use a texture instead... + glGenTextures(1, &rbid); + glBindTexture(GL_TEXTURE_2D, rbid); + + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, + width, height, + 0, internal_format, GL_UNSIGNED_SHORT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +} + +inline GlRenderBuffer::~GlRenderBuffer() +{ + // We have no GL context whilst exiting. + if( width!=0 && !pangolin::ShouldQuit() ) { + glDeleteTextures(1, &rbid); + } +} +#endif // HAVE_GLES + +inline GlRenderBuffer::GlRenderBuffer(GlRenderBuffer&& tex) + : width(tex.width), height(tex.height), rbid(tex.rbid) +{ + tex.rbid = tex.width = tex.height = 0; +} + +//////////////////////////////////////////////////////////////////////////// + +inline GlFramebuffer::GlFramebuffer() + : fbid(0), attachments(0) +{ +} + +inline GlFramebuffer::~GlFramebuffer() +{ + if(fbid) { + glDeleteFramebuffersEXT(1, &fbid); + } +} + +inline GlFramebuffer::GlFramebuffer(GlTexture& colour, GlRenderBuffer& depth) + : attachments(0) +{ + glGenFramebuffersEXT(1, &fbid); + AttachColour(colour); + AttachDepth(depth); + CheckGlDieOnError(); +} + +inline GlFramebuffer::GlFramebuffer(GlTexture& colour0, GlTexture& colour1, GlRenderBuffer& depth) + : attachments(0) +{ + glGenFramebuffersEXT(1, &fbid); + AttachColour(colour0); + AttachColour(colour1); + AttachDepth(depth); + CheckGlDieOnError(); +} + +inline void GlFramebuffer::Bind() const +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbid); +#ifndef HAVE_GLES + glDrawBuffers( attachments, attachment_buffers ); +#endif +} + +inline void GlFramebuffer::Reinitialise() +{ + if(fbid) { + glDeleteFramebuffersEXT(1, &fbid); + } + glGenFramebuffersEXT(1, &fbid); +} + +inline void GlFramebuffer::Unbind() const +{ +#ifndef HAVE_GLES + glDrawBuffers( 1, attachment_buffers ); +#endif + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +} + +inline GLenum GlFramebuffer::AttachColour(GlTexture& tex ) +{ + if(!fbid) Reinitialise(); + + const GLenum color_attachment = GL_COLOR_ATTACHMENT0_EXT + attachments; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbid); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, color_attachment, GL_TEXTURE_2D, tex.tid, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + attachments++; + CheckGlDieOnError(); + return color_attachment; +} + +inline void GlFramebuffer::AttachDepth(GlRenderBuffer& rb ) +{ + if(!fbid) Reinitialise(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbid); +#if !defined(HAVE_GLES) + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rb.rbid); +#elif defined(HAVE_GLES_2) + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, rb.rbid, 0); +#else + throw std::exception(); +#endif + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + CheckGlDieOnError(); +} + +//////////////////////////////////////////////////////////////////////////// + +inline GlBuffer::GlBuffer() + : bo(0), num_elements(0) +{ +} + +inline GlBuffer::GlBuffer(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse ) + : bo(0), num_elements(0) +{ + Reinitialise(buffer_type, num_elements, datatype, count_per_element, gluse ); +} + +inline GlBuffer::GlBuffer(GlBuffer&& buffer) +{ + *this = std::move(buffer); +} + +inline void GlBuffer::operator=(GlBuffer&& buffer) +{ + bo = buffer.bo; + buffer_type = buffer.buffer_type; + gluse = buffer.gluse; + datatype = buffer.datatype; + num_elements = buffer.num_elements; + count_per_element = buffer.count_per_element; + buffer.bo = 0; +} + +inline bool GlBuffer::IsValid() const +{ + return bo != 0; +} + +inline size_t GlBuffer::SizeBytes() const +{ + return num_elements * GlDataTypeBytes(datatype) * count_per_element; +} + +inline void GlBuffer::Reinitialise(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse ) +{ + this->buffer_type = buffer_type; + this->gluse = gluse; + this->datatype = datatype; + this->num_elements = num_elements; + this->count_per_element = count_per_element; + + if(!bo) { + glGenBuffers(1, &bo); + } + + Bind(); + glBufferData(buffer_type, num_elements*GlDataTypeBytes(datatype)*count_per_element, 0, gluse); + Unbind(); +} + +inline void GlBuffer::Reinitialise(GlBuffer const& other ) +{ + Reinitialise(other.buffer_type, other.num_elements, other.datatype, other.count_per_element, other.gluse); +} + +inline void GlBuffer::Resize(GLuint new_num_elements) +{ + if(bo!=0) { +#ifndef HAVE_GLES + // Backup current data, reinit memory, restore old data + const size_t backup_elements = std::min(new_num_elements,num_elements); + const size_t backup_size_bytes = backup_elements*GlDataTypeBytes(datatype)*count_per_element; + unsigned char* backup = new unsigned char[backup_size_bytes]; + Bind(); + glGetBufferSubData(buffer_type, 0, backup_size_bytes, backup); + glBufferData(buffer_type, new_num_elements*GlDataTypeBytes(datatype)*count_per_element, 0, gluse); + glBufferSubData(buffer_type, 0, backup_size_bytes, backup); + Unbind(); + delete[] backup; +#else + throw std::exception(); +#endif + }else{ + Reinitialise(buffer_type, new_num_elements, datatype, count_per_element, gluse); + } + num_elements = new_num_elements; +} + +inline GlBuffer::~GlBuffer() +{ + if(bo!=0) { + glDeleteBuffers(1, &bo); + } +} + +inline void GlBuffer::Bind() const +{ + glBindBuffer(buffer_type, bo); +} + +inline void GlBuffer::Unbind() const +{ + glBindBuffer(buffer_type, 0); +} + +inline void GlBuffer::Upload(const GLvoid* data, GLsizeiptr size_bytes, GLintptr offset) +{ + Bind(); + glBufferSubData(buffer_type,offset,size_bytes, data); + Unbind(); +} + +inline void GlBuffer::Download(GLvoid* data, GLsizeiptr size_bytes, GLintptr offset) const +{ + Bind(); + glGetBufferSubData(buffer_type, offset, size_bytes, data); + Unbind(); +} + +//////////////////////////////////////////////////////////////////////////// + +inline GlSizeableBuffer::GlSizeableBuffer(GlBufferType buffer_type, GLuint initial_num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse ) + : GlBuffer(buffer_type, initial_num_elements, datatype, count_per_element, gluse), m_num_verts(0) +{ + +} + +inline void GlSizeableBuffer::Clear() +{ + m_num_verts = 0; +} + +#ifdef USE_EIGEN +template inline +void GlSizeableBuffer::Add(const Eigen::DenseBase& vec) +{ + typedef typename Eigen::DenseBase::Scalar Scalar; + assert(vec.rows()==GlBuffer::count_per_element); + CheckResize(m_num_verts + 1); + // TODO: taking address of first element is really dodgey. Need to work out + // when this is okay! + Upload(&vec(0,0), sizeof(Scalar)*vec.rows()*vec.cols(), sizeof(Scalar)*vec.rows()*m_num_verts); + m_num_verts += vec.cols(); +} + +template inline +void GlSizeableBuffer::Update(const Eigen::DenseBase& vec, size_t position ) +{ + typedef typename Eigen::DenseBase::Scalar Scalar; + assert(vec.rows()==GlBuffer::count_per_element); + CheckResize(position + vec.cols() ); + // TODO: taking address of first element is really dodgey. Need to work out + // when this is okay! + Upload(&vec(0,0), sizeof(Scalar)*vec.rows()*vec.cols(), sizeof(Scalar)*vec.rows()*position ); + m_num_verts = std::max(position+vec.cols(), m_num_verts); +} +#endif + +inline size_t GlSizeableBuffer::start() const { + return 0; +} + +inline size_t GlSizeableBuffer::size() const { + return m_num_verts; +} + +inline void GlSizeableBuffer::CheckResize(size_t num_verts) +{ + if( num_verts > GlBuffer::num_elements) { + const size_t new_size = NextSize(num_verts); + GlBuffer::Resize((GLuint)new_size); + } +} + +inline size_t GlSizeableBuffer::NextSize(size_t min_size) const +{ + size_t new_size = std::max(GlBuffer::num_elements, 1u); + while(new_size < min_size) { + new_size *= 2; + } + return new_size; +} + +} diff --git a/include/pangolin/gl/glchar.h b/include/pangolin/gl/glchar.h new file mode 100644 index 0000000..000d0da --- /dev/null +++ b/include/pangolin/gl/glchar.h @@ -0,0 +1,78 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin { + +struct PANGOLIN_EXPORT XYUV +{ + XYUV() {} + XYUV(GLfloat x, GLfloat y, GLfloat tu, GLfloat tv) + : x(x), y(y), tu(tu), tv(tv) {} + + XYUV operator+(float dx) const { + return XYUV(x+dx,y,tu,tv); + } + + GLfloat x, y, tu, tv; +}; + +class PANGOLIN_EXPORT GlChar +{ +public: + GlChar(); + GlChar(int tw, int th, int x, int y, int w, int h, GLfloat x_step, GLfloat ox, GLfloat oy); + + inline const XYUV& GetVert(size_t i) const { + return vs[i]; + } + + inline GLfloat StepX() const { + return x_step; + } + + inline GLfloat YMin() const { + return y_min; + } + + inline GLfloat YMax() const { + return y_max; + } + + void Draw() const; + +protected: + XYUV vs[4]; + GLfloat x_step; + GLfloat y_min, y_max; +}; + +} diff --git a/include/pangolin/gl/glcuda.h b/include/pangolin/gl/glcuda.h new file mode 100644 index 0000000..9fbf141 --- /dev/null +++ b/include/pangolin/gl/glcuda.h @@ -0,0 +1,258 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include "gl.h" + +namespace pangolin +{ + +//////////////////////////////////////////////// +// Interface +//////////////////////////////////////////////// + +struct GlBufferCudaPtr : public GlBuffer +{ + //! Default constructor represents 'no buffer' + GlBufferCudaPtr(); + + GlBufferCudaPtr(GlBufferType buffer_type, GLuint size_bytes, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ); + GlBufferCudaPtr(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ); + + PANGOLIN_DEPRECATED + GlBufferCudaPtr(GlBufferType buffer_type, GLuint width, GLuint height, GLenum datatype, GLuint count_per_element, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ); + + ~GlBufferCudaPtr(); + + void Reinitialise(GlBufferType buffer_type, GLuint size_bytes, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ); + void Reinitialise(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ); + + /** + * Use parameters from another @c GlBufferCudaPtr to initialize this buffer. + */ + void Reinitialise(const GlBufferCudaPtr& other); + + unsigned int cuda_use; + cudaGraphicsResource* cuda_res; +}; + +struct GlTextureCudaArray : GlTexture +{ + GlTextureCudaArray(); + // Some internal_formats aren't accepted. I have trouble with GL_RGB8 + GlTextureCudaArray(int width, int height, GLint internal_format, bool sampling_linear = true, int border = 0, GLenum glformat = GL_RGBA, GLenum gltype = GL_UNSIGNED_BYTE, GLvoid* data = NULL); + ~GlTextureCudaArray(); + + void Reinitialise(int width, int height, GLint internal_format, bool sampling_linear = true, int border = 0, GLenum glformat = GL_RGBA, GLenum gltype = GL_UNSIGNED_BYTE, GLvoid* data = NULL) override; + cudaGraphicsResource* cuda_res; +}; + +struct CudaScopedMappedPtr +{ + CudaScopedMappedPtr(const GlBufferCudaPtr& buffer); + ~CudaScopedMappedPtr(); + void* operator*(); + cudaGraphicsResource* res; + +private: + CudaScopedMappedPtr(const CudaScopedMappedPtr&) {} +}; + +struct CudaScopedMappedArray +{ + CudaScopedMappedArray(const GlTextureCudaArray& tex); + ~CudaScopedMappedArray(); + cudaArray* operator*(); + cudaGraphicsResource* res; + +private: + CudaScopedMappedArray(const CudaScopedMappedArray&) {} +}; + +void CopyPboToTex(GlBufferCudaPtr& buffer, GlTexture& tex); + +void swap(GlBufferCudaPtr& a, GlBufferCudaPtr& b); + +//////////////////////////////////////////////// +// Implementation +//////////////////////////////////////////////// + +inline GlBufferCudaPtr::GlBufferCudaPtr() + : cuda_res(0) +{ +} + +inline GlBufferCudaPtr::GlBufferCudaPtr(GlBufferType buffer_type, GLuint size_bytes, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ) + : cuda_res(0) +{ + Reinitialise(buffer_type, size_bytes, cudause, gluse); +} + +inline GlBufferCudaPtr::GlBufferCudaPtr(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, unsigned int cudause, GLenum gluse ) + : cuda_res(0) +{ + Reinitialise(buffer_type, num_elements, datatype, count_per_element, cudause, gluse); +} + +inline GlBufferCudaPtr::GlBufferCudaPtr(GlBufferType buffer_type, GLuint width, GLuint height, GLenum datatype, GLuint count_per_element, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ) + : cuda_res(0) +{ + Reinitialise(buffer_type, width*height, datatype, count_per_element, cudause, gluse); +} + +inline GlBufferCudaPtr::~GlBufferCudaPtr() +{ + if(cuda_res) { + cudaGraphicsUnregisterResource(cuda_res); + } +} + +inline void GlBufferCudaPtr::Reinitialise(GlBufferType buffer_type, GLuint size_bytes, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ) +{ + GlBufferCudaPtr::Reinitialise(buffer_type, size_bytes, GL_BYTE, 1, cudause, gluse); +} + +inline void GlBufferCudaPtr::Reinitialise(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, unsigned int cudause /*= cudaGraphicsMapFlagsNone*/, GLenum gluse /*= GL_DYNAMIC_DRAW*/ ) +{ + if(cuda_res) { + cudaGraphicsUnregisterResource(cuda_res); + } + GlBuffer::Reinitialise(buffer_type, num_elements, datatype, count_per_element, gluse); + + cuda_use = cudause; + cudaGraphicsGLRegisterBuffer( &cuda_res, bo, cudause ); +} + +inline void GlBufferCudaPtr::Reinitialise(const GlBufferCudaPtr& other) +{ + Reinitialise(other.buffer_type, other.num_elements, other.datatype, other.count_per_element, other.cuda_use, other.gluse); +} + +inline GlTextureCudaArray::GlTextureCudaArray() + : GlTexture(), cuda_res(0) +{ + // Not a texture +} + +inline GlTextureCudaArray::GlTextureCudaArray(int width, int height, GLint internal_format, bool sampling_linear, int border, GLenum glformat, GLenum gltype, GLvoid *data) + :GlTexture(width,height,internal_format, sampling_linear, border, glformat, gltype, data) +{ + // TODO: specify flags too + const cudaError_t err = cudaGraphicsGLRegisterImage(&cuda_res, tid, GL_TEXTURE_2D, cudaGraphicsMapFlagsNone); + if( err != cudaSuccess ) { + std::cout << "cudaGraphicsGLRegisterImage failed: " << err << std::endl; + } +} + +inline GlTextureCudaArray::~GlTextureCudaArray() +{ + if(cuda_res) { + cudaGraphicsUnregisterResource(cuda_res); + } +} + +inline void GlTextureCudaArray::Reinitialise(int width, int height, GLint internal_format, bool sampling_linear, int border, GLenum glformat, GLenum gltype, GLvoid* data) +{ + if(cuda_res) { + cudaGraphicsUnregisterResource(cuda_res); + } + + GlTexture::Reinitialise(width, height, internal_format, sampling_linear, border, glformat, gltype, data); + + const cudaError_t err = cudaGraphicsGLRegisterImage(&cuda_res, tid, GL_TEXTURE_2D, cudaGraphicsMapFlagsNone); + if( err != cudaSuccess ) { + std::cout << "cudaGraphicsGLRegisterImage failed: " << err << std::endl; + } +} + +inline CudaScopedMappedPtr::CudaScopedMappedPtr(const GlBufferCudaPtr& buffer) + : res(buffer.cuda_res) +{ + cudaGraphicsMapResources(1, &res, 0); +} + +inline CudaScopedMappedPtr::~CudaScopedMappedPtr() +{ + cudaGraphicsUnmapResources(1, &res, 0); +} + +inline void* CudaScopedMappedPtr::operator*() +{ + size_t num_bytes; + void* d_ptr; + cudaGraphicsResourceGetMappedPointer(&d_ptr,&num_bytes,res); + return d_ptr; +} + +inline CudaScopedMappedArray::CudaScopedMappedArray(const GlTextureCudaArray& tex) + : res(tex.cuda_res) +{ + cudaGraphicsMapResources(1, &res); +} + +inline CudaScopedMappedArray::~CudaScopedMappedArray() +{ + cudaGraphicsUnmapResources(1, &res); +} + +inline cudaArray* CudaScopedMappedArray::operator*() +{ + cudaArray* array; + cudaGraphicsSubResourceGetMappedArray(&array, res, 0, 0); + return array; +} + +inline void CopyPboToTex(const GlBufferCudaPtr& buffer, GlTexture& tex, GLenum buffer_layout, GLenum buffer_data_type ) +{ + buffer.Bind(); + tex.Bind(); + glTexImage2D(GL_TEXTURE_2D, 0, tex.internal_format, tex.width, tex.height, 0, buffer_layout, buffer_data_type, 0); + buffer.Unbind(); + tex.Unbind(); +} + +template +inline void CopyDevMemtoTex(T* d_img, size_t pitch, GlTextureCudaArray& tex ) +{ + CudaScopedMappedArray arr_tex(tex); + cudaMemcpy2DToArray(*arr_tex, 0, 0, d_img, pitch, tex.width*sizeof(T), tex.height, cudaMemcpyDeviceToDevice ); +} + +inline void swap(GlBufferCudaPtr& a, GlBufferCudaPtr& b) +{ + std::swap(a.bo, b.bo); + std::swap(a.cuda_res, b.cuda_res); + std::swap(a.buffer_type, b.buffer_type); +} + + +} diff --git a/include/pangolin/gl/gldraw.h b/include/pangolin/gl/gldraw.h new file mode 100644 index 0000000..611a3b4 --- /dev/null +++ b/include/pangolin/gl/gldraw.h @@ -0,0 +1,518 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) //prevent including Eigen in cuda files +#define USE_EIGEN +#endif + +#ifdef USE_EIGEN +#include +#include +#endif // USE_EIGEN + +namespace pangolin +{ + +// h [0,360) +// s [0,1] +// v [0,1] +inline void glColorHSV( GLfloat hue, GLfloat s=1.0f, GLfloat v=1.0f ) +{ + const GLfloat h = hue / 60.0f; + const int i = (int)floor(h); + const GLfloat f = (i%2 == 0) ? 1-(h-i) : h-i; + const GLfloat m = v * (1-s); + const GLfloat n = v * (1-s*f); + switch(i) + { + case 0: glColor4f(v,n,m,1); break; + case 1: glColor4f(n,v,m,1); break; + case 2: glColor4f(m,v,n,1); break; + case 3: glColor4f(m,n,v,1); break; + case 4: glColor4f(n,m,v,1); break; + case 5: glColor4f(v,m,n,1); break; + default: + break; + } +} + +inline void glColorBin( int bin, int max_bins, GLfloat sat=1.0f, GLfloat val=1.0f ) +{ + if( bin >= 0 ) { + const GLfloat hue = (GLfloat)(bin%max_bins) * 360.0f / (GLfloat)max_bins; + glColorHSV(hue,sat,val); + }else{ + glColor4f(1,1,1,1); + } +} + +template +inline void glDrawVertices( + size_t num_vertices, const T* const vertex_ptr, GLenum mode, + size_t elements_per_vertex = GlFormatTraits::components, + size_t vertex_stride_bytes = 0 ) +{ + if(num_vertices > 0) + { + PANGO_ENSURE(vertex_ptr != nullptr); + PANGO_ENSURE(mode != GL_LINES || num_vertices % 2 == 0, "number of vertices (%) must be even in GL_LINES mode", num_vertices ); + + glVertexPointer(elements_per_vertex, GlFormatTraits::gltype, vertex_stride_bytes, vertex_ptr); + glEnableClientState(GL_VERTEX_ARRAY); + glDrawArrays(mode, 0, num_vertices); + glDisableClientState(GL_VERTEX_ARRAY); + } +} + +template +inline void glDrawColoredVertices( + size_t num_vertices, const TV* const vertex_ptr, const TC* const color_ptr, GLenum mode, + size_t elements_per_vertex = GlFormatTraits::components, + size_t elements_per_color = GlFormatTraits::components, + size_t vertex_stride_bytes = 0, + size_t color_stride_bytes = 0 +) { + if(color_ptr) { + glColorPointer(elements_per_color, GlFormatTraits::gltype, color_stride_bytes, color_ptr); + glEnableClientState(GL_COLOR_ARRAY); + glDrawVertices(num_vertices, vertex_ptr, mode, elements_per_vertex, vertex_stride_bytes); + glDisableClientState(GL_COLOR_ARRAY); + }else{ + glDrawVertices(num_vertices, vertex_ptr, mode, elements_per_vertex, vertex_stride_bytes); + } +} + +inline void glDrawLine( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) +{ + const GLfloat verts[] = { x1,y1, x2,y2 }; + glDrawVertices(2, verts, GL_LINES, 2); +} + +inline void glDrawLine( GLfloat x1, GLfloat y1, GLfloat z1, GLfloat x2, GLfloat y2, GLfloat z2) +{ + const GLfloat verts[] = { x1,y1,z1, x2,y2,z2 }; + glDrawVertices(2, verts, GL_LINES, 3); +} + +inline void glDrawCross( GLfloat x, GLfloat y, GLfloat rad ) +{ + const GLfloat verts[] = { x-rad,y, x+rad, y, x,y-rad, x, y+rad}; + glDrawVertices(4, verts, GL_LINES, 2); +} + +inline void glDrawCross( GLfloat x, GLfloat y, GLfloat z, GLfloat rad ) +{ + const GLfloat verts[] = { x-rad,y,z, x+rad,y,z, x,y-rad,z, x,y+rad,z, x,y,z-rad, x,y,z+rad }; + glDrawVertices(6, verts, GL_LINES, 3); +} + +inline void glDrawAxis(float s) +{ + const GLfloat cols[] = { 1,0,0, 1,0,0, 0,1,0, 0,1,0, 0,0,1, 0,0,1 }; + const GLfloat verts[] = { 0,0,0, s,0,0, 0,0,0, 0,s,0, 0,0,0, 0,0,s }; + glDrawColoredVertices(6, verts, cols, GL_LINES, 3, 3); +} + +inline void glDrawRect( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLenum mode = GL_TRIANGLE_FAN ) +{ + const GLfloat verts[] = { x1,y1, x2,y1, x2,y2, x1,y2 }; + glDrawVertices(4, verts, mode, 2); +} + +inline void glDrawRectPerimeter( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 ) +{ + glDrawRect(x1,y1, x2,y2, GL_LINE_LOOP); +} + +inline void glDrawCirclePerimeter( float x, float y, float rad ) +{ + const int N = 50; + GLfloat verts[N*2]; + + const float TAU_DIV_N = 2*(float)M_PI/N; + for(int i = 0; i < N*2; i+=2) { + verts[i] = x + rad * cos(i*TAU_DIV_N); + verts[i+1] = y + rad * sin(i*TAU_DIV_N); + } + + glDrawVertices(N, verts, GL_LINES, 2); +} + +inline void glDrawCircle( GLfloat x, GLfloat y, GLfloat rad ) +{ + const int N = 50; + GLfloat verts[N*2]; + + // Draw vertices anticlockwise for front face + const float TAU_DIV_N = 2*(float)M_PI/N; + for(int i = 0; i < N*2; i+=2) { + verts[i] = x + rad * cos(-i*TAU_DIV_N); + verts[i+1] = y + rad * sin(-i*TAU_DIV_N); + } + + // Render filled shape and outline (to make it look smooth) + glVertexPointer(2, GL_FLOAT, 0, verts); + glEnableClientState(GL_VERTEX_ARRAY); + glDrawArrays(GL_TRIANGLE_FAN, 0, N); + glDrawArrays(GL_LINE_STRIP, 0, N); + glDisableClientState(GL_VERTEX_ARRAY); +} + +inline void glDrawColouredCube(GLfloat axis_min=-0.5f, GLfloat axis_max = +0.5f) +{ + const GLfloat l = axis_min; + const GLfloat h = axis_max; + + const GLfloat verts[] = { + l,l,h, h,l,h, l,h,h, h,h,h, // FRONT + l,l,l, l,h,l, h,l,l, h,h,l, // BACK + l,l,h, l,h,h, l,l,l, l,h,l, // LEFT + h,l,l, h,h,l, h,l,h, h,h,h, // RIGHT + l,h,h, h,h,h, l,h,l, h,h,l, // TOP + l,l,h, l,l,l, h,l,h, h,l,l // BOTTOM + }; + + glVertexPointer(3, GL_FLOAT, 0, verts); + glEnableClientState(GL_VERTEX_ARRAY); + + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); + + glColor4f(0.0f, 1.0f, 0.0f, 1.0f); + glDrawArrays(GL_TRIANGLE_STRIP, 8, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 12, 4); + + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + glDrawArrays(GL_TRIANGLE_STRIP, 16, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 20, 4); + + glDisableClientState(GL_VERTEX_ARRAY); +} + +inline void glDraw_x0(GLfloat scale, int grid) +{ + const GLfloat maxord = grid*scale; + for (int i = -grid; i <= grid; ++i) { + glDrawLine(0.0, i*scale, -maxord, 0.0, i*scale, +maxord); + glDrawLine(0.0, -maxord, i*scale, 0.0, +maxord, i*scale); + } +} + +inline void glDraw_y0(GLfloat scale, int grid) +{ + const GLfloat maxord = grid*scale; + for (int i = -grid; i <= grid; ++i) { + glDrawLine(i*scale, 0.0, -maxord, i*scale, 0.0, +maxord); + glDrawLine(-maxord, 0.0, i*scale, +maxord, 0.0, i*scale); + } +} + +inline void glDraw_z0(GLfloat scale, int grid) +{ + const GLfloat maxord = grid*scale; + for(int i=-grid; i<=grid; ++i ) { + glDrawLine(i*scale,-maxord, i*scale,+maxord); + glDrawLine(-maxord, i*scale, +maxord, i*scale); + } +} + +inline void glDrawFrustrum( GLfloat u0, GLfloat v0, GLfloat fu, GLfloat fv, int w, int h, GLfloat scale ) +{ + const GLfloat xl = scale * u0; + const GLfloat xh = scale * (w*fu + u0); + const GLfloat yl = scale * v0; + const GLfloat yh = scale * (h*fv + v0); + + const GLfloat verts[] = { + xl,yl,scale, xh,yl,scale, + xh,yh,scale, xl,yh,scale, + xl,yl,scale, 0,0,0, + xh,yl,scale, 0,0,0, + xl,yh,scale, 0,0,0, + xh,yh,scale + }; + + glDrawVertices(11, verts, GL_LINE_STRIP, 3); +} + +inline void glDrawTexture(GLenum target, GLuint texid) +{ + glBindTexture(target, texid); + glEnable(target); + + const GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 }; + glVertexPointer(2, GL_FLOAT, 0, sq_vert); + glEnableClientState(GL_VERTEX_ARRAY); + + const GLfloat sq_tex[] = { 0,0, 1,0, 1,1, 0,1 }; + glTexCoordPointer(2, GL_FLOAT, 0, sq_tex); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glColor4f(1,1,1,1); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(target); +} + +inline void glDrawTextureFlipY(GLenum target, GLuint texid) +{ + glBindTexture(target, texid); + glEnable(target); + + const GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 }; + glVertexPointer(2, GL_FLOAT, 0, sq_vert); + glEnableClientState(GL_VERTEX_ARRAY); + + const GLfloat sq_tex[] = { 0,1, 1,1, 1,0, 0,0 }; + glTexCoordPointer(2, GL_FLOAT, 0, sq_tex); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glColor4f(1,1,1,1); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(target); +} + + +#ifdef USE_EIGEN + +#ifndef HAVE_GLES +inline void glVertex( const Eigen::Vector3d& p ) +{ + glVertex3dv(p.data()); +} +#endif + +inline void glDrawLine( const Eigen::Vector2d& p1, const Eigen::Vector2d& p2 ) +{ + glDrawLine((GLfloat)p1(0), (GLfloat)p1(1), (GLfloat)p2(0), (GLfloat)p2(1)); +} + +// Draws a vector of 2d or 3d vertices using provided ``mode``. +// +// Preconditions: +// - ``mode`` must be GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, etc +// - If ``mode == GL_LINES``, then ``vertices.size()`` must be a multiple of 2. +// +template +void glDrawVertices(const std::vector, Allocator>& vertices, GLenum mode) +{ + glDrawVertices(vertices.size(), vertices.data(), mode); +} + +// Draws a vector of 2d or 3d points. +// +template +void glDrawPoints(const std::vector, Allocator>& vertices) +{ + glDrawVertices(vertices, GL_POINTS); +} + +// Draws a vector of 2d or 3d lines. +// +// Precondition: ``vertices.size()`` must be a multiple of 2. +// +template +void glDrawLines(const std::vector, Allocator>& vertices) +{ + glDrawVertices(vertices, GL_LINES); +} + +// Draws a 2d or 3d line strip. +// +template +void glDrawLineStrip(const std::vector, Allocator>& vertices) +{ + glDrawVertices(vertices, GL_LINE_STRIP); +} + +// Draws a 2d or 3d line loop. +// +template +void glDrawLineLoop(const std::vector, Allocator>& vertices) +{ + glDrawVertices(vertices, GL_LINE_LOOP); +} + +inline void glDrawCross( const Eigen::Vector2d& p, double r = 5.0 ) +{ + glDrawCross((GLfloat)p(0), (GLfloat)p(1), (GLfloat)r); +} + +inline void glDrawCross( const Eigen::Vector3d& p, double r = 5.0 ) +{ + glDrawCross((GLfloat)p(0), (GLfloat)p(1), (GLfloat)p(2), (GLfloat)r); +} + +inline void glDrawCircle( const Eigen::Vector2d& p, double radius = 5.0 ) +{ + glDrawCircle((GLfloat)p(0), (GLfloat)p(1), (GLfloat)radius); +} + +inline void glDrawCirclePerimeter( const Eigen::Vector2d& p, double radius = 5.0 ) +{ + glDrawCirclePerimeter((GLfloat)p(0), (GLfloat)p(1), (GLfloat)radius); +} + +inline void glSetFrameOfReference( const Eigen::Matrix4f& T_wf ) +{ + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultMatrixf( T_wf.data() ); +} + +inline void glSetFrameOfReference( const Eigen::Matrix4d& T_wf ) +{ + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); +#ifndef HAVE_GLES + glMultMatrixd( T_wf.data() ); +#else + const Eigen::Matrix4f fT_wf = T_wf.cast(); + glMultMatrixf( fT_wf.data() ); +#endif +} + +inline void glSetFrameOfReference( const pangolin::OpenGlMatrix& T_wf ) +{ + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glMultMatrixd( T_wf.m ); +} + +inline void glUnsetFrameOfReference() +{ + glPopMatrix(); +} + +template +inline void glDrawAxis( const T& T_wf, S scale ) +{ + glSetFrameOfReference(T_wf); + glDrawAxis(scale); + glUnsetFrameOfReference(); +} + +template +inline void glDrawFrustrum( const Eigen::Matrix& Kinv, int w, int h, GLfloat scale ) +{ + glDrawFrustrum((GLfloat)Kinv(0,2), (GLfloat)Kinv(1,2), (GLfloat)Kinv(0,0), (GLfloat)Kinv(1,1), w, h, scale); +} + +template +inline void glDrawFrustrum( const Eigen::Matrix& Kinv, int w, int h, const Eigen::Matrix& T_wf, T scale ) +{ + glSetFrameOfReference(T_wf); + glDrawFrustrum(Kinv,w,h,scale); + glUnsetFrameOfReference(); +} + +template +inline void glDrawAlignedBox( const Eigen::AlignedBox& box, GLenum mode = GL_TRIANGLE_FAN ) +{ + const Eigen::Matrix l = box.min().template cast(); + const Eigen::Matrix h = box.max().template cast(); + + GLfloat verts[] = { + l[0], l[1], + h[0], l[1], + h[0], h[1], + l[0], h[1] + }; + + glDrawVertices(4, verts, mode, 2); +} + +template +inline void glDrawAlignedBoxPerimeter( const Eigen::AlignedBox& box) +{ + glDrawAlignedBox(box, GL_LINE_LOOP); +} + +template +inline void glDrawAlignedBox( const Eigen::AlignedBox& box) +{ + const Eigen::Matrix l = box.min().template cast(); + const Eigen::Matrix h = box.max().template cast(); + + GLfloat verts[] = { + l[0], l[1], l[2], + l[0], l[1], h[2], + h[0], l[1], h[2], + h[0], l[1], l[2], + l[0], l[1], l[2], + l[0], h[1], l[2], + h[0], h[1], l[2], + h[0], l[1], l[2], + h[0], h[1], l[2], + h[0], h[1], h[2], + l[0], h[1], h[2], + l[0], h[1], l[2], + l[0], h[1], h[2], + l[0], l[1], h[2], + h[0], l[1], h[2], + h[0], h[1], h[2] + }; + + glDrawVertices(16, verts, GL_LINE_STRIP, 3); +} + +#endif // USE_EIGEN + +#ifndef HAVE_GLES +inline void glPixelTransferScale( float r, float g, float b ) +{ + glPixelTransferf(GL_RED_SCALE,r); + glPixelTransferf(GL_GREEN_SCALE,g); + glPixelTransferf(GL_BLUE_SCALE,b); +} + +inline void glPixelTransferScale( float scale ) +{ + glPixelTransferScale(scale,scale,scale); +} +#endif + +void glRecordGraphic(float x, float y, float radius); + +} diff --git a/include/pangolin/gl/glfont.h b/include/pangolin/gl/glfont.h new file mode 100644 index 0000000..2304c2f --- /dev/null +++ b/include/pangolin/gl/glfont.h @@ -0,0 +1,78 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2015 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include + +namespace pangolin { + +class PANGOLIN_EXPORT GlFont +{ +public: + // Singleton instance if requested. + static GlFont& I(); + + // Load GL Font data. Delay uploading as texture until first use. + GlFont(const unsigned char* ttf_buffer, float pixel_height, int tex_w=512, int tex_h=512); + GlFont(const std::string& filename, float pixel_height, int tex_w=512, int tex_h=512); + + virtual ~GlFont(); + + // Generate renderable GlText object from this font. + GlText Text( const char* fmt, ... ); + + GlText Text( const std::string& str ); + + inline float Height() const { + return font_height_px; + } + +protected: + void InitialiseFont(const unsigned char* ttf_buffer, float pixel_height, int tex_w, int tex_h); + + // This can only be called once GL context is initialised + void InitialiseGlTexture(); + + const static int FIRST_CHAR = 32; + const static int NUM_CHARS = 96; + + float font_height_px; + + int tex_w; + int tex_h; + unsigned char* font_bitmap; + GlTexture mTex; + + GlChar chardata[NUM_CHARS]; + GLfloat kern_table[NUM_CHARS*NUM_CHARS]; +}; + +} diff --git a/include/pangolin/gl/glformattraits.h b/include/pangolin/gl/glformattraits.h new file mode 100644 index 0000000..2863ea2 --- /dev/null +++ b/include/pangolin/gl/glformattraits.h @@ -0,0 +1,214 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#ifdef HAVE_EIGEN +# include +#endif + +namespace pangolin +{ + +template +struct GlFormatTraits; +//{ +// static const GLint glinternalformat = 0; +// static const GLenum glformat = 0; +// static const GLenum gltype = 0; +// static const T glmin = 0; +// static const T glmax = 0; +//}; + +template<> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_LUMINANCE32F_ARB; + static const GLenum glformat = GL_LUMINANCE; + static const GLenum gltype = GL_UNSIGNED_BYTE; + static const size_t components = 1; +}; + +template<> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_LUMINANCE32F_ARB; + static const GLenum glformat = GL_LUMINANCE; + static const GLenum gltype = GL_UNSIGNED_SHORT; + static const size_t components = 1; +}; + +template<> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_LUMINANCE32F_ARB; + static const GLenum glformat = GL_LUMINANCE; + static const GLenum gltype = GL_UNSIGNED_INT; + static const size_t components = 1; +}; + +template<> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_LUMINANCE32F_ARB; + static const GLenum glformat = GL_LUMINANCE; + static const GLenum gltype = GL_INT; + static const size_t components = 1; +}; + +template<> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_LUMINANCE32F_ARB; + static const GLenum glformat = GL_LUMINANCE; + static const GLenum gltype = GL_FLOAT; + static const size_t components = 1; +}; + +template<> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_LUMINANCE32F_ARB; + static const GLenum glformat = GL_LUMINANCE; + static const GLenum gltype = GL_DOUBLE; + static const size_t components = 1; +}; + + + +#ifdef HAVE_EIGEN + +////////////////////////////////////////////////////////////////// + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGB32F_ARB; + static const GLenum glformat = GL_RG; + static const GLenum gltype = GL_INT; + static const size_t components = 2; +}; + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGB32F_ARB; + static const GLenum glformat = GL_RG; + static const GLenum gltype = GL_FLOAT; + static const size_t components = 2; +}; + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGB32F_ARB; + static const GLenum glformat = GL_RG; + static const GLenum gltype = GL_DOUBLE; + static const size_t components = 2; +}; + +////////////////////////////////////////////////////////////////// + +template <> +struct GlFormatTraits> +{ + static const GLint glinternalformat = GL_RGB; + static const GLenum glformat = GL_RGB; + static const GLenum gltype = GL_UNSIGNED_BYTE; + static const size_t components = 3; +}; + +template <> +struct GlFormatTraits> +{ + static const GLint glinternalformat = GL_RGBA32F_ARB; + static const GLenum glformat = GL_RGB; + static const GLenum gltype = GL_UNSIGNED_SHORT; + static const size_t components = 3; +}; + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGB32F_ARB; + static const GLenum glformat = GL_RGB; + static const GLenum gltype = GL_FLOAT; + static const size_t components = 3; +}; + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGB32F_ARB; + static const GLenum glformat = GL_RGB; + static const GLenum gltype = GL_DOUBLE; + static const size_t components = 3; +}; + +////////////////////////////////////////////////////////////////// + +template <> +struct GlFormatTraits> +{ + static const GLint glinternalformat = GL_RGBA; + static const GLenum glformat = GL_RGBA; + static const GLenum gltype = GL_UNSIGNED_BYTE; + static const size_t components = 4; +}; + +template <> +struct GlFormatTraits> +{ + static const GLint glinternalformat = GL_RGBA32F_ARB; + static const GLenum glformat = GL_RGBA; + static const GLenum gltype = GL_UNSIGNED_SHORT; + static const size_t components = 4; +}; + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGBA32F_ARB; + static const GLenum glformat = GL_RGBA; + static const GLenum gltype = GL_FLOAT; + static const size_t components = 4; +}; + +template <> +struct GlFormatTraits +{ + static const GLint glinternalformat = GL_RGBA32F_ARB; + static const GLenum glformat = GL_RGBA; + static const GLenum gltype = GL_DOUBLE; + static const size_t components = 4; +}; + +#endif // HAVE_EIGEN + +} diff --git a/include/pangolin/gl/glglut.h b/include/pangolin/gl/glglut.h new file mode 100644 index 0000000..65af723 --- /dev/null +++ b/include/pangolin/gl/glglut.h @@ -0,0 +1,48 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "glplatform.h" + +#ifdef HAVE_GLUT + #ifdef HAVE_APPLE_OPENGL_FRAMEWORK + #include + #define HAVE_GLUT_APPLE_FRAMEWORK + + inline void glutBitmapString(void* font, const unsigned char* str) + { + const unsigned char* s = str; + while(*s != 0) { + glutBitmapCharacter(font, *s); + ++s; + } + } + #else + #include + #endif // HAVE_APPLE_OPENGL_FRAMEWORK +#endif // HAVE_GLUT diff --git a/include/pangolin/gl/glinclude.h b/include/pangolin/gl/glinclude.h new file mode 100644 index 0000000..513356d --- /dev/null +++ b/include/pangolin/gl/glinclude.h @@ -0,0 +1,46 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove, Richard Newcombe + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#ifdef HAVE_GLES +#include +#endif + +#define CheckGlDieOnError() pangolin::_CheckGlDieOnError( __FILE__, __LINE__ ); +namespace pangolin { +inline void _CheckGlDieOnError( const char *sFile, const int nLine ) +{ + GLenum glError = glGetError(); + if( glError != GL_NO_ERROR ) { + pango_print_error( "OpenGL Error: %s (%d)\n", glErrorString(glError), glError ); + pango_print_error("In: %s, line %d\n", sFile, nLine); + } +} +} diff --git a/include/pangolin/gl/glpangoglu.h b/include/pangolin/gl/glpangoglu.h new file mode 100644 index 0000000..c6f5dae --- /dev/null +++ b/include/pangolin/gl/glpangoglu.h @@ -0,0 +1,80 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin { + +/// Clone of gluProject +PANGOLIN_EXPORT +const GLubyte* glErrorString(GLenum error); + +/// Clone of gluProject +PANGOLIN_EXPORT +GLint glProject( + float objx, float objy, float objz, + const float modelMatrix[16], + const float projMatrix[16], + const GLint viewport[4], + float* winx, float* winy, float* winz +); + + +/// Clone of gluUnProject +PANGOLIN_EXPORT +GLint glUnProject( + float winx, float winy, float winz, + const float modelMatrix[16], + const float projMatrix[16], + const GLint viewport[4], + float* objx, float* objy, float* objz +); + +/// Clone of gluProject +PANGOLIN_EXPORT +GLint glProject( + double objx, double objy, double objz, + const double modelMatrix[16], + const double projMatrix[16], + const GLint viewport[4], + double* winx, double* winy, double* winz +); + + +/// Clone of gluUnProject +PANGOLIN_EXPORT +GLint glUnProject( + double winx, double winy, double winz, + const double modelMatrix[16], + const double projMatrix[16], + const GLint viewport[4], + double* objx, double* objy, double* objz +); + +} diff --git a/include/pangolin/gl/glpixformat.h b/include/pangolin/gl/glpixformat.h new file mode 100644 index 0000000..23f851f --- /dev/null +++ b/include/pangolin/gl/glpixformat.h @@ -0,0 +1,91 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace pangolin { + +// This class may dissapear in the future +struct GlPixFormat +{ + GlPixFormat() {} + + GlPixFormat(const PixelFormat& fmt) + { + switch( fmt.channels) { + case 1: glformat = GL_LUMINANCE; break; + case 3: glformat = (fmt.format == "BGR24" || fmt.format == "BGR48") ? GL_BGR : GL_RGB; break; + case 4: glformat = (fmt.format == "BGRA24" || fmt.format == "BGRA32" || fmt.format == "BGRA48") ? GL_BGRA : GL_RGBA; break; + default: throw std::runtime_error("Unable to form OpenGL format from video format: '" + fmt.format + "'."); + } + + const bool is_integral = fmt.format.find('F') == std::string::npos; + + switch (fmt.channel_bits[0]) { + case 8: gltype = GL_UNSIGNED_BYTE; break; + case 16: gltype = GL_UNSIGNED_SHORT; break; + case 32: gltype = (is_integral ? GL_UNSIGNED_INT : GL_FLOAT); break; + case 64: gltype = (is_integral ? GL_UNSIGNED_INT64_NV : GL_DOUBLE); break; + default: throw std::runtime_error("Unknown OpenGL data type for video format: '" + fmt.format + "'."); + } + + if(glformat == GL_LUMINANCE) { + if(gltype == GL_UNSIGNED_BYTE) { + scalable_internal_format = GL_LUMINANCE8; + }else{ + scalable_internal_format = GL_LUMINANCE32F_ARB; + } + }else{ + if(gltype == GL_UNSIGNED_BYTE) { + scalable_internal_format = GL_RGBA8; + }else{ + scalable_internal_format = GL_RGBA32F; + } + } + } + + template + static GlPixFormat FromType() + { + GlPixFormat fmt; + fmt.glformat = GlFormatTraits::glformat; + fmt.gltype = GlFormatTraits::gltype; + fmt.scalable_internal_format = GlFormatTraits::glinternalformat; + return fmt; + } + + GLint glformat; + GLenum gltype; + GLint scalable_internal_format; +}; + +} diff --git a/include/pangolin/gl/glplatform.h b/include/pangolin/gl/glplatform.h new file mode 100644 index 0000000..2e05e13 --- /dev/null +++ b/include/pangolin/gl/glplatform.h @@ -0,0 +1,85 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +////////////////////////////////////////////////////////// +// Attempt to portably include Necessary OpenGL headers +////////////////////////////////////////////////////////// + +#include + +#ifdef _WIN_ + // Define maths quantities when using to match posix systems + #ifndef _USE_MATH_DEFINES + # define _USE_MATH_DEFINES + #endif + + // Don't define min / max macros in windows.h or other unnecessary macros + #ifndef NOMINMAX + # define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + # define WIN32_LEAN_AND_MEAN + #endif + #include + + // Undef nuisance Windows.h macros which interfere with our methods + #undef LoadImage + #undef near + #undef far + #undef ERROR +#endif + +#ifdef HAVE_GLEW + #include +#endif + +#ifdef HAVE_GLES + #if defined(_ANDROID_) + #include + #ifdef HAVE_GLES_2 + #include + #include + #else + #include + #define GL_GLEXT_PROTOTYPES + #include + #endif + #elif defined(_APPLE_IOS_) + #include + #include + #endif +#else + #ifdef _OSX_ + #include + #else + #include + #endif +#endif // HAVE_GLES + +#include diff --git a/include/pangolin/gl/glsl.h b/include/pangolin/gl/glsl.h new file mode 100644 index 0000000..d7c9ff1 --- /dev/null +++ b/include/pangolin/gl/glsl.h @@ -0,0 +1,563 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_GLES + #define GLhandleARB GLuint +#endif + +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) //prevent including Eigen in cuda files +#define USE_EIGEN +#endif + +#ifdef USE_EIGEN +#include +#endif // USE_EIGEN + +namespace pangolin +{ + +//////////////////////////////////////////////// +// Standard attribute locations +//////////////////////////////////////////////// + +const GLuint DEFAULT_LOCATION_POSITION = 0; +const GLuint DEFAULT_LOCATION_COLOUR = 1; +const GLuint DEFAULT_LOCATION_NORMAL = 2; +const GLuint DEFAULT_LOCATION_TEXCOORD = 3; + +const char DEFAULT_NAME_POSITION[] = "a_position"; +const char DEFAULT_NAME_COLOUR[] = "a_color"; +const char DEFAULT_NAME_NORMAL[] = "a_normal"; +const char DEFAULT_NAME_TEXCOORD[] = "a_texcoord"; + +//////////////////////////////////////////////// +// Interface +//////////////////////////////////////////////// + +enum GlSlShaderType +{ + GlSlFragmentShader = GL_FRAGMENT_SHADER, + GlSlVertexShader = GL_VERTEX_SHADER, + GlSlGeometryShader = 0x8DD9 /*GL_GEOMETRY_SHADER*/, + GlSlComputeShader = 0x91B9 /*GL_COMPUTE_SHADER*/ +}; + +class GlSlProgram +{ +public: + GlSlProgram(); + + //! Move Constructor + GlSlProgram(GlSlProgram&& tex); + + ~GlSlProgram(); + + bool AddShader( + GlSlShaderType shader_type, + const std::string& source_code, + const std::map& program_defines = std::map(), + const std::vector& search_path = std::vector() + ); + + bool AddShaderFromFile( + GlSlShaderType shader_type, + const std::string& filename, + const std::map& program_defines = std::map(), + const std::vector& search_path = std::vector() + ); + + bool Link(); + + GLint GetAttributeHandle(const std::string& name); + GLint GetUniformHandle(const std::string& name); + + // Before setting uniforms, be sure to Bind() the GlSl program first. + void SetUniform(const std::string& name, int x); + void SetUniform(const std::string& name, int x1, int x2); + void SetUniform(const std::string& name, int x1, int x2, int x3); + void SetUniform(const std::string& name, int x1, int x2, int x3, int x4); + + void SetUniform(const std::string& name, float f); + void SetUniform(const std::string& name, float f1, float f2); + void SetUniform(const std::string& name, float f1, float f2, float f3); + void SetUniform(const std::string& name, float f1, float f2, float f3, float f4); + + void SetUniform(const std::string& name, Colour c); + + void SetUniform(const std::string& name, const OpenGlMatrix& m); + +#if GL_VERSION_4_3 + GLint GetProgramResourceIndex(const std::string& name); + void SetShaderStorageBlock(const std::string& name, const int& bindingIndex); +#endif + + void Bind(); + void SaveBind(); + void Unbind(); + + void BindPangolinDefaultAttribLocationsAndLink(); + + GLint ProgramId() { return prog; } + +protected: + std::string ParseIncludeFilename( + const std::string& location + ); + + std::string SearchIncludePath( + const std::string& filename, + const std::vector& search_path, + const std::string& current_path + ); + + bool AddPreprocessedShader( + GlSlShaderType shader_type, + const std::string& source_code, + const std::string& name_for_errors + ); + + void ParseGLSL( + std::istream& input, + std::ostream& output, + const std::map& program_defines, + const std::vector& search_path, + const std::string& current_path + ); + + bool linked; + std::vector shaders; + GLenum prog; + + GLint prev_prog; +}; + +class GlSlUtilities +{ +public: + inline static GlSlProgram& OffsetAndScale(float offset, float scale) { + GlSlProgram& prog = Instance().prog_offsetscale; + prog.Bind(); + prog.SetUniform("offset", offset); + prog.SetUniform("scale", scale); + return prog; + } + + inline static GlSlProgram& Scale(float scale, float bias = 0.0f) { + GlSlProgram& prog = Instance().prog_scale; + prog.Bind(); + prog.SetUniform("scale", scale); + prog.SetUniform("bias", bias); + return prog; + } + + inline static void UseNone() + { + glUseProgram(0); + } + +protected: + static GlSlUtilities& Instance() { + // TODO: BUG: The GlSLUtilities instance needs to be tied + // to the OpenGL context, not the thread, or globally. +#ifndef PANGO_NO_THREADLOCAL + thread_local +#else + static +#endif + GlSlUtilities instance; + return instance; + } + + // protected constructor + GlSlUtilities() { + const char* source_scale = + "uniform float scale;" + "uniform float bias;" + "uniform sampler2D tex;" + "void main() {" + " vec2 uv = gl_TexCoord[0].st;" + " if(0.0 <= uv.x && uv.x <= 1.0 && 0.0 <= uv.y && uv.y <= 1.0) {" + " gl_FragColor = texture2D(tex,uv);" + " gl_FragColor.xyz *= scale;" + " gl_FragColor.xyz += vec3(bias,bias,bias);" + " }else{" + " float v = 0.1;" + " gl_FragColor.xyz = vec3(v,v,v);" + " }" + "}"; + prog_scale.AddShader(GlSlFragmentShader, source_scale); + prog_scale.Link(); + + const char* source_offsetscale = + "uniform float offset;" + "uniform float scale;" + "uniform sampler2D tex;" + "void main() {" + " vec2 uv = gl_TexCoord[0].st;" + " if(0.0 <= uv.x && uv.x <= 1.0 && 0.0 <= uv.y && uv.y <= 1.0) {" + " gl_FragColor = texture2D(tex,gl_TexCoord[0].st);" + " gl_FragColor.xyz += vec3(offset,offset,offset);" + " gl_FragColor.xyz *= scale;" + " }else{" + " float v = 0.1;" + " gl_FragColor.xyz = vec3(v,v,v);" + " }" + "}"; + prog_offsetscale.AddShader(GlSlFragmentShader, source_offsetscale); + prog_offsetscale.Link(); + } + + GlSlProgram prog_scale; + GlSlProgram prog_offsetscale; +}; + + +//////////////////////////////////////////////// +// Implementation +//////////////////////////////////////////////// + +inline bool IsLinkSuccessPrintLog(GLhandleARB prog) +{ + GLint status; + glGetProgramiv(prog, GL_LINK_STATUS, &status); + if(status != GL_TRUE) { + pango_print_error("GLSL Program link failed: "); + const int PROGRAM_LOG_MAX_LEN = 10240; + char infolog[PROGRAM_LOG_MAX_LEN]; + GLsizei len; + glGetProgramInfoLog(prog, PROGRAM_LOG_MAX_LEN, &len, infolog); + if(len) { + pango_print_error("%s\n",infolog); + }else{ + pango_print_error("No details provided.\n"); + } + return false; + } + return true; +} + +inline bool IsCompileSuccessPrintLog(GLhandleARB shader, const std::string& name_for_errors) +{ + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if(status != GL_TRUE) { + pango_print_error("GLSL Shader compilation failed: "); + const int SHADER_LOG_MAX_LEN = 10240; + char infolog[SHADER_LOG_MAX_LEN]; + GLsizei len; + glGetShaderInfoLog(shader, SHADER_LOG_MAX_LEN, &len, infolog); + if(len) { + pango_print_error("%s:\n%s\n",name_for_errors.c_str(), infolog); + }else{ + pango_print_error("%s:\nNo details provided.\n",name_for_errors.c_str()); + } + return false; + } + return true; +} + +inline GlSlProgram::GlSlProgram() + : linked(false), prog(0), prev_prog(0) +{ +} + +//! Move Constructor +inline GlSlProgram::GlSlProgram(GlSlProgram&& o) + : linked(o.linked), shaders(o.shaders), prog(o.prog), prev_prog(o.prev_prog) +{ + o.prog = 0; +} + +inline GlSlProgram::~GlSlProgram() +{ + if(prog) { + // Remove and delete each shader + for(size_t i=0; i", start+1); + if(end != std::string::npos) { + return location.substr(start+1, end - start - 1); + } + } + throw std::runtime_error("GLSL Parser: Unable to parse include location " + location ); +} + +inline std::string GlSlProgram::SearchIncludePath( + const std::string& filename, + const std::vector& search_path, + const std::string& current_path +) { + if(FileExists(current_path + "/" + filename)) { + return current_path + "/" + filename; + }else{ + for(size_t i=0; i < search_path.size(); ++i) { + const std::string hypoth = search_path[i] + "/" + filename; + if( FileExists(hypoth) ) { + return hypoth; + } + } + } + return ""; +} + +inline void GlSlProgram::ParseGLSL( + std::istream& input, std::ostream& output, + const std::map& program_defines, + const std::vector &search_path, + const std::string ¤t_path +) { + const size_t MAXLINESIZE = 10240; + char line[MAXLINESIZE]; + + while(!input.eof()) { + // Take like from source + input.getline(line,MAXLINESIZE); + + // Transform + if( !strncmp(line, "#include", 8 ) ) { + // C++ / G3D style include directive + const std::string import_file = ParseIncludeFilename(line+8); + const std::string resolved_file = SearchIncludePath(import_file, search_path, current_path); + + std::ifstream ifs(resolved_file.c_str()); + if(ifs.good()) { + const std::string file_path = pangolin::PathParent(resolved_file); + ParseGLSL(ifs, output, program_defines, search_path, file_path); + }else{ + throw std::runtime_error("GLSL Parser: Unable to open " + import_file ); + } + }else if( !strncmp(line, "#expect", 7) ) { + // G3D style 'expect' directive, annotating expected preprocessor + // definition with document string + + // Consume whitespace before token + size_t token_start = 7; + while( std::isspace(line[token_start]) ) ++token_start; + + // Iterate over contigous charecters until \0 or whitespace + size_t token_end = token_start; + while( line[token_end] && !std::isspace(line[token_end]) ) ++token_end; + + std::string token(line+token_start, line+token_end); + std::map::const_iterator it = program_defines.find(token); + if( it == program_defines.end() ) { + pango_print_warn("Expected define missing (defaulting to 0): '%s'\n%s\n", token.c_str(), line + token_end ); + output << "#define " << token << " 0" << std::endl; + }else{ + output << "#define " << token << " " << it->second << std::endl; + } + }else{ + // Output directly + output << line << std::endl; + } + } +} + +inline bool GlSlProgram::AddShader( + GlSlShaderType shader_type, + const std::string& source_code, + const std::map& program_defines, + const std::vector& search_path +) { + std::istringstream iss(source_code); + std::stringstream buffer; + ParseGLSL(iss, buffer, program_defines, search_path, "."); + return AddPreprocessedShader(shader_type, buffer.str(), "" ); +} + +inline bool GlSlProgram::AddShaderFromFile( + GlSlShaderType shader_type, + const std::string& filename, + const std::map& program_defines, + const std::vector& search_path +) { + std::ifstream ifs(filename.c_str()); + if(ifs.is_open()) { + std::stringstream buffer; + ParseGLSL(ifs, buffer, program_defines, search_path, "."); + return AddPreprocessedShader(shader_type, buffer.str(), filename ); + }else{ + throw std::runtime_error("Unable to open " + filename ); + } +} + +inline bool GlSlProgram::Link() +{ + glLinkProgram(prog); + return IsLinkSuccessPrintLog(prog); +} + +inline void GlSlProgram::Bind() +{ + prev_prog = 0; + glUseProgram(prog); +} + +inline void GlSlProgram::SaveBind() +{ + glGetIntegerv(GL_CURRENT_PROGRAM, &prev_prog); + glUseProgram(prog); +} + +inline void GlSlProgram::Unbind() +{ + glUseProgram(prev_prog); +} + +inline GLint GlSlProgram::GetAttributeHandle(const std::string& name) +{ + return glGetAttribLocation(prog, name.c_str()); +} + +inline GLint GlSlProgram::GetUniformHandle(const std::string& name) +{ + return glGetUniformLocation(prog, name.c_str()); +} + +inline void GlSlProgram::SetUniform(const std::string& name, int x) +{ + glUniform1i( GetUniformHandle(name), x); +} + +inline void GlSlProgram::SetUniform(const std::string& name, int x1, int x2) +{ + glUniform2i( GetUniformHandle(name), x1, x2); +} + +inline void GlSlProgram::SetUniform(const std::string& name, int x1, int x2, int x3) +{ + glUniform3i( GetUniformHandle(name), x1, x2, x3); +} + +inline void GlSlProgram::SetUniform(const std::string& name, int x1, int x2, int x3, int x4) +{ + glUniform4i( GetUniformHandle(name), x1, x2, x3, x4); +} + +inline void GlSlProgram::SetUniform(const std::string& name, float f) +{ + glUniform1f( GetUniformHandle(name), f); +} + +inline void GlSlProgram::SetUniform(const std::string& name, float f1, float f2) +{ + glUniform2f( GetUniformHandle(name), f1,f2); +} + +inline void GlSlProgram::SetUniform(const std::string& name, float f1, float f2, float f3) +{ + glUniform3f( GetUniformHandle(name), f1,f2,f3); +} + +inline void GlSlProgram::SetUniform(const std::string& name, float f1, float f2, float f3, float f4) +{ + glUniform4f( GetUniformHandle(name), f1,f2,f3,f4); +} + +inline void GlSlProgram::SetUniform(const std::string& name, Colour c) +{ + glUniform4f( GetUniformHandle(name), c.r, c.g, c.b, c.a); +} + +inline void GlSlProgram::SetUniform(const std::string& name, const OpenGlMatrix& mat) +{ + // glUniformMatrix4dv seems to be crashing... + float m[16]; + for (int i = 0; i < 16; ++i) { + m[i] = (float)mat.m[i]; + } + glUniformMatrix4fv( GetUniformHandle(name), 1, GL_FALSE, m); +} + +inline void GlSlProgram::BindPangolinDefaultAttribLocationsAndLink() +{ + glBindAttribLocation(prog, DEFAULT_LOCATION_POSITION, DEFAULT_NAME_POSITION); + glBindAttribLocation(prog, DEFAULT_LOCATION_COLOUR, DEFAULT_NAME_COLOUR); + glBindAttribLocation(prog, DEFAULT_LOCATION_NORMAL, DEFAULT_NAME_NORMAL); + glBindAttribLocation(prog, DEFAULT_LOCATION_TEXCOORD, DEFAULT_NAME_TEXCOORD); + Link(); +} + +#if GL_VERSION_4_3 +inline GLint GlSlProgram::GetProgramResourceIndex(const std::string& name) +{ + return glGetProgramResourceIndex(prog, GL_SHADER_STORAGE_BLOCK, name.c_str()); +} + +inline void GlSlProgram::SetShaderStorageBlock(const std::string& name, const int& bindingIndex) +{ + glShaderStorageBlockBinding(prog, GetProgramResourceIndex(name), bindingIndex); +} +#endif + +} diff --git a/include/pangolin/gl/glstate.h b/include/pangolin/gl/glstate.h new file mode 100644 index 0000000..0757ba2 --- /dev/null +++ b/include/pangolin/gl/glstate.h @@ -0,0 +1,220 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Vincent Mamo, Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +class GlState { + + class CapabilityState { + public: + + CapabilityState(GLenum cap, GLboolean enable) + : m_cap(cap), m_enable(enable) + { + + } + + void Apply() { + if(m_enable) { + ::glEnable(m_cap); + }else{ + ::glDisable(m_cap); + } + } + + void UnApply() { + if(m_enable) { + ::glDisable(m_cap); + }else{ + ::glEnable(m_cap); + } + } + + protected: + GLenum m_cap; + GLboolean m_enable; + }; + +public: + GlState() + : m_DepthMaskCalled(false), + m_ShadeModelCalled(false), + m_CullFaceCalled(false), + m_PointSizeCalled(false), + m_LineWidthCalled(false), + m_ColorMaskCalled(false), + m_ViewportCalled(false) + { + } + + ~GlState() { + // Restore original state + while (!m_history.empty()) { + m_history.top().UnApply(); + m_history.pop(); + } + + if (m_DepthMaskCalled) { + ::glDepthMask(m_OriginalDepthMask); + } + + if (m_ShadeModelCalled) { + ::glShadeModel(m_OriginalShadeModel); + } + + if (m_CullFaceCalled) { + ::glCullFace(m_OriginalCullFace); + } + + if(m_PointSizeCalled) { + ::glPointSize(m_OriginalPointSize); + } + + if(m_LineWidthCalled) { + ::glLineWidth(m_OriginalLineWidth); + } + + if (m_ColorMaskCalled) { + ::glColorMask(m_OriginalColorMask[0], m_OriginalColorMask[1], m_OriginalColorMask[2], m_OriginalColorMask[3]); + } + + if (m_ViewportCalled) { + ::glViewport(m_OriginalViewport[0], m_OriginalViewport[1], m_OriginalViewport[2], m_OriginalViewport[3]); + } + } + + static inline GLboolean IsEnabled(GLenum cap) + { + GLboolean curVal; + glGetBooleanv(cap, &curVal); + return curVal; + } + + inline void glEnable(GLenum cap) + { + if(!IsEnabled(cap)) { + m_history.push(CapabilityState(cap,true)); + ::glEnable(cap); + } + } + + inline void glDisable(GLenum cap) + { + if(IsEnabled(cap)) { + m_history.push(CapabilityState(cap,false)); + ::glDisable(cap); + } + } + + bool m_DepthMaskCalled; + GLboolean m_OriginalDepthMask; + inline void glDepthMask(GLboolean flag) + { + if(!m_DepthMaskCalled) { + m_DepthMaskCalled = true; + glGetBooleanv(GL_DEPTH_WRITEMASK, &m_OriginalDepthMask); + } + ::glDepthMask(flag); + } + + bool m_ShadeModelCalled; + GLint m_OriginalShadeModel; + inline void glShadeModel(GLint mode) + { + if(!m_ShadeModelCalled) { + m_ShadeModelCalled = true; + glGetIntegerv(GL_SHADE_MODEL, &m_OriginalShadeModel); + } + ::glShadeModel(mode); + } + + bool m_CullFaceCalled; + GLint m_OriginalCullFace; + void glCullFace(GLenum mode) + { + if(!m_ShadeModelCalled) { + m_ShadeModelCalled = true; + glGetIntegerv(GL_CULL_FACE_MODE, &m_OriginalCullFace); + } + ::glCullFace(mode); + } + + bool m_PointSizeCalled; + GLfloat m_OriginalPointSize; + void glPointSize(GLfloat size) + { + if(!m_PointSizeCalled) { + m_PointSizeCalled = true; + glGetFloatv(GL_POINT_SIZE, &m_OriginalPointSize); + } + ::glPointSize(size); + } + + bool m_LineWidthCalled; + GLfloat m_OriginalLineWidth; + void glLineWidth(GLfloat width) + { + if(!m_LineWidthCalled) { + m_LineWidthCalled = true; + glGetFloatv(GL_LINE_WIDTH, &m_OriginalLineWidth); + } + ::glLineWidth(width); + + } + + bool m_ColorMaskCalled; + GLboolean m_OriginalColorMask[4]; + inline void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) + { + if(!m_ColorMaskCalled) { + m_ColorMaskCalled = true; + glGetBooleanv(GL_COLOR_WRITEMASK, m_OriginalColorMask); + } + ::glColorMask(red, green, blue, alpha); + } + + bool m_ViewportCalled; + GLint m_OriginalViewport[4]; + inline void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) + { + if(!m_ViewportCalled) { + m_ViewportCalled = true; + glGetIntegerv(GL_VIEWPORT, m_OriginalViewport); + } + ::glViewport(x, y, width, height); + } + + std::stack m_history; +}; + +} diff --git a/include/pangolin/gl/gltext.h b/include/pangolin/gl/gltext.h new file mode 100644 index 0000000..1c8f0b2 --- /dev/null +++ b/include/pangolin/gl/gltext.h @@ -0,0 +1,98 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin { + +class PANGOLIN_EXPORT GlText +{ +public: + GlText(); + + GlText(const GlText& txt); + + GlText(const GlTexture& font_tex); + + void AddSpace(GLfloat s); + + // Add specified charector to this string. + void Add(unsigned char c, const GlChar& glc); + + // Clear text + void Clear(); + + // Render without transform in text-centric pixel coordinates + void Draw() const; + void DrawGlSl() const; + +#ifdef BUILD_PANGOLIN_GUI + // Render at (x,y,z)' in object coordinates, + // keeping text size and orientation constant + void Draw(GLfloat x, GLfloat y, GLfloat z = 0.0f) const; + + // Render at (x,y,z)' in window coordinates. + void DrawWindow(GLfloat x, GLfloat y, GLfloat z = 0.0f) const; +#endif // BUILD_PANGOLIN_GUI + + // Return text that this object signifies. + const std::string& Text() const { + return str; + } + + // Return width in pixels of this text. + GLfloat Width() const { + return width; + } + + // Return height in pixels of this text. + GLfloat Height() const { + return ymax; + } + + // Return height in pixels of this text, including under baseline + GLfloat FullHeight() const { + return ymax - ymin; + } + +//protected: + const GlTexture* tex; + std::string str; + GLfloat width; + GLfloat ymin; + GLfloat ymax; + + std::vector vs; +}; + +} diff --git a/include/pangolin/gl/gltexturecache.h b/include/pangolin/gl/gltexturecache.h new file mode 100644 index 0000000..f3ad303 --- /dev/null +++ b/include/pangolin/gl/gltexturecache.h @@ -0,0 +1,116 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT TextureCache +{ +public: + static TextureCache& I(); + + GlTexture& GlTex(GLsizei w, GLsizei h, GLint internal_format, GLint glformat, GLenum gltype) + { + const long key = + (((long)internal_format)<<20) ^ + (((long)glformat)<<10) ^ gltype; + + // Lookup texture + std::shared_ptr& ptex = texture_map[key]; + if(!ptex) { + ptex = std::shared_ptr(new GlTexture()); + } + GlTexture& tex = *ptex; + + // Initialise if it didn't already exist or the size was too small + if(!tex.tid || tex.width < w || tex.height < h) { + tex.Reinitialise( + std::max(tex.width,w), std::max(tex.height,h), + internal_format, default_sampling_linear, 0, + glformat, gltype + ); + } + + return tex; + } + + template + GlTexture& GlTex(GLsizei w, GLsizei h) + { + return GlTex( w, h, + GlFormatTraits::glinternalformat, + GlFormatTraits::glformat, + GlFormatTraits::gltype + ); + } + +protected: + bool default_sampling_linear; + std::map > texture_map; + + // Protected constructor + TextureCache() + : default_sampling_linear(true) + { + } +}; + +template +void RenderToViewport(Image& image, bool flipx=false, bool flipy=false) +{ + // Retrieve texture that is at least as large as image and of appropriate type. + GlTexture& tex = TextureCache::I().GlTex(image.w, image.h); + tex.Upload(image.ptr,0,0, image.w, image.h, GlFormatTraits::glformat, GlFormatTraits::gltype); + tex.RenderToViewport(Viewport(0,0,image.w, image.h), flipx, flipy); +} + +// This method may dissapear in the future +inline void RenderToViewport( + Image& image, + const pangolin::GlPixFormat& fmt, + bool flipx=false, bool flipy=false, + bool linear_sampling = true +) { + pangolin::GlTexture& tex = pangolin::TextureCache::I().GlTex((GLsizei)image.w, (GLsizei)image.h, fmt.scalable_internal_format, fmt.glformat, fmt.gltype); + tex.Bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_sampling ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_sampling ? GL_LINEAR : GL_NEAREST); + tex.Upload(image.ptr,0,0, (GLsizei)image.w, (GLsizei)image.h, fmt.glformat, fmt.gltype); + tex.RenderToViewport(pangolin::Viewport(0,0,(GLint)image.w, (GLint)image.h), flipx, flipy); +} + +} diff --git a/include/pangolin/gl/glvbo.h b/include/pangolin/gl/glvbo.h new file mode 100644 index 0000000..546f6d5 --- /dev/null +++ b/include/pangolin/gl/glvbo.h @@ -0,0 +1,225 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin +{ + +//////////////////////////////////////////////// +// Interface +//////////////////////////////////////////////// + +void MakeTriangleStripIboForVbo(GlBuffer& ibo, int w, int h); + +GlBuffer MakeTriangleStripIboForVbo(int w, int h); + +void RenderVbo(GlBuffer& vbo, GLenum mode = GL_POINTS); + +void RenderVboCbo(GlBuffer& vbo, GlBuffer& cbo, bool draw_color = true, GLenum mode = GL_POINTS); + +void RenderVboIbo(GlBuffer& vbo, GlBuffer& ibo, bool draw_mesh = true); + +void RenderVboIboCbo(GlBuffer& vbo, GlBuffer& ibo, GlBuffer& cbo, bool draw_mesh = true, bool draw_color = true); + +void RenderVboIboNbo(GlBuffer& vbo, GlBuffer& ibo, GlBuffer& nbo, bool draw_mesh = true, bool draw_normals = true); + +void RenderVboIboCboNbo(GlBuffer& vbo, GlBuffer& ibo, GlBuffer& cbo, GlBuffer& nbo, bool draw_mesh = true, bool draw_color = true, bool draw_normals = true); + +//////////////////////////////////////////////// +// Implementation +//////////////////////////////////////////////// + +inline void MakeTriangleStripIboForVbo(GlBuffer& ibo, int w, int h) +{ + const int num_elements = w*(h-1)*2; + unsigned int* buffer = new unsigned int[num_elements]; + unsigned int* ptr = buffer; + + for(int y=0; y < (h-1);) + { + for(int x=0; x=(h-1)) break; + for(int x=w-1; x>=0; --x) { + (*ptr++) = y*w+x; + (*ptr++) = (y+1)*w+x; + } + ++y; + } + + ibo.Reinitialise(GlElementArrayBuffer, num_elements, GL_UNSIGNED_INT, 1, GL_STATIC_DRAW ); + ibo.Upload(buffer, sizeof(unsigned int)*num_elements ); + + delete[] buffer; +} + +inline GlBuffer MakeTriangleStripIboForVbo(int w, int h) +{ + GlBuffer ibo; + MakeTriangleStripIboForVbo(ibo,w,h); + return ibo; +} + +inline void RenderVbo(GlBuffer& vbo, GLenum mode) +{ + vbo.Bind(); + glVertexPointer(vbo.count_per_element, vbo.datatype, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(mode, 0, vbo.num_elements); + + glDisableClientState(GL_VERTEX_ARRAY); + vbo.Unbind(); +} + +inline void RenderVboCbo(GlBuffer& vbo, GlBuffer& cbo, bool draw_color, GLenum mode ) +{ + if(draw_color) { + cbo.Bind(); + glColorPointer(cbo.count_per_element, cbo.datatype, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } + + RenderVbo(vbo,mode); + + if(draw_color) { + glDisableClientState(GL_COLOR_ARRAY); + cbo.Unbind(); + } +} + +inline void RenderVboIbo(GlBuffer& vbo, GlBuffer& ibo, bool draw_mesh) +{ + vbo.Bind(); + glVertexPointer(vbo.count_per_element, vbo.datatype, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(draw_mesh) { + ibo.Bind(); + glDrawElements(GL_TRIANGLE_STRIP,ibo.num_elements, ibo.datatype, 0); + ibo.Unbind(); + }else{ + glDrawArrays(GL_POINTS, 0, vbo.num_elements); + } + + glDisableClientState(GL_VERTEX_ARRAY); + vbo.Unbind(); +} + +inline void RenderVboIboCbo(GlBuffer& vbo, GlBuffer& ibo, GlBuffer& cbo, bool draw_mesh, bool draw_color ) +{ + if(draw_color) { + cbo.Bind(); + glColorPointer(cbo.count_per_element, cbo.datatype, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } + + RenderVboIbo(vbo,ibo,draw_mesh); + + if(draw_color) { + glDisableClientState(GL_COLOR_ARRAY); + cbo.Unbind(); + } +} + +inline void RenderVboIboCboNbo(GlBuffer& vbo, GlBuffer& ibo, GlBuffer& cbo, GlBuffer& nbo, bool draw_mesh, bool draw_color, bool draw_normals) +{ + if(draw_color) { + cbo.Bind(); + glColorPointer(cbo.count_per_element, cbo.datatype, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } + + if(draw_normals) { + nbo.Bind(); + glNormalPointer(nbo.datatype, (GLsizei)(nbo.count_per_element * GlDataTypeBytes(nbo.datatype)),0); + glEnableClientState(GL_NORMAL_ARRAY); + } + + vbo.Bind(); + glVertexPointer(vbo.count_per_element, vbo.datatype, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(draw_mesh) { + ibo.Bind(); + glDrawElements(GL_TRIANGLE_STRIP,ibo.num_elements, ibo.datatype, 0); + ibo.Unbind(); + }else{ + glDrawArrays(GL_POINTS, 0, vbo.num_elements); + } + + if(draw_color) { + glDisableClientState(GL_COLOR_ARRAY); + cbo.Unbind(); + } + + if(draw_normals) { + glDisableClientState(GL_NORMAL_ARRAY); + nbo.Unbind(); + } + + glDisableClientState(GL_VERTEX_ARRAY); + vbo.Unbind(); +} + +inline void RenderVboIboNbo(GlBuffer& vbo, GlBuffer& ibo, GlBuffer& nbo, bool draw_mesh, bool draw_normals) +{ + vbo.Bind(); + glVertexPointer(vbo.count_per_element, vbo.datatype, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(draw_normals) { + nbo.Bind(); + glNormalPointer(nbo.datatype, (GLsizei)(nbo.count_per_element * GlDataTypeBytes(nbo.datatype)), 0); + glEnableClientState(GL_NORMAL_ARRAY); + } + + if(draw_mesh) { + ibo.Bind(); + glDrawElements(GL_TRIANGLE_STRIP,ibo.num_elements, ibo.datatype, 0); + ibo.Unbind(); + }else{ + glDrawArrays(GL_POINTS, 0, vbo.num_elements); + } + + if(draw_normals) { + glDisableClientState(GL_NORMAL_ARRAY); + nbo.Unbind(); + } + + glDisableClientState(GL_VERTEX_ARRAY); + vbo.Unbind(); +} + +} diff --git a/include/pangolin/handler/handler.h b/include/pangolin/handler/handler.h new file mode 100644 index 0000000..5cd948b --- /dev/null +++ b/include/pangolin/handler/handler.h @@ -0,0 +1,111 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) //prevent including Eigen in cuda files +#define USE_EIGEN +#endif + +#ifdef USE_EIGEN +#include +#endif + +#ifdef _OSX_ +#define PANGO_DFLT_HANDLER3D_ZF (1.0f/50.0f) +#else +#define PANGO_DFLT_HANDLER3D_ZF (1.0f/10.0f) +#endif + +namespace pangolin +{ + +// Forward declarations +struct View; + +/// Input Handler base class. +/// Virtual methods which recurse into sub-displays. +struct PANGOLIN_EXPORT Handler +{ + virtual ~Handler() {} + virtual void Keyboard(View&, unsigned char key, int x, int y, bool pressed); + virtual void Mouse(View&, MouseButton button, int x, int y, bool pressed, int button_state); + virtual void MouseMotion(View&, int x, int y, int button_state); + virtual void PassiveMouseMotion(View&, int x, int y, int button_state); + virtual void Special(View&, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state); +}; + +struct PANGOLIN_EXPORT HandlerScroll : Handler +{ + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int button_state); + void Special(View&, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state); +}; + +struct PANGOLIN_EXPORT Handler3D : Handler +{ + Handler3D(OpenGlRenderState& cam_state, AxisDirection enforce_up=AxisNone, float trans_scale=0.01f, float zoom_fraction= PANGO_DFLT_HANDLER3D_ZF); + + virtual bool ValidWinDepth(GLprecision depth); + virtual void PixelUnproject( View& view, GLprecision winx, GLprecision winy, GLprecision winz, GLprecision Pc[3]); + virtual void GetPosNormal(View& view, int x, int y, GLprecision p[3], GLprecision Pw[3], GLprecision Pc[3], GLprecision nw[3], GLprecision default_z = 1.0); + + void Keyboard(View&, unsigned char key, int x, int y, bool pressed); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int button_state); + void MouseMotion(View&, int x, int y, int button_state); + void Special(View&, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state); + +#ifdef USE_EIGEN + // Return selected point in world coordinates + inline Eigen::Vector3d Selected_P_w() const { + return Eigen::Map>(Pw).cast(); + } +#endif + +protected: + OpenGlRenderState* cam_state; + const static int hwin = 8; + AxisDirection enforce_up; + float tf; // translation factor + float zf; // zoom fraction + CameraSpec cameraspec; + GLprecision last_z; + float last_pos[2]; + GLprecision rot_center[3]; + + GLprecision p[3]; + GLprecision Pw[3]; + GLprecision Pc[3]; + GLprecision n[3]; +}; + +static Handler StaticHandler; +static HandlerScroll StaticHandlerScroll; + +} diff --git a/include/pangolin/handler/handler_enums.h b/include/pangolin/handler/handler_enums.h new file mode 100644 index 0000000..c84d648 --- /dev/null +++ b/include/pangolin/handler/handler_enums.h @@ -0,0 +1,94 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +namespace pangolin +{ + +// Supported Key modifiers for GlobalKeyPressCallback. +// e.g. PANGO_CTRL + 'r', PANGO_SPECIAL + GLUT_KEY_RIGHT, etc. +const int PANGO_SPECIAL = 128; +const int PANGO_CTRL = -96; +const int PANGO_OPTN = 132; + +// Ordinary keys +const int PANGO_KEY_TAB = 9; +const int PANGO_KEY_ESCAPE = 27; + +// Special Keys (same as GLUT_ defines) +const int PANGO_KEY_F1 = 1; +const int PANGO_KEY_F2 = 2; +const int PANGO_KEY_F3 = 3; +const int PANGO_KEY_F4 = 4; +const int PANGO_KEY_F5 = 5; +const int PANGO_KEY_F6 = 6; +const int PANGO_KEY_F7 = 7; +const int PANGO_KEY_F8 = 8; +const int PANGO_KEY_F9 = 9; +const int PANGO_KEY_F10 = 10; +const int PANGO_KEY_F11 = 11; +const int PANGO_KEY_F12 = 12; +const int PANGO_KEY_LEFT = 100; +const int PANGO_KEY_UP = 101; +const int PANGO_KEY_RIGHT = 102; +const int PANGO_KEY_DOWN = 103; +const int PANGO_KEY_PAGE_UP = 104; +const int PANGO_KEY_PAGE_DOWN = 105; +const int PANGO_KEY_HOME = 106; +const int PANGO_KEY_END = 107; +const int PANGO_KEY_INSERT = 108; + +enum MouseButton +{ + MouseButtonLeft = 1, + MouseButtonMiddle = 2, + MouseButtonRight = 4, + MouseWheelUp = 8, + MouseWheelDown = 16, + MouseWheelRight = 32, + MouseWheelLeft = 64, +}; + +enum KeyModifier +{ + KeyModifierShift = 1<<16, + KeyModifierCtrl = 1<<17, + KeyModifierAlt = 1<<18, + KeyModifierCmd = 1<<19, + KeyModifierFnc = 1<<20 +}; + +enum InputSpecial +{ + InputSpecialScroll, + InputSpecialZoom, + InputSpecialRotate, + InputSpecialTablet +}; + +} diff --git a/include/pangolin/handler/handler_glbuffer.h b/include/pangolin/handler/handler_glbuffer.h new file mode 100644 index 0000000..3d984f1 --- /dev/null +++ b/include/pangolin/handler/handler_glbuffer.h @@ -0,0 +1,48 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef HANDLER_GLBUFFER_H +#define HANDLER_GLBUFFER_H + +#include +#include + +namespace pangolin +{ + +struct Handler3DFramebuffer : public pangolin::Handler3D +{ + Handler3DFramebuffer(GlFramebuffer& fb, pangolin::OpenGlRenderState& cam_state, pangolin::AxisDirection enforce_up=pangolin::AxisNone, float trans_scale=0.01f); + void GetPosNormal(pangolin::View& view, int x, int y, GLprecision p[3], GLprecision Pw[3], GLprecision Pc[3], GLprecision /*n*/[3], GLprecision default_z); + +protected: + GlFramebuffer& fb; +}; + +} + +#endif // HANDLER_GLBUFFER_H diff --git a/include/pangolin/handler/handler_image.h b/include/pangolin/handler/handler_image.h new file mode 100644 index 0000000..c13435a --- /dev/null +++ b/include/pangolin/handler/handler_image.h @@ -0,0 +1,159 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace pangolin +{ + +class ImageViewHandler : public Handler +{ +public: + struct EventData { + EventData(View& v, ImageViewHandler& h) : view(v), handler(h) {} + View& view; + ImageViewHandler& handler; + }; + + struct OnSelectionEventData : public EventData { + OnSelectionEventData(View& v, ImageViewHandler& h, bool dragging) + : EventData(v,h), dragging(dragging) {} + bool dragging; + }; + + typedef std::function OnSelectionCallbackFn; + + // Default constructor: User must call SetDimensions() once image dimensions are known. + // Default range is [0,1] in x and y. + ImageViewHandler(); + + // View ranges store extremes of image (boundary of pixels) + // in 'discrete' coords, where 0,0 is center of top-left pixel. + ImageViewHandler(size_t w, size_t h); + + void SetDimensions(size_t w, size_t h); + + void UpdateView(); + + void glSetViewOrtho(); + + void glRenderTexture(pangolin::GlTexture& tex); + void glRenderTexture(GLuint tex, GLint width, GLint height); + + void glRenderOverlay(); + + void ScreenToImage(Viewport& v, float xpix, float ypix, float& ximg, float& yimg); + + void ImageToScreen(Viewport& v, float ximg, float yimg, float& xpix, float& ypix); + + bool UseNN() const; + + bool& UseNN(); + + bool& FlipTextureY(); + + pangolin::XYRangef& GetViewToRender(); + + float GetViewScale(); + + pangolin::XYRangef& GetView(); + + pangolin::XYRangef& GetDefaultView(); + + pangolin::XYRangef& GetSelection(); + + void GetHover(float& x, float& y); + + void SetView(const pangolin::XYRangef& range); + + void SetViewSmooth(const pangolin::XYRangef& range); + + void ScrollView(float x, float y); + + void ScrollViewSmooth(float x, float y); + + void ScaleView(float x, float y, float cx, float cy); + + void ScaleViewSmooth(float x, float y, float cx, float cy); + + void ResetView(); + + /////////////////////////////////////////////////////// + /// pangolin::Handler + /////////////////////////////////////////////////////// + + void Keyboard(View&, unsigned char key, int /*x*/, int /*y*/, bool pressed) override; + + void Mouse(View& view, pangolin::MouseButton button, int x, int y, bool pressed, int button_state) override; + + void MouseMotion(View& view, int x, int y, int button_state) override; + + void PassiveMouseMotion(View&, int /*x*/, int /*y*/, int /*button_state*/) override; + + void Special(View& view, pangolin::InputSpecial inType, float x, float y, float p1, float p2, float /*p3*/, float /*p4*/, int /*button_state*/) override; + + /////////////////////////////////////////////////////// + /// Callbacks + /////////////////////////////////////////////////////// + + OnSelectionCallbackFn OnSelectionCallback; + +protected: + void FixSelection(XYRangef& sel); + + void AdjustScale(); + + void AdjustTranslation(); + + static ImageViewHandler* to_link; + static float animate_factor; + + ImageViewHandler* linked_view_handler; + + pangolin::XYRangef rview_default; + pangolin::XYRangef rview_max; + pangolin::XYRangef rview; + pangolin::XYRangef target; + pangolin::XYRangef selection; + + float hover_img[2]; + int last_mouse_pos[2]; + + bool use_nn; + bool flipTextureY; +}; + +} diff --git a/include/pangolin/hud/oculus_hud.h b/include/pangolin/hud/oculus_hud.h new file mode 100644 index 0000000..26dbc66 --- /dev/null +++ b/include/pangolin/hud/oculus_hud.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace pangolin +{ + +class OculusHud; + +struct HandlerOculus : public pangolin::Handler +{ + HandlerOculus(OculusHud& oculus, pangolin::Handler* h = 0); + + void Keyboard(View&, unsigned char key, int x, int y, bool pressed); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int button_state); + void MouseMotion(View&, int x, int y, int button_state); + void PassiveMouseMotion(View&, int x, int y, int button_state); + void Special(View&, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state); + + void SetHandler(pangolin::Handler* h); + +protected: + OculusHud& oculus; + pangolin::Handler* handler; +}; + + +class OculusHud : public View +{ +public: + OculusHud(); + + void Render(); + void RenderFramebuffer(); + + void SetHandler(Handler *handler); + + void SetParams(float focalLength, float lensXOffset, float eye_y, float eye_z); + + OVR::HMDInfo& HmdInfo(); + + unsigned int NumEyes() const; + + View& CommonView(); + + OpenGlMatrix HeadTransform(); + OpenGlMatrix HeadTransformDelta(); + pangolin::GlFramebuffer& Framebuffer(); + pangolin::OpenGlRenderState& DefaultRenderState(); + + void UnwarpPoint(unsigned int view, const float in[2], float out[2]); + +protected: + // Oculus SDK Shader for applying lens and chromatic distortion. + static const char* PostProcessFullFragShaderSrc; + + void InitialiseOculus(); + void InitialiseFramebuffer(); + void InitialiseShader(); + + pangolin::GlTexture colourbuffer; + pangolin::GlRenderBuffer depthbuffer; + pangolin::GlFramebuffer framebuffer; + pangolin::GlSlProgram occ; + bool post_unwarp; + + OVR::Ptr pManager; + OVR::Ptr pHMD; + OVR::Ptr pSensor; + std::shared_ptr pFusionResult; + OVR::HMDInfo HMD; + OVR::Util::Render::StereoConfig stereo; + + View eyeview[2]; + View common; + pangolin::OpenGlRenderState default_cam; + HandlerOculus handler; + + // Center to Oculus transform + OpenGlMatrix T_oc; +}; + +} diff --git a/include/pangolin/image/copy.h b/include/pangolin/image/copy.h new file mode 100644 index 0000000..160fa6b --- /dev/null +++ b/include/pangolin/image/copy.h @@ -0,0 +1,45 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +namespace pangolin { + +// Hold a reference to an object to be copied +template +struct CopyObject { + CopyObject(const T& obj) : obj(obj) { } + const T& obj; +}; + +// Return copy wrapper for assignment to another object. +template +typename pangolin::CopyObject Copy(const T& obj) { + return typename pangolin::CopyObject(obj); +} + +} diff --git a/include/pangolin/image/image.h b/include/pangolin/image/image.h new file mode 100644 index 0000000..c34578f --- /dev/null +++ b/include/pangolin/image/image.h @@ -0,0 +1,414 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#ifdef PANGO_ENABLE_BOUNDS_CHECKS +# define PANGO_BOUNDS_ASSERT(...) PANGO_ENSURE(##__VA_ARGS__) +#else +# define PANGO_BOUNDS_ASSERT(...) ((void)0) +#endif + +// Allow user defined macro to be inserted into Image class. +#ifndef PANGO_EXTENSION_IMAGE +# define PANGO_EXTENSION_IMAGE +#endif + +namespace pangolin +{ + +// Simple image wrapper +template +struct Image +{ + inline Image() + : pitch(0), ptr(0), w(0), h(0) + { + } + + inline Image(T* ptr, size_t w, size_t h, size_t pitch) + : pitch(pitch), ptr(ptr), w(w), h(h) + { + } + + + PANGO_HOST_DEVICE inline + size_t SizeBytes() const + { + return pitch * h; + } + + PANGO_HOST_DEVICE inline + size_t Area() const + { + return w * h; + } + + PANGO_HOST_DEVICE inline + bool IsValid() const + { + return ptr != 0; + } + + PANGO_HOST_DEVICE inline + bool IsContiguous() const + { + return w*sizeof(T) == pitch; + } + + ////////////////////////////////////////////////////// + // Iterators + ////////////////////////////////////////////////////// + + PANGO_HOST_DEVICE inline + T* begin() + { + return ptr; + } + + PANGO_HOST_DEVICE inline + T* end() + { + return RowPtr(h-1) + w; + } + + PANGO_HOST_DEVICE inline + const T* begin() const + { + return ptr; + } + + PANGO_HOST_DEVICE inline + const T* end() const + { + return RowPtr(h-1) + w; + } + + PANGO_HOST_DEVICE inline + size_t size() const + { + return w*h; + } + + ////////////////////////////////////////////////////// + // Image transforms + ////////////////////////////////////////////////////// + + template + PANGO_HOST_DEVICE inline + void Transform(UnaryOperation unary_op) + { + PANGO_ASSERT(IsValid()); + + for(size_t y=0; y < h; ++y) { + T* el = RowPtr(y); + const T* el_end = el+w; + for( ; el != el_end; ++el) { + *el = unary_op(*el); + } + } + } + + PANGO_HOST_DEVICE inline + void Fill(const T& val) + { + Transform( [&](const T&) {return val;} ); + } + + PANGO_HOST_DEVICE inline + void Replace(const T& oldval, const T& newval) + { + Transform( [&](const T& val) { + return (val == oldval) ? newval : val; + }); + } + + inline + void Memset(unsigned char v = 0) + { + PANGO_ASSERT(IsValid()); + if(IsContiguous()) { + ::pangolin::Memset((char*)ptr, v, pitch*h); + }else{ + for(size_t y=0; y < h; ++y) { + ::pangolin::Memset((char*)RowPtr(y), v, pitch); + } + } + } + + inline + void CopyFrom(const Image& img) + { + if(IsValid() && img.IsValid()) { + PANGO_ASSERT(w >= img.w && h >= img.h); + PitchedCopy((char*)ptr,pitch,(char*)img.ptr,img.pitch, std::min(img.w,w)*sizeof(T), std::min(img.h,h) ); + }else if( img.IsValid() != IsValid() ){ + PANGO_ASSERT(false && "Cannot copy from / to an unasigned image." ); + } + } + + ////////////////////////////////////////////////////// + // Reductions + ////////////////////////////////////////////////////// + + template + PANGO_HOST_DEVICE inline + T Accumulate(const T init, BinaryOperation binary_op) + { + PANGO_ASSERT(IsValid()); + + T val = init; + for(size_t y=0; y < h; ++y) { + T* el = RowPtr(y); + const T* el_end = el+w; + for(; el != el_end; ++el) { + val = binary_op(val, *el); + } + } + return val; + } + + std::pair MinMax() const + { + PANGO_ASSERT(IsValid()); + + std::pair minmax(std::numeric_limits::max(), std::numeric_limits::lowest()); + for(size_t r=0; r < h; ++r) { + const T* ptr = RowPtr(r); + const T* end = ptr + w; + while( ptr != end) { + minmax.first = std::min(*ptr, minmax.first); + minmax.second = std::max(*ptr, minmax.second); + ++ptr; + } + } + return minmax; + } + + template + Tout Sum() const + { + return Accumulate((T)0, [](const T& lhs, const T& rhs){ return lhs + rhs; }); + } + + template + Tout Mean() const + { + return Sum() / Area(); + } + + + ////////////////////////////////////////////////////// + // Direct Pixel Access + ////////////////////////////////////////////////////// + + PANGO_HOST_DEVICE inline + T* RowPtr(size_t y) + { + return (T*)((unsigned char*)(ptr) + y*pitch); + } + + PANGO_HOST_DEVICE inline + const T* RowPtr(size_t y) const + { + return (T*)((unsigned char*)(ptr) + y*pitch); + } + + PANGO_HOST_DEVICE inline + T& operator()(size_t x, size_t y) + { + PANGO_BOUNDS_ASSERT( InBounds(x,y) ); + return RowPtr(y)[x]; + } + + PANGO_HOST_DEVICE inline + const T& operator()(size_t x, size_t y) const + { + PANGO_BOUNDS_ASSERT( InBounds(x,y) ); + return RowPtr(y)[x]; + } + + template + PANGO_HOST_DEVICE inline + T& operator()(const TVec& p) + { + PANGO_BOUNDS_ASSERT( InBounds(p[0],p[1]) ); + return RowPtr(p[1])[p[0]]; + } + + template + PANGO_HOST_DEVICE inline + const T& operator()(const TVec& p) const + { + PANGO_BOUNDS_ASSERT( InBounds(p[0],p[1]) ); + return RowPtr(p[1])[p[0]]; + } + + PANGO_HOST_DEVICE inline + T& operator[](size_t ix) + { + PANGO_BOUNDS_ASSERT( InImage(ptr+ix) ); + return ptr[ix]; + } + + PANGO_HOST_DEVICE inline + const T& operator[](size_t ix) const + { + PANGO_BOUNDS_ASSERT( InImage(ptr+ix) ); + return ptr[ix]; + } + + ////////////////////////////////////////////////////// + // Bounds Checking + ////////////////////////////////////////////////////// + + PANGO_HOST_DEVICE + bool InImage(const T* ptest) const + { + return ptr <= ptest && ptest < RowPtr(h); + } + + PANGO_HOST_DEVICE inline + bool InBounds(int x, int y) const + { + return 0 <= x && x < (int)w && 0 <= y && y < (int)h; + } + + PANGO_HOST_DEVICE inline + bool InBounds(float x, float y, float border) const + { + return border <= x && x < (w-border) && border <= y && y < (h-border); + } + + template + PANGO_HOST_DEVICE inline + bool InBounds( const TVec& p, const TBorder border = (TBorder)0 ) const + { + return border <= p[0] && p[0] < ((int)w - border) && border <= p[1] && p[1] < ((int)h - border); + } + + ////////////////////////////////////////////////////// + // Obtain slices / subimages + ////////////////////////////////////////////////////// + + PANGO_HOST_DEVICE inline + const Image SubImage(size_t x, size_t y, size_t width, size_t height) const + { + PANGO_ASSERT( (x+width) <= w && (y+height) <= h); + return Image( RowPtr(y)+x, width, height, pitch); + } + + PANGO_HOST_DEVICE inline + Image SubImage(size_t x, size_t y, size_t width, size_t height) + { + PANGO_ASSERT( (x+width) <= w && (y+height) <= h); + return Image( RowPtr(y)+x, width, height, pitch); + } + + PANGO_HOST_DEVICE inline + Image Row(int y) const + { + return SubImage(0,y,w,1); + } + + PANGO_HOST_DEVICE inline + Image Col(int x) const + { + return SubImage(x,0,1,h); + } + + ////////////////////////////////////////////////////// + // Data mangling + ////////////////////////////////////////////////////// + + template + PANGO_HOST_DEVICE inline + Image Reinterpret() const + { + PANGO_ASSERT(sizeof(TRecast) == sizeof(T), "sizeof(TRecast) must match sizeof(T): % != %", sizeof(TRecast), sizeof(T) ); + return UnsafeReinterpret(); + } + + template + PANGO_HOST_DEVICE inline + Image UnsafeReinterpret() const + { + return Image((TRecast*)ptr,w,h,pitch); + } + + ////////////////////////////////////////////////////// + // Deprecated methods + ////////////////////////////////////////////////////// + +// PANGOLIN_DEPRECATED inline + Image(size_t w, size_t h, size_t pitch, T* ptr) + : pitch(pitch), ptr(ptr), w(w), h(h) + { + } + + // Use RAII/move aware pangolin::ManagedImage instead +// PANGOLIN_DEPRECATED inline + void Dealloc() + { + if(ptr) { + ::operator delete(ptr); + ptr = nullptr; + } + } + + // Use RAII/move aware pangolin::ManagedImage instead +// PANGOLIN_DEPRECATED inline + void Alloc(size_t w, size_t h, size_t pitch) + { + Dealloc(); + this->w = w; + this->h = h; + this->pitch = pitch; + this->ptr = (T*)::operator new(h*pitch); + } + + ////////////////////////////////////////////////////// + // Data members + ////////////////////////////////////////////////////// + + size_t pitch; + T* ptr; + size_t w; + size_t h; + + PANGO_EXTENSION_IMAGE +}; + +} diff --git a/include/pangolin/image/image_convert.h b/include/pangolin/image/image_convert.h new file mode 100644 index 0000000..36ae99f --- /dev/null +++ b/include/pangolin/image/image_convert.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace pangolin +{ + +template +void ImageConvert(Image& dst, const Image& src, To scale = 1.0) +{ + for(unsigned int y = 0; y < dst.h; ++y) + { + const T* prs = src.RowPtr(y); + To* prd = dst.RowPtr(y); + for(unsigned int x = 0; x < dst.w; ++x) + { + *(prd++) = scale * ComponentCast::cast(*(prs++)); + } + } +} + +template +ManagedImage ImageConvert(const Image& src, To scale = 1.0) +{ + ManagedImage dst(src.w, src.h); + ImageConvert(dst,src,scale); + return dst; +} + +} diff --git a/include/pangolin/image/image_io.h b/include/pangolin/image/image_io.h new file mode 100644 index 0000000..fcc594b --- /dev/null +++ b/include/pangolin/image/image_io.h @@ -0,0 +1,65 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include + +namespace pangolin { + +PANGOLIN_EXPORT +TypedImage LoadImage(std::istream& in, ImageFileType file_type); + +PANGOLIN_EXPORT +TypedImage LoadImage(const std::string& filename, ImageFileType file_type); + +PANGOLIN_EXPORT +TypedImage LoadImage(const std::string& filename); + +PANGOLIN_EXPORT +TypedImage LoadImage(const std::string& filename, const PixelFormat& raw_fmt, size_t raw_width, size_t raw_height, size_t raw_pitch); + +/// Quality \in [0..100] for lossy formats +PANGOLIN_EXPORT +void SaveImage(const Image& image, const pangolin::PixelFormat& fmt, std::ostream& out, ImageFileType file_type, bool top_line_first = true, float quality = 100.0f); + +/// Quality \in [0..100] for lossy formats +PANGOLIN_EXPORT +void SaveImage(const Image& image, const pangolin::PixelFormat& fmt, const std::string& filename, ImageFileType file_type, bool top_line_first = true, float quality = 100.0f); + +/// Quality \in [0..100] for lossy formats +PANGOLIN_EXPORT +void SaveImage(const Image& image, const pangolin::PixelFormat& fmt, const std::string& filename, bool top_line_first = true, float quality = 100.0f); + +/// Quality \in [0..100] for lossy formats +PANGOLIN_EXPORT +void SaveImage(const TypedImage& image, const std::string& filename, bool top_line_first = true, float quality = 100.0f); + +} diff --git a/include/pangolin/image/image_utils.h b/include/pangolin/image/image_utils.h new file mode 100644 index 0000000..f96fcf9 --- /dev/null +++ b/include/pangolin/image/image_utils.h @@ -0,0 +1,153 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace pangolin +{ + +namespace internal +{ + +template +std::pair GetMinMax(const Image& img, size_t channels) +{ + const size_t max_channels = 3; + const size_t colour_channels = std::min(channels, max_channels); + std::pair chan_mm[max_channels]; + for(size_t c = 0; c < max_channels; ++c) + { + chan_mm[c].first = +std::numeric_limits::max(); + chan_mm[c].second = -std::numeric_limits::max(); + } + + for(size_t y = 0; y < img.h; ++y) + { + T* pix = (T*)((char*)img.ptr + y * img.pitch); + for(size_t x = 0; x < img.w; ++x) + { + for(size_t c = 0; c < colour_channels; ++c) + { + if(pix[c] < chan_mm[c].first) + chan_mm[c].first = (float)pix[c]; + if(pix[c] > chan_mm[c].second) + chan_mm[c].second = (float)pix[c]; + } + + pix += channels; + } + } + + // Find min / max of all channels, ignoring 4th alpha channel + std::pair mm = chan_mm[0]; + for(size_t c = 1; c < colour_channels; ++c) + { + mm.first = std::min(mm.first, chan_mm[c].first); + mm.second = std::max(mm.second, chan_mm[c].second); + } + + return mm; +} + +template +pangolin::Image GetImageRoi( pangolin::Image img, size_t channels, const pangolin::XYRangei& roi ) +{ + return pangolin::Image( + img.RowPtr(std::min(roi.y.min,roi.y.max)) + channels*std::min(roi.x.min,roi.x.max), + roi.x.AbsSize(), roi.y.AbsSize(), + img.pitch + ); +} + +template +std::pair GetOffsetScale(const pangolin::Image& img, size_t channels, float type_max, float format_max) +{ + // Find min / max of all channels, ignoring 4th alpha channel + const std::pair mm = internal::GetMinMax(img,channels); + const float type_scale = format_max / type_max; + const float offset = -type_scale* mm.first; + const float scale = type_max / (mm.second - mm.first); + return std::pair(offset, scale); +} + +} // internal + +inline std::pair GetMinMax( + const Image& img, + XYRangei iroi, const GlPixFormat& glfmt +) { + using namespace internal; + + iroi.Clamp(0, (int)img.w - 1, 0, (int)img.h - 1); + + const size_t num_channels = pangolin::GlFormatChannels(glfmt.glformat); + + if(glfmt.gltype == GL_UNSIGNED_BYTE) { + return GetMinMax(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels); + } else if(glfmt.gltype == GL_UNSIGNED_SHORT) { + return GetMinMax(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels); + } else if(glfmt.gltype == GL_FLOAT) { + return GetMinMax(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels); + } else if(glfmt.gltype == GL_DOUBLE) { + return GetMinMax(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels); + } else { + return std::pair(std::numeric_limits::max(), std::numeric_limits::lowest()); + } +} + +inline std::pair GetOffsetScale( + const pangolin::Image& img, + pangolin::XYRangei iroi, const pangolin::GlPixFormat& glfmt +) { + using namespace internal; + + iroi.Clamp(0, (int)img.w-1, 0, (int)img.h-1 ); + + const size_t num_channels = pangolin::GlFormatChannels(glfmt.glformat); + + if(glfmt.gltype == GL_UNSIGNED_BYTE) { + return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels, 255.0f, 1.0f); + }else if(glfmt.gltype == GL_UNSIGNED_SHORT) { + return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels, 65535.0f, 1.0f); + }else if(glfmt.gltype == GL_FLOAT) { + return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels, 1.0f, 1.0f); + }else if(glfmt.gltype == GL_DOUBLE) { + return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret(), num_channels, iroi), num_channels, 1.0f, 1.0f); + }else{ + return std::pair(0.0f, 1.0f); + } +} + +} diff --git a/include/pangolin/image/managed_image.h b/include/pangolin/image/managed_image.h new file mode 100644 index 0000000..dd21c43 --- /dev/null +++ b/include/pangolin/image/managed_image.h @@ -0,0 +1,175 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin { + +template using DefaultImageAllocator = std::allocator; + +// Image that manages it's own memory, storing a strong pointer to it's memory +template > +class ManagedImage : public Image +{ +public: + // Destructor + inline + ~ManagedImage() + { + Deallocate(); + } + + // Null image + inline + ManagedImage() + { + } + + // Row image + inline + ManagedImage(size_t w) + : Image( + Allocator().allocate(w), + w, 1, w*sizeof(T) + ) + { + } + + inline + ManagedImage(size_t w, size_t h) + : Image( + Allocator().allocate(w*h), + w, h, w*sizeof(T) + ) + { + } + + inline + ManagedImage(size_t w, size_t h, size_t pitch_bytes) + : Image( + Allocator().allocate( (h*pitch_bytes) / sizeof(T) + 1), + w, h, pitch_bytes + ) + { + } + + // Not copy constructable + inline + ManagedImage( const ManagedImage& other ) = delete; + + // Move constructor + inline + ManagedImage(ManagedImage&& img) + { + *this = std::move(img); + } + + // Move asignment + inline + void operator=(ManagedImage&& img) + { + Deallocate(); + Image::pitch = img.pitch; + Image::ptr = img.ptr; + Image::w = img.w; + Image::h = img.h; + img.ptr = nullptr; + } + + // Explicit copy constructor + template + ManagedImage( const CopyObject& other ) + { + CopyFrom(other.obj); + } + + // Explicit copy assignment + template + void operator=(const CopyObject& other) + { + CopyFrom(other.obj); + } + + inline + void Swap(ManagedImage& img) + { + std::swap(img.pitch, Image::pitch); + std::swap(img.ptr, Image::ptr); + std::swap(img.w, Image::w); + std::swap(img.h, Image::h); + } + + inline + void CopyFrom(const Image& img) + { + if(!Image::IsValid() || Image::w != img.w || Image::h != img.h) { + Reinitialise(img.w,img.h); + } + Image::CopyFrom(img); + } + + inline + void Reinitialise(size_t w, size_t h) + { + if(!Image::ptr || Image::w != w || Image::h != h) { + *this = ManagedImage(w,h); + } + } + + inline + void Reinitialise(size_t w, size_t h, size_t pitch) + { + if(!Image::ptr || Image::w != w || Image::h != h || Image::pitch != pitch) { + *this = ManagedImage(w,h,pitch); + } + } + + inline void Deallocate() + { + if (Image::ptr) { + Allocator().deallocate(Image::ptr, (Image::h * Image::pitch) / sizeof(T) ); + Image::ptr = nullptr; + } + } + + // Move asignment + template inline + void OwnAndReinterpret(ManagedImage&& img) + { + Deallocate(); + Image::pitch = img.pitch; + Image::ptr = (T*)img.ptr; + Image::w = img.w; + Image::h = img.h; + img.ptr = nullptr; + } +}; + +} diff --git a/include/pangolin/image/memcpy.h b/include/pangolin/image/memcpy.h new file mode 100644 index 0000000..88d3f0c --- /dev/null +++ b/include/pangolin/image/memcpy.h @@ -0,0 +1,110 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include + +#ifdef HAVE_CUDA +# include +#endif + +namespace pangolin { + +template +PANGO_HOST_DEVICE inline +bool IsDevicePtr(T* ptr) +{ +#ifdef HAVE_CUDA + cudaPointerAttributes attributes; + cudaError_t res = cudaPointerGetAttributes(&attributes,ptr); + + //Flushing the error flag for future CUDA error checks + if(res != cudaSuccess) + { + cudaGetLastError(); + return false; + } + + return attributes.memoryType == cudaMemoryTypeDevice; +#else + PANGOLIN_UNUSED(ptr); + return false; +#endif +} + +PANGO_HOST_DEVICE inline +void MemCopy(void *dst, const void *src, size_t size_bytes) +{ +#ifdef HAVE_CUDA + cudaMemcpy(dst,src, size_bytes, cudaMemcpyDefault); +#else + std::memcpy(dst, src, size_bytes); +#endif +} + +inline +void PitchedCopy(char* dst, unsigned int dst_pitch_bytes, const char* src, unsigned int src_pitch_bytes, unsigned int width_bytes, unsigned int height) +{ +#ifdef HAVE_CUDA + cudaMemcpy2D(dst, dst_pitch_bytes, src, src_pitch_bytes, width_bytes, height, cudaMemcpyDefault); +#else + if(dst_pitch_bytes == width_bytes && src_pitch_bytes == width_bytes ) { + std::memcpy(dst, src, height * width_bytes); + }else{ + for(unsigned int row=0; row < height; ++row) { + std::memcpy(dst, src, width_bytes); + dst += dst_pitch_bytes; + src += src_pitch_bytes; + } + } +#endif +} + +PANGO_HOST_DEVICE inline +void Memset(char* ptr, unsigned char v, size_t size_bytes) +{ +#ifdef __CUDA_ARCH__ + // Called in kernel + char* end = ptr + size_bytes; + for(char* p=ptr; p != end; ++p) *p = v; +#else +# ifdef HAVE_CUDA + if(IsDevicePtr(ptr)) + { + cudaMemset(ptr, v, size_bytes); + }else +# endif // HAVE_CUDA + { + std::memset(ptr, v, size_bytes); + } +#endif // __CUDA_ARCH__ +} + +} diff --git a/include/pangolin/image/pixel_format.h b/include/pangolin/image/pixel_format.h new file mode 100644 index 0000000..91c37bc --- /dev/null +++ b/include/pangolin/image/pixel_format.h @@ -0,0 +1,65 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011-2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +struct PANGOLIN_EXPORT PixelFormat +{ + // Previously, VideoInterface::PixFormat returned a string. + // For compatibility, make this string convertable + inline operator std::string() const { return format; } + + std::string format; + unsigned int channels; + unsigned int channel_bits[4]; + unsigned int bpp; + bool planar; +}; + + +//! Return Pixel Format properties given string specification in +//! FFMPEG notation. +PANGOLIN_EXPORT +PixelFormat PixelFormatFromString(const std::string& format); + +//////////////////////////////////////////////////////////////////// +/// Deprecated aliases for above + +PANGOLIN_DEPRECATED +typedef PixelFormat VideoPixelFormat; +PANGOLIN_DEPRECATED +inline PixelFormat VideoFormatFromString(const std::string& format) { + return PixelFormatFromString(format); +} + +} diff --git a/include/pangolin/image/typed_image.h b/include/pangolin/image/typed_image.h new file mode 100644 index 0000000..39e5062 --- /dev/null +++ b/include/pangolin/image/typed_image.h @@ -0,0 +1,91 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin { + +struct TypedImage : public ManagedImage +{ + typedef ManagedImage Base; + + inline TypedImage() + : Base() + { + } + + inline TypedImage(size_t w, size_t h, const PixelFormat& fmt) + : Base(w,h,w*fmt.bpp/8), fmt(fmt) + { + } + + inline TypedImage(size_t w, size_t h, const PixelFormat& fmt, size_t pitch ) + : Base(w,h, pitch), fmt(fmt) + { + } + + inline + void Reinitialise(size_t w, size_t h, const PixelFormat& fmt) + { + Base::Reinitialise(w, h, w*fmt.bpp/8); + this->fmt = fmt; + } + + inline + void Reinitialise(size_t w, size_t h, const PixelFormat& fmt, size_t pitch) + { + Base::Reinitialise(w, h, pitch); + this->fmt = fmt; + } + + // Not copy constructable + inline + TypedImage( const TypedImage& other ) = delete; + + // Move constructor + inline + TypedImage(TypedImage&& img) + { + *this = std::move(img); + } + + // Move asignment + inline + void operator=(TypedImage&& img) + { + fmt = img.fmt; + Base::operator =( std::move(img)); + } + + + PixelFormat fmt; +}; + +} diff --git a/include/pangolin/ios/PangolinAppDelegate.h b/include/pangolin/ios/PangolinAppDelegate.h new file mode 100644 index 0000000..6bae208 --- /dev/null +++ b/include/pangolin/ios/PangolinAppDelegate.h @@ -0,0 +1,36 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import "PangolinUIView.h" + +@interface PangolinAppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/include/pangolin/ios/PangolinUIView.h b/include/pangolin/ios/PangolinUIView.h new file mode 100644 index 0000000..f7aea7f --- /dev/null +++ b/include/pangolin/ios/PangolinUIView.h @@ -0,0 +1,22 @@ +// +// GlTestViewController.h +// gltest +// +// Created by Steven Lovegrove on 30/01/2014. +// Copyright (c) 2014 Steven Lovegrove. All rights reserved. +// + +#import + +#include +#include + +@interface PangolinUIView : UIView { + CAEAGLLayer* _eaglLayer; + EAGLContext* _context; + + GLuint _colorRenderBuffer; + GLuint _depthRenderBuffer; +} + +@end \ No newline at end of file diff --git a/include/pangolin/log/packet.h b/include/pangolin/log/packet.h new file mode 100644 index 0000000..a904373 --- /dev/null +++ b/include/pangolin/log/packet.h @@ -0,0 +1,70 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include + +namespace pangolin { + +// Encapsulate serialized reading of Packet from stream. +struct Packet +{ + Packet(PacketStream& s, std::unique_lock&& mutex, std::vector& srcs); + Packet(const Packet&) = delete; + Packet(Packet&& o); + ~Packet(); + + size_t BytesRead() const; + int BytesRemaining() const; + + PacketStream& Stream() + { + return _stream; + } + + PacketStreamSourceId src; + int64_t time; + size_t size; + size_t sequence_num; + picojson::value meta; + std::streampos frame_streampos; + +private: + void ParsePacketHeader(PacketStream& s, std::vector& srcs); + void ReadRemaining(); + + PacketStream& _stream; + + std::unique_lock lock; + + std::streampos data_streampos; + size_t _data_len; +}; + +} diff --git a/include/pangolin/log/packetstream.h b/include/pangolin/log/packetstream.h new file mode 100644 index 0000000..1b9c0f3 --- /dev/null +++ b/include/pangolin/log/packetstream.h @@ -0,0 +1,111 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include + +#include +#include + +namespace pangolin +{ + +class PacketStream: public std::ifstream +{ +public: + PacketStream() + : _is_pipe(false) + { + cclear(); + } + + PacketStream(const std::string& filename) + : Base(filename.c_str(), std::ios::in | std::ios::binary), + _is_pipe(IsPipe(filename)) + { + cclear(); + } + + bool seekable() const + { + return is_open() && !_is_pipe; + } + + void open(const std::string& filename) + { + close(); + _is_pipe = IsPipe(filename); + Base::open(filename.c_str(), std::ios::in | std::ios::binary); + } + + void close() + { + cclear(); + if (Base::is_open()) Base::close(); + } + + void seekg(std::streampos target); + + void seekg(std::streamoff off, std::ios_base::seekdir way); + + std::streampos tellg(); + + size_t read(char* target, size_t len); + + char get(); + + size_t skip(size_t len); + + size_t readUINT(); + + int64_t readTimestamp(); + + pangoTagType peekTag(); + + pangoTagType readTag(); + + pangoTagType readTag(pangoTagType); + + pangoTagType syncToTag(); + +private: + using Base = std::ifstream; + + bool _is_pipe; + pangoTagType _tag; + + // Amount of frame data left to read. Tracks our position within a data block. + + + void cclear() { + _tag = 0; + } +}; + + +} diff --git a/include/pangolin/log/packetstream_reader.h b/include/pangolin/log/packetstream_reader.h new file mode 100644 index 0000000..d929eb4 --- /dev/null +++ b/include/pangolin/log/packetstream_reader.h @@ -0,0 +1,120 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT PacketStreamReader +{ +public: + PacketStreamReader(); + + PacketStreamReader(const std::string& filename); + + ~PacketStreamReader(); + + void Open(const std::string& filename); + + void Close(); + + const std::vector& + Sources() const + { + return _sources; + } + + // Grab Next available frame packetstream + Packet NextFrame(); + + // Grab Next available frame in packetstream from src, discarding other frames. + Packet NextFrame(PacketStreamSourceId src); + + bool Good() const + { + return _stream.good(); + } + + // Jumps to a particular packet. + size_t Seek(PacketStreamSourceId src, size_t framenum); + + // Jumps to the first packet with time >= time + size_t Seek(PacketStreamSourceId src, SyncTime::TimePoint time); + + void FixFileIndex(); + +private: + bool GoodToRead(); + + bool SetupIndex(); + + void ParseHeader(); + + void ParseNewSource(); + + bool ParseIndex(); + + void RebuildIndex(); + + void AppendIndex(); + + std::streampos ParseFooter(); + + void SkipSync(); + + void ReSync() { + _stream.syncToTag(); + } + + std::string _filename; + std::vector _sources; + SyncTime::TimePoint packet_stream_start; + + PacketStream _stream; + std::recursive_mutex _mutex; + + bool _is_pipe; + int _pipe_fd; +}; + + + + + + + + +} diff --git a/include/pangolin/log/packetstream_source.h b/include/pangolin/log/packetstream_source.h new file mode 100644 index 0000000..7a79e49 --- /dev/null +++ b/include/pangolin/log/packetstream_source.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +namespace pangolin { + +using PacketStreamSourceId = size_t; + +struct PANGOLIN_EXPORT PacketStreamSource +{ + struct PacketInfo + { + std::streampos pos; + int64_t capture_time; + }; + + PacketStreamSource() + : id(static_cast(-1)), + version(0), + data_alignment_bytes(1), + data_size_bytes(0), + next_packet_id(0) + { + } + + std::streampos FindSeekLocation(size_t packet_id) + { + if(packet_id < index.size()) { + return index[packet_id].pos; + }else{ + return std::streampos(-1); + } + + } + + int64_t NextPacketTime() const + { + if(next_packet_id < index.size()) { + return index[next_packet_id].capture_time; + }else{ + return 0; + } + } + + std::string driver; + size_t id; + std::string uri; + picojson::value info; + int64_t version; + int64_t data_alignment_bytes; + std::string data_definitions; + int64_t data_size_bytes; + + // Index keyed by packet_id + std::vector index; + + // Based on current position in stream + size_t next_packet_id; +}; + +} diff --git a/include/pangolin/log/packetstream_tags.h b/include/pangolin/log/packetstream_tags.h new file mode 100644 index 0000000..13216f3 --- /dev/null +++ b/include/pangolin/log/packetstream_tags.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace pangolin { + +using pangoTagType = uint32_t; + +const static std::string PANGO_MAGIC = "PANGO"; + +const static std::string pss_src_driver = "driver"; +const static std::string pss_src_id = "id"; +const static std::string pss_src_info = "info"; +const static std::string pss_src_uri = "uri"; +const static std::string pss_src_packet = "packet"; +const static std::string pss_src_version = "version"; +const static std::string pss_pkt_alignment_bytes = "alignment_bytes"; +const static std::string pss_pkt_definitions = "definitions"; +const static std::string pss_pkt_size_bytes = "size_bytes"; +const static std::string pss_pkt_format_written = "format_written"; + +const unsigned int TAG_LENGTH = 3; + +#define PANGO_TAG(a,b,c) ( (c<<16) | (b<<8) | a) +const uint32_t TAG_PANGO_HDR = PANGO_TAG('L', 'I', 'N'); +const uint32_t TAG_PANGO_MAGIC = PANGO_TAG('P', 'A', 'N'); +const uint32_t TAG_PANGO_SYNC = PANGO_TAG('S', 'Y', 'N'); +const uint32_t TAG_PANGO_STATS = PANGO_TAG('S', 'T', 'A'); +const uint32_t TAG_PANGO_FOOTER = PANGO_TAG('F', 'T', 'R'); +const uint32_t TAG_ADD_SOURCE = PANGO_TAG('S', 'R', 'C'); +const uint32_t TAG_SRC_JSON = PANGO_TAG('J', 'S', 'N'); +const uint32_t TAG_SRC_PACKET = PANGO_TAG('P', 'K', 'T'); +const uint32_t TAG_END = PANGO_TAG('E', 'N', 'D'); +#undef PANGO_TAG + +inline std::string tagName(int v) +{ + char b[4]; + b[0] = v&0xff; + b[1] = (v>>8)&0xff; + b[2] = (v>>16)&0xff; + b[3] = 0x00; + return std::string(b); +} + +} diff --git a/include/pangolin/log/packetstream_writer.h b/include/pangolin/log/packetstream_writer.h new file mode 100644 index 0000000..195ff92 --- /dev/null +++ b/include/pangolin/log/packetstream_writer.h @@ -0,0 +1,173 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT PacketStreamWriter +{ +public: + PacketStreamWriter() + : _stream(&_buffer), _indexable(false), _open(false), _bytes_written(0) + { + _stream.exceptions(std::ostream::badbit); + } + + PacketStreamWriter(const std::string& filename, size_t buffer_size = 100*1024*1024) + : _buffer(pangolin::PathExpand(filename), buffer_size), _stream(&_buffer), + _indexable(!IsPipe(filename)), _open(_stream.good()), _bytes_written(0) + { + _stream.exceptions(std::ostream::badbit); + WriteHeader(); + } + + ~PacketStreamWriter() { + Close(); + } + + void Open(const std::string& filename, size_t buffer_size = 100 * 1024 * 1024) + { + Close(); + _buffer.open(filename, buffer_size); + _open = _stream.good(); + _bytes_written = 0; + _indexable = !IsPipe(filename); + WriteHeader(); + } + + void Close() + { + if (_open) + { + if (_indexable) { + WriteEnd(); + } + _buffer.close(); + _open = false; + } + } + + // Does not write footer or index. + void ForceClose() + { + if (_open) + { + _buffer.force_close(); + Close(); + } + } + + + // Writes to the stream immediately upon add. Return source id # and writes + // source id # to argument struct + PacketStreamSourceId AddSource(PacketStreamSource& source); + + // If constructor is called inline + PacketStreamSourceId AddSource(const PacketStreamSource& source); + + void WriteSourcePacket( + PacketStreamSourceId src, const char* source,const int64_t receive_time_us, + size_t sourcelen, const picojson::value& meta = picojson::value() + ); + + // For stream read/write synchronization. Note that this is NOT the same as + // time synchronization on playback of iPacketStreams. + void WriteSync(); + + // Writes the end of the stream data, including the index. Does NOT close + // the underlying ostream. + void WriteEnd(); + + const std::vector& Sources() const { + return _sources; + } + + bool IsOpen() const { + return _open; + } + +private: + void WriteHeader(); + void Write(const PacketStreamSource&); + void WriteMeta(PacketStreamSourceId src, const picojson::value& data); + + threadedfilebuf _buffer; + std::ostream _stream; + bool _indexable, _open; + + std::vector _sources; + size_t _bytes_written; + std::recursive_mutex _lock; +}; + +inline void writeCompressedUnsignedInt(std::ostream& writer, size_t n) +{ + while (n >= 0x80) + { + writer.put(0x80 | (n & 0x7F)); + n >>= 7; + } + writer.put(static_cast(n)); +} + +inline void writeTimestamp(std::ostream& writer, int64_t time_us) +{ + writer.write(reinterpret_cast(&time_us), sizeof(decltype(time_us))); +} + +inline void writeTag(std::ostream& writer, const pangoTagType tag) +{ + writer.write(reinterpret_cast(&tag), TAG_LENGTH); +} + +inline picojson::value SourceStats(const std::vector& srcs) +{ + picojson::value stat; + stat["num_sources"] = srcs.size(); + stat["src_packet_index"] = picojson::array(); + stat["src_packet_times"] = picojson::array(); + + for(auto& src : srcs) { + picojson::array pkt_index, pkt_times; + for (const PacketStreamSource::PacketInfo& frame : src.index) { + pkt_index.emplace_back(frame.pos); + pkt_times.emplace_back(frame.capture_time); + } + stat["src_packet_index"].push_back(std::move(pkt_index)); + stat["src_packet_times"].push_back(std::move(pkt_times)); + } + return stat; +} + +} diff --git a/include/pangolin/log/playback_session.h b/include/pangolin/log/playback_session.h new file mode 100644 index 0000000..fb28d13 --- /dev/null +++ b/include/pangolin/log/playback_session.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace pangolin { + +class Params; + +class PlaybackSession +{ +public: + // Singleton Instance + static std::shared_ptr Default(); + + // Return thread-safe, shared instance of PacketStreamReader, providing + // serialised read for PacketStreamReader + std::shared_ptr Open(const std::string& filename) + { + const std::string path = SanitizePath(PathExpand(filename)); + + auto i = readers.find(path); + if(i == readers.end()) { + auto psr = std::make_shared(path); + readers[path] = psr; + return psr; + }else{ + return i->second; + } + } + + SyncTime& Time() + { + return time; + } + + static std::shared_ptr ChooseFromParams(const Params& params); + +private: + std::map> readers; + SyncTime time; +}; + +} diff --git a/include/pangolin/log/sync_time.h b/include/pangolin/log/sync_time.h new file mode 100644 index 0000000..bb0e7e9 --- /dev/null +++ b/include/pangolin/log/sync_time.h @@ -0,0 +1,230 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace pangolin +{ + +// Lightweight timestamp class to allow synchronized playback from the same (or a different) stream. +// All playback functions called with the same SyncTime will be time-synchronized, and will remain synchronized on seek() if the SyncTime is passed in when seeking. +// Playback with multiple SyncTimes (on the same or different streams) should also be synced, even in different processes or systems (underlying clock sync is not required). +// However, playback with multiple SyncTimes will break on seek(). +class PANGOLIN_EXPORT SyncTime +{ +public: + using Clock = std::chrono::system_clock; + using Duration = Clock::duration; + using TimePoint = Clock::time_point; + + struct SeekInterruption: std::runtime_error + { + SeekInterruption() : std::runtime_error("Time queue invalidated by seek"){} + }; + + SyncTime(Duration virtual_clock_offset = std::chrono::milliseconds(0)) + : seeking(false) + { + SetOffset(virtual_clock_offset); + } + + // No copy constructor + SyncTime(const SyncTime&) = delete; + + void SetOffset(Duration virtual_clock_offset) + { + virtual_offset = virtual_clock_offset; + } + + void SetClock(TimePoint virtual_now) + { + virtual_offset = virtual_now - Clock::now(); + } + + TimePoint TimeNow() const + { + return Clock::now() + virtual_offset; + } + + TimePoint ToVirtual(TimePoint real) const + { + return real + virtual_offset; + } + + TimePoint ToReal(TimePoint virt) const + { + return virt - virtual_offset; + } + + void WaitUntil(TimePoint virtual_time) const + { + std::this_thread::sleep_until( ToReal(virtual_time) ); + } + + int64_t QueueEvent(int64_t new_event_time_us) + { + return WaitDequeueAndQueueEvent(0, new_event_time_us); + } + + void DequeueEvent(int64_t event_time_us) + { + std::unique_lock l(time_mutex); + + auto i = std::find(time_queue_us.begin(), time_queue_us.end(), event_time_us); + PANGO_ENSURE(i != time_queue_us.end()); + time_queue_us.erase(i); + } + + int64_t WaitDequeueAndQueueEvent(int64_t event_time_us, int64_t new_event_time_us =0 ) + { + std::unique_lock l(time_mutex); + + if(event_time_us) { + PANGO_ENSURE(time_queue_us.size()); + + // Wait until we're top the priority-queue + queue_changed.wait(l, [&](){ + if(seeking) { + // Time queue will be invalidated on seek. + // Unblock without action + throw SeekInterruption(); + } + return time_queue_us.back() == event_time_us; + }); + + // Dequeue + time_queue_us.pop_back(); + } + + if(new_event_time_us) { + // Add the new event whilst we still hold the lock, so that our + // event can't be missed + insert_sorted(time_queue_us, new_event_time_us, std::greater()); + + if(time_queue_us.back() == new_event_time_us) { + // Return to avoid yielding when we're next. + return new_event_time_us; + } + } + + // Only yield if another device is next + queue_changed.notify_all(); + return new_event_time_us; + } + + void NotifyAll() + { + queue_changed.notify_all(); + } + + std::mutex& TimeMutex() + { + return time_mutex; + } + + void Stop() + { + seeking = true; + OnTimeStop(); + queue_changed.notify_all(); + } + + void Start() + { + OnTimeStart(); + seeking=false; + } + + void Seek(TimePoint t) + { + Stop(); + OnSeek(t); + Start(); + } + + Signal<> OnTimeStart; + + Signal<> OnTimeStop; + + Signal OnSeek; + +private: + template< typename T, typename Pred > + static typename std::vector::iterator + insert_sorted( std::vector & vec, T const& item, Pred pred ) + { + return vec.insert ( + std::upper_bound( vec.begin(), vec.end(), item, pred ), item + ); + } + + std::vector time_queue_us; + Duration virtual_offset; + std::mutex time_mutex; + std::condition_variable queue_changed; + bool seeking; +}; + +struct SyncTimeEventPromise +{ + SyncTimeEventPromise(SyncTime& sync, int64_t time_us = 0) + : sync(sync), time_us(time_us) + { + sync.QueueEvent(time_us); + } + + ~SyncTimeEventPromise() + { + Cancel(); + } + + void Cancel() + { + if(time_us) { + sync.DequeueEvent(time_us); + time_us = 0; + } + } + + void WaitAndRenew(int64_t new_time_us) + { + time_us = sync.WaitDequeueAndQueueEvent(time_us, new_time_us); + } + +private: + SyncTime& sync; + int64_t time_us; +}; + +} diff --git a/include/pangolin/pangolin.h b/include/pangolin/pangolin.h new file mode 100644 index 0000000..e16e8a7 --- /dev/null +++ b/include/pangolin/pangolin.h @@ -0,0 +1,67 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#ifdef BUILD_PANGOLIN_GUI + #include + #include + #include + #include + #include + #include + #include + #ifdef HAVE_GLUT + #include + #endif // HAVE_GLUT + #ifdef _ANDROID_ + #include + #endif + #if !defined(HAVE_GLES) || defined(HAVE_GLES_2) + #include + #endif +#endif // BUILD_PANGOLIN_GUI + +#ifdef BUILD_PANGOLIN_VARS + #include + #ifdef BUILD_PANGOLIN_GUI + #include + #endif // BUILD_PANGOLIN_GUI +#endif // BUILD_PANGOLIN_VARS + +#ifdef BUILD_PANGOLIN_VIDEO + #include + #include + #include +#endif // BUILD_PANGOLIN_VIDEO + +#include + +// Let other libraries headers know about Pangolin +#define HAVE_PANGOLIN diff --git a/include/pangolin/platform.h b/include/pangolin/platform.h new file mode 100644 index 0000000..b5a223b --- /dev/null +++ b/include/pangolin/platform.h @@ -0,0 +1,81 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +// Include portable printf-style format macros +#define __STDC_FORMAT_MACROS + +#ifdef _GCC_ +# define PANGOLIN_DEPRECATED __attribute__((deprecated)) +#elif defined _MSVC_ +# define PANGOLIN_DEPRECATED __declspec(deprecated) +#else +# define PANGOLIN_DEPRECATED +#endif + +#ifdef _MSVC_ +# define __thread __declspec(thread) +# include +#else +# define PANGOLIN_EXPORT +#endif //_MSVC_ + +#define PANGOLIN_UNUSED(x) (void)(x) + +#ifdef _APPLE_IOS_ +// Not supported on this platform. +#define __thread +#endif // _APPLE_IOS_ + +// HOST / DEVICE Annotations +#ifdef __CUDACC__ +# include +# define PANGO_HOST_DEVICE __host__ __device__ +#else +# define PANGO_HOST_DEVICE +#endif + +// Non-standard check that header exists (Clang, GCC 5.X) +// Useful for +#if defined(__has_include) +# define PANGO_HEADER_EXISTS(x) __has_include(x) +#else +# define PANGO_HEADER_EXISTS(x) 0 +#endif + +// Workaround for Apple-Clangs lack of thread_local support +#if defined(_CLANG_) && defined(_OSX_) +# if !__has_feature(cxx_thread_local) +# define PANGO_NO_THREADLOCAL +# endif +#endif + +#include +#include diff --git a/include/pangolin/plot/datalog.h b/include/pangolin/plot/datalog.h new file mode 100644 index 0000000..d0120b0 --- /dev/null +++ b/include/pangolin/plot/datalog.h @@ -0,0 +1,243 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include // std::min, std::max +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) //prevent including Eigen in cuda files +#define USE_EIGEN +#endif + +#ifdef USE_EIGEN +#include +#endif + +namespace pangolin +{ + +/// Simple statistics recorded for a logged input dimension. +struct DimensionStats +{ + DimensionStats() + { + Reset(); + } + + void Reset() + { + isMonotonic = true; + sum = 0.0f; + sum_sq = 0.0f; + min = std::numeric_limits::max(); + max = std::numeric_limits::lowest(); + } + + void Add(const float v) + { + isMonotonic = isMonotonic && (v >= max); + sum += v; + sum_sq += v*v; + min = std::min(min, v); + max = std::max(max, v); + } + + bool isMonotonic; + float sum; + float sum_sq; + float min; + float max; +}; + +class DataLogBlock +{ +public: + /// @param dim: dimension of sample + /// @param max_samples: maximum number of samples this block can hold + /// @param start_id: index of first sample (from entire dataset) in this buffer + DataLogBlock(size_t dim, size_t max_samples, size_t start_id) + : dim(dim), max_samples(max_samples), samples(0), + start_id(start_id) + { + sample_buffer = std::unique_ptr(new float[dim*max_samples]); +// stats = std::unique_ptr(new DimensionStats[dim]); + } + + ~DataLogBlock() + { + } + + size_t Samples() const + { + return samples; + } + + size_t MaxSamples() const + { + return max_samples; + } + + /// Return how many more samples can fit in this block + size_t SampleSpaceLeft() const + { + return MaxSamples()- Samples(); + } + + bool IsFull() const + { + return Samples() >= MaxSamples(); + } + + /// Add data to block + void AddSamples(size_t num_samples, size_t dimensions, const float* data_dim_major ); + + /// Delete all samples + void ClearLinked() + { + samples = 0; + nextBlock.reset(); + } + + DataLogBlock* NextBlock() const + { + return nextBlock.get(); + } + + size_t StartId() const + { + return start_id; + } + + float* DimData(size_t d) const + { + return sample_buffer.get() + d; + } + + size_t Dimensions() const + { + return dim; + } + + const float* Sample(size_t n) const + { + const int id = (int)n - (int)start_id; + + if( 0 <= id && id < (int)samples ) { + return sample_buffer.get() + dim*id; + }else{ + if(nextBlock) { + return nextBlock->Sample(n); + }else{ + throw std::out_of_range("Index out of range."); + } + } + } + +protected: + size_t dim; + size_t max_samples; + size_t samples; + size_t start_id; + std::unique_ptr sample_buffer; +// std::unique_ptr stats; + std::unique_ptr nextBlock; +}; + +/// A DataLog can efficiently record floating point sample data of any size. +/// Memory is allocated in blocks is transparent to the user. +class PANGOLIN_EXPORT DataLog +{ +public: + /// @param block_samples_alloc number of samples each memory block can hold. + DataLog(unsigned int block_samples_alloc = 10000 ); + + ~DataLog(); + + /// Provide textual labels corresponding to each dimension logged. + /// This information may be used by graphical interfaces to DataLog. + void SetLabels(const std::vector & labels); + const std::vector& Labels() const; + + void Log(size_t dimension, const float * vals, unsigned int samples = 1); + void Log(float v); + void Log(float v1, float v2); + void Log(float v1, float v2, float v3); + void Log(float v1, float v2, float v3, float v4); + void Log(float v1, float v2, float v3, float v4, float v5); + void Log(float v1, float v2, float v3, float v4, float v5, float v6); + void Log(float v1, float v2, float v3, float v4, float v5, float v6, float v7); + void Log(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8); + void Log(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9); + void Log(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9, float v10); + void Log(const std::vector & vals); + +#ifdef USE_EIGEN + template + void Log(const Eigen::MatrixBase& M) + { + Log( M.rows() * M.cols(), M.template cast().eval().data() ); + } +#endif + + void Clear(); + void Save(std::string filename); + + // Return first block of stored data + const DataLogBlock* FirstBlock() const; + + // Return last block of stored data + const DataLogBlock* LastBlock() const; + + // Return number of samples stored in this DataLog + size_t Samples() const; + + // Return pointer to stored sample n + const float* Sample(int n) const; + + // Return stats computed for each dimension if enabled. + const DimensionStats& Stats(size_t dim) const; + + std::mutex access_mutex; + +protected: + unsigned int block_samples_alloc; + std::vector labels; + std::unique_ptr block0; + DataLogBlock* blockn; + std::vector stats; + bool record_stats; +}; + +} diff --git a/include/pangolin/plot/plotter.h b/include/pangolin/plot/plotter.h new file mode 100644 index 0000000..b33219e --- /dev/null +++ b/include/pangolin/plot/plotter.h @@ -0,0 +1,279 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PLOTTER_H +#define PLOTTER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pangolin +{ + +enum DrawingMode +{ + DrawingModePoints = GL_POINTS, + DrawingModeDashed = GL_LINES, + DrawingModeLine = GL_LINE_STRIP, + DrawingModeNone, +}; + +struct Marker +{ + enum Direction + { + Horizontal, + Vertical + }; + + enum Equality + { + LessThan = -1, + Equal = 0, + GreaterThan = 1 + }; + + Marker(Direction d, float value, Equality leg = Equal, Colour c = Colour() ) + : colour(c) + { + if(d == Horizontal) { + range.x = Rangef::Open(); + range.y = Rangef::Containing(value); + if(leg == LessThan) { + range.y.Insert(std::numeric_limits::lowest() ); + }else if(leg == GreaterThan) { + range.y.Insert(std::numeric_limits::max() ); + } + }else if(d == Vertical) { + range.x = Rangef::Containing(value); + range.y = Rangef::Open(); + if(leg == LessThan) { + range.x.Insert(std::numeric_limits::lowest() ); + }else if(leg == GreaterThan) { + range.x.Insert(std::numeric_limits::max() ); + } + } + } + + Marker(const XYRangef& range, const Colour& c = Colour() ) + : range(range), colour(c) + { + } + + XYRangef range; + Colour colour; +}; + +class PANGOLIN_EXPORT Plotter : public View, Handler +{ +public: + Plotter( + DataLog* default_log, + float left=0, float right=600, float bottom=-1, float top=1, + float tickx=30, float ticky=0.5, + Plotter* linked_plotter_x = 0, + Plotter* linked_plotter_y = 0 + ); + + virtual ~Plotter(); + + void Render(); + + XYRangef& GetSelection(); + + XYRangef& GetDefaultView(); + void SetDefaultView(const XYRangef& range); + + XYRangef& GetView(); + void SetView(const XYRangef& range); + void SetViewSmooth(const XYRangef& range); + + void ScrollView(float x, float y); + void ScrollViewSmooth(float x, float y); + + void ScaleView(float x, float y, float cx, float cy); + void ScaleViewSmooth(float x, float y, float cx, float cy); + + void ResetView(); + + void SetTicks(float tickx, float ticky); + + void Track(const std::string& x="$i", const std::string& y = ""); + void ToggleTracking(); + + void Trigger(const std::string& x="$0", int edge = -1, float value = 0.0f); + void ToggleTrigger(); + + void SetBackgroundColour(const Colour& col); + void SetAxisColour(const Colour& col); + void SetTickColour(const Colour& col); + + void ScreenToPlot(int xpix, int ypix, float &xplot, float &yplot); + void Keyboard(View&, unsigned char key, int x, int y, bool pressed); + void Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state); + void MouseMotion(View&, int x, int y, int mouse_state); + void PassiveMouseMotion(View&, int x, int y, int button_state); + void Special(View&, InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4, int button_state); + + /// Remove all current series plots + void ClearSeries(); + + /// Add series X,Y plot from glsl compatible expressions x_expr, y_expr + /// $i refers to integral index of datum in log. + /// $0, $1, $2, ... refers to nth series in log. + /// e.g. x_expr = "$i", y_expr = "$0" // index - data[0] plot + /// e.g. x_expr = "$0", y_expr = "$1" // data[0], data[1] X-Y plot + /// e.g. x_exptr ="$i", y_expr = "sqrt($1)} // index - sqrt(data[0]) plot + void AddSeries(const std::string& x_expr, const std::string& y_expr, + DrawingMode drawing_mode = DrawingModeLine, Colour colour = Colour::Unspecified(), + const std::string &title = "$y", DataLog* log = nullptr + ); + + std::string PlotTitleFromExpr(const std::string& expr) const; + + /// Remove all current markers + void ClearMarkers(); + + /// Add horizontal or vertical inequality marker; equal-to, less-than, or greater than. + /// This is useful for annotating a critical point or valid region. + Marker& AddMarker( + Marker::Direction d, float value, + Marker::Equality leg = Marker::Equal, Colour c = Colour() + ); + + Marker& AddMarker( const Marker& marker ); + + void ClearImplicitPlots(); + void AddImplicitPlot(); + +protected: + struct PANGOLIN_EXPORT Tick + { + float val; + float factor; + std::string symbol; + }; + + struct PANGOLIN_EXPORT PlotAttrib + { + PlotAttrib(std::string name, int plot_id, int location = -1) + : name(name), plot_id(plot_id), location(location) { } + + std::string name; + int plot_id; + int location; + }; + + struct PANGOLIN_EXPORT PlotSeries + { + PlotSeries(); + void CreatePlot(const std::string& x, const std::string& y, Colour c, std::string title); + + GlSlProgram prog; + GlText title; + bool contains_id; + std::vector attribs; + DataLog* log; + GLenum drawing_mode; + Colour colour; + bool used; + }; + + struct PANGOLIN_EXPORT PlotImplicit + { + // Assign to gl_FragColor + void CreatePlot(const std::string& code); + + // Expression uses x,y and assignes colours [0,1] to r,g,b,a + void CreateColouredPlot(const std::string& code); + + // Expression uses x,y and evaluates to true/false; + void CreateInequality(const std::string& ie, Colour c); + + // Expression uses x,y and evaluates to a number + void CreateDistancePlot(const std::string& dist); + + GlSlProgram prog; + }; + + void FixSelection(); + void UpdateView(); + Tick FindTickFactor(float tick); + + DataLog* default_log; + + ColourWheel colour_wheel; + Colour colour_bg; + Colour colour_tk; + Colour colour_ax; + + GlSlProgram prog_lines; + GlSlProgram prog_text; + + std::vector plotseries; + std::vector plotmarkers; + std::vector plotimplicits; + + Tick tick[2]; + XYRangef rview_default; + XYRangef rview; + XYRangef target; + XYRangef selection; + + void ComputeTrackValue( float track_val[2] ); + XYRangef ComputeAutoSelection(); + + bool track; + std::string track_x; + std::string track_y; + float last_track_val[2]; + + // -1: falling, -0:disable, 1: rising + int trigger_edge; + float trigger_value; + std::string trigger; + + float hover[2]; + int last_mouse_pos[2]; + + Plotter* linked_plotter_x; + Plotter* linked_plotter_y; +}; + +} // namespace pangolin + +#endif // PLOTTER_H diff --git a/include/pangolin/plot/range.h b/include/pangolin/plot/range.h new file mode 100644 index 0000000..ec2906e --- /dev/null +++ b/include/pangolin/plot/range.h @@ -0,0 +1,372 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include + +//prevent including Eigen in cuda files +#if defined(HAVE_EIGEN) && !defined(__CUDACC__) +# define USE_EIGEN +#endif + +#ifdef USE_EIGEN +# include +# include +#endif // USE_EIGEN + +namespace pangolin +{ + + +template +struct Range +{ + static Range Open() + { + return Range(std::numeric_limits::lowest(), std::numeric_limits::max()); + } + + static Range Empty() + { + return Range(std::numeric_limits::max(), std::numeric_limits::lowest()); + } + + static Range Containing(T val) + { + return Range(val, val); + } + + Range() + : min(+std::numeric_limits::max()), + max(-std::numeric_limits::max()) + { + } + + Range(T rmin, T rmax) + : min(rmin), max(rmax) + { + } + + Range operator+(T v) + { + return Range(min+v, max+v); + } + + Range operator-(T v) + { + return Range(min-v, max-v); + } + + Range& operator+=(T v) + { + min += v; + max += v; + return *this; + } + + Range& operator-=(T v) + { + min -= v; + max -= v; + return *this; + } + + Range& operator*=(T v) + { + min *= v; + max *= v; + return *this; + } + + Range& operator/=(T v) + { + min /= v; + max /= v; + return *this; + } + + Range& operator+=(const Range& o) + { + min += o.min; + max += o.max; + return *this; + } + + Range& operator-=(const Range& o) + { + min -= o.min; + max -= o.max; + return *this; + } + + Range operator+(const Range& o) const + { + return Range(min + o.min, max + o.max); + } + + Range operator-(const Range& o) const + { + return Range(min - o.min, max - o.max); + } + + Range operator*(float s) const + { + return Range(T(s*min), T(s*max)); + } + + T Size() const + { + return max - min; + } + + T AbsSize() const + { + return std::abs(Size()); + } + + T Mid() const + { + return (min + max) / (T)2.0f; + } + + void Scale(float s, float center = 0.0f) + { + min = T(s*(min-center) + center); + max = T(s*(max-center) + center); + } + + void Insert(T v) + { + min = std::min(min,v); + max = std::max(max,v); + } + + void Insert(const Range& r) + { + Insert(r.min); + Insert(r.max); + } + + void Clamp(T vmin, T vmax) + { + min = std::min(std::max(vmin, min), vmax); + max = std::min(std::max(vmin, max), vmax); + } + + void Clamp(const Range& o) + { + Clamp(o.min, o.max); + } + + void Clear() + { + min = std::numeric_limits::max(); + max = std::numeric_limits::lowest(); + } + + bool Contains(T v) const + { + return min <= v && v <= max; + } + + bool ContainsWeak(T v) const + { + return (min <= v && v <= max) + || (max <= v && v <= min); + } + + template + Range Cast() const + { + return Range(To(min), To(max)); + } + + T min; + T max; +}; + +template +struct XYRange +{ + static XYRange Open() + { + return XYRange( + Range(std::numeric_limits::lowest(), std::numeric_limits::max()), + Range(std::numeric_limits::lowest(), std::numeric_limits::max()) + ); + } + + static XYRange Empty() + { + return XYRange( + Range(std::numeric_limits::max(), std::numeric_limits::lowest()), + Range(std::numeric_limits::max(), std::numeric_limits::lowest()) + ); + } + + static XYRange Containing(T x, T y) + { + return XYRange( + Range(x, x), + Range(y, y) + ); + } + + XYRange() + { + } + + XYRange(const Range& xrange, const Range& yrange) + : x(xrange), y(yrange) + { + } + + XYRange(T xmin, T xmax, T ymin, T ymax) + : x(xmin,xmax), y(ymin,ymax) + { + } + + XYRange operator-(const XYRange& o) const + { + return XYRange(x - o.x, y - o.y); + } + + XYRange operator*(float s) const + { + return XYRange(x*s, y*s); + } + + XYRange& operator+=(const XYRange& o) + { + x += o.x; + y += o.y; + return *this; + } + + void Scale(float sx, float sy, float centerx, float centery) + { + x.Scale(sx, centerx); + y.Scale(sy, centery); + } + + void Clear() + { + x.Clear(); + y.Clear(); + } + + void Clamp(T xmin, T xmax, T ymin, T ymax) + { + x.Clamp(xmin,xmax); + y.Clamp(ymin,ymax); + } + + void Clamp(const XYRange& o) + { + x.Clamp(o.x); + y.Clamp(o.y); + } + + void Insert(T xval, T yval) + { + x.Insert(xval); + y.Insert(yval); + } + + void Insert(XYRange r) + { + x.Insert(r.x); + y.Insert(r.y); + } + + float Area() const + { + return x.Size() * y.Size(); + } + + bool Contains(float px, float py) const + { + return x.Contains(px) && y.Contains(py); + } + + bool ContainsWeak(float px, float py) const + { + return x.ContainsWeak(px) && y.ContainsWeak(py); + } + + template + XYRange Cast() const + { + return XYRange( + x.template Cast(), + y.template Cast() + ); + } + +#ifdef USE_EIGEN + operator Eigen::AlignedBox() const { + return Eigen::AlignedBox( + Eigen::Matrix(x.min, y.min), + Eigen::Matrix(x.max, y.max) + ); + } + + Eigen::Matrix Center() const { + return Eigen::Matrix(x.Mid(), y.Mid()); + } +#endif + + Range x; + Range y; +}; + +typedef Range Rangei; +typedef Range Rangef; +typedef Range Ranged; + +typedef XYRange XYRangei; +typedef XYRange XYRangef; +typedef XYRange XYRanged; + +template inline +Rangei Round(const Range& r) +{ + return Rangei( int(r.min+0.5), int(r.max+0.5) ); +} + +template inline +XYRangei Round(const XYRange& r) +{ + return XYRangei( Round(r.x), Round(r.y) ); +} + +} diff --git a/include/pangolin/python/PyInterpreter.h b/include/pangolin/python/PyInterpreter.h new file mode 100644 index 0000000..ac08fb9 --- /dev/null +++ b/include/pangolin/python/PyInterpreter.h @@ -0,0 +1,70 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace pangolin +{ + +class PyInterpreter : public ConsoleInterpreter +{ +public: + PyInterpreter(); + + ~PyInterpreter() override; + + void PushCommand(const std::string &cmd) override; + + bool PullLine(ConsoleLine& line) override; + + std::vector Complete( + const std::string& cmd, int max_options + ) override; + + static void AttachPrefix(void* data, const std::string& name, VarValueGeneric& var, bool brand_new ); + +private: + PyObject* pycompleter; + PyObject* pycomplete; + + std::string ToString(PyObject* py); + void CheckPrintClearError(); + PyUniqueObj EvalExec(const std::string& cmd); + + std::queue line_queue; + std::set base_prefixes; +}; + +} diff --git a/include/pangolin/python/PyModulePangolin.h b/include/pangolin/python/PyModulePangolin.h new file mode 100644 index 0000000..8e06c59 --- /dev/null +++ b/include/pangolin/python/PyModulePangolin.h @@ -0,0 +1,40 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include + +namespace pangolin +{ + +PANGOLIN_EXPORT +PyMODINIT_FUNC InitPangoModule(); + +} diff --git a/include/pangolin/python/PyPangoIO.h b/include/pangolin/python/PyPangoIO.h new file mode 100644 index 0000000..f910c77 --- /dev/null +++ b/include/pangolin/python/PyPangoIO.h @@ -0,0 +1,172 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include + +namespace pangolin +{ + +struct PyPangoIO { + PyObject_HEAD + + static PyTypeObject Py_type; + static PyMethodDef Py_methods[]; + + PyPangoIO(PyTypeObject *type, std::queue& line_queue, ConsoleLineType line_type) + : line_queue(line_queue), line_type(line_type) + { +#if PY_MAJOR_VERSION >= 3 + ob_base.ob_refcnt = 1; + ob_base.ob_type = type; +#else + ob_refcnt = 1; + ob_type = type; +#endif + } + + static void Py_dealloc(PyPangoIO* self) + { + delete self; + } + + static PyObject * Py_new(PyTypeObject */*type*/, PyObject */*args*/, PyObject */*kwds*/) + { + // Failure. Can only new in c++ + return 0; + } + + static int Py_init(PyPangoIO* /*self*/, PyObject* /*args*/, PyObject* /*kwds*/) + { + return 0; + } + + static PyObject* Py_getattr(PyPangoIO *self, char* name) + { +#if PY_MAJOR_VERSION >= 3 + PyObject* pystr = PyUnicode_FromString(name); +#else + PyObject* pystr = PyString_FromString(name); +#endif + return PyObject_GenericGetAttr((PyObject*)self, pystr ); + } + + static int Py_setattr(PyPangoIO *self, char* name, PyObject* val) + { +#if PY_MAJOR_VERSION >= 3 + PyObject* pystr = PyUnicode_FromString(name); +#else + PyObject* pystr = PyString_FromString(name); +#endif + return PyObject_GenericSetAttr((PyObject*)self, pystr, val); + } + + static PyObject* Py_write(PyPangoIO* self, PyObject *args) + { + const char *text = 0; + if (PyArg_ParseTuple(args, "s", &text)) { + self->buffer += std::string(text); + size_t nl = self->buffer.find_first_of('\n'); + while(nl != std::string::npos) { + const std::string line = self->buffer.substr(0,nl); + self->line_queue.push(ConsoleLine(line,self->line_type)); + self->buffer = self->buffer.substr(nl+1); + nl = self->buffer.find_first_of('\n'); + } + } + Py_RETURN_NONE; + } + + std::string buffer; + std::queue& line_queue; + ConsoleLineType line_type; +}; + +PyMethodDef PyPangoIO::Py_methods[] = { + {"write", (PyCFunction)PyPangoIO::Py_write, METH_VARARGS, "Write to console" }, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject PyPangoIO::Py_type = { + PyVarObject_HEAD_INIT(NULL,0) + "pangolin.PangoIO", /* tp_name*/ + sizeof(PyPangoIO), /* tp_basicsize*/ + 0, /* tp_itemsize*/ + (destructor)PyPangoIO::Py_dealloc, /* tp_dealloc*/ + 0, /* tp_print*/ + (getattrfunc)PyPangoIO::Py_getattr, /* tp_getattr*/ + (setattrfunc)PyPangoIO::Py_setattr, /* tp_setattr*/ + 0, /* tp_compare*/ + 0, /* tp_repr*/ + 0, /* tp_as_number*/ + 0, /* tp_as_sequence*/ + 0, /* tp_as_mapping*/ + 0, /* tp_hash */ + 0, /* tp_call*/ + 0, /* tp_str*/ + 0, /* tp_getattro*/ + 0, /* tp_setattro*/ + 0, /* tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/ + "PyPangoIO object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyPangoIO::Py_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyPangoIO::Py_init, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)PyPangoIO::Py_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0 /* tp_version_tag */ +}; + +} diff --git a/include/pangolin/python/PyUniqueObj.h b/include/pangolin/python/PyUniqueObj.h new file mode 100644 index 0000000..cb3d3c9 --- /dev/null +++ b/include/pangolin/python/PyUniqueObj.h @@ -0,0 +1,111 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +/// Class represents a reference counted PythonObject. +/// PythonObject is appropriately Py_INCREF'd and Py_DECREF'd +class PyUniqueObj +{ +public: + inline + PyUniqueObj() + : obj(0) + { + } + + /// Assumption: PythonObject has already been appropriately INCREF'd. + inline + PyUniqueObj(PyObject* obj) + : obj(obj) + { + } + + inline + PyUniqueObj(const PyUniqueObj& other) + :obj(other.obj) + { + if(obj) Py_INCREF(obj); + } + + inline + ~PyUniqueObj() + { + if(obj) Py_DECREF(obj); + } + + inline + PyUniqueObj(PyUniqueObj&& other) + : obj(other.obj) + { + other.obj = 0; + } + + inline + void operator=(PyUniqueObj&& other) + { + Release(); + obj = other.obj; + other.obj = 0; + } + + inline + void operator=(PyObject* obj) + { + Release(); + this->obj = obj; + } + + inline + void Release() { + if(obj) { + Py_DECREF(obj); + obj = 0; + } + } + + inline + PyObject* operator*() { + return obj; + } + + inline + operator PyObject*() { + return obj; + } + +private: + PyObject* obj; +}; + +} diff --git a/include/pangolin/python/PyVar.h b/include/pangolin/python/PyVar.h new file mode 100644 index 0000000..79fdb9f --- /dev/null +++ b/include/pangolin/python/PyVar.h @@ -0,0 +1,267 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace pangolin +{ + +PyObject* GetPangoVarAsPython(const std::string& name) +{ + VarState::VarStoreContainer::iterator i = VarState::I().vars.find(name); + if(i != VarState::I().vars.end()) { + VarValueGeneric* var = i->second; + + try{ + if( !strcmp(var->TypeId(), typeid(bool).name() ) ) { + const bool val = Var(*var).Get(); + return PyBool_FromLong( val ); + }else if( !strcmp(var->TypeId(), typeid(short).name() ) || + !strcmp(var->TypeId(), typeid(int).name() ) || + !strcmp(var->TypeId(), typeid(long).name() ) ) { + const long val = Var(*var).Get(); + return PyLong_FromLong( val ); + }else if( !strcmp(var->TypeId(), typeid(double).name() ) || + !strcmp(var->TypeId(), typeid(float).name() ) ) { + const double val = Var(*var).Get(); + return PyFloat_FromDouble(val); + }else{ + const std::string val = var->str->Get(); +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(val.c_str()); +#else + return PyString_FromString(val.c_str()); +#endif + } + }catch(std::exception) { + } + } + + Py_RETURN_NONE; +} + +void SetPangoVarFromPython(const std::string& name, PyObject* val) +{ + try{ + if (PyFloat_Check(val)) { + pangolin::Var pango_var(name); + pango_var = PyFloat_AsDouble(val); + pango_var.Meta().gui_changed = true; + }else if (PyLong_Check(val)) { + pangolin::Var pango_var(name); + pango_var = PyLong_AsLong(val); + pango_var.Meta().gui_changed = true; + }else if (PyBool_Check(val)) { + pangolin::Var pango_var(name); + pango_var = (val == Py_True) ? true : false; + pango_var.Meta().gui_changed = true; + } +#if PY_MAJOR_VERSION >= 3 + else if (PyUnicode_Check(val)) { + pangolin::Var pango_var(name); + pango_var = PyUnicode_AsUTF8(val); + pango_var.Meta().gui_changed = true; + } +#else + else if (PyString_Check(val)) { + pangolin::Var pango_var(name); + pango_var = PyString_AsString(val); + pango_var.Meta().gui_changed = true; + } else if (PyInt_Check(val)) { + pangolin::Var pango_var(name); + pango_var = PyInt_AsLong(val); + pango_var.Meta().gui_changed = true; + } +#endif + else { + PyUniqueObj pystr = PyObject_Repr(val); +#if PY_MAJOR_VERSION >= 3 + const std::string str = PyUnicode_AsUTF8(pystr); +#else + const std::string str = PyString_AsString(pystr); +#endif + pangolin::Var pango_var(name); + pango_var = str; + pango_var.Meta().gui_changed = true; + } + FlagVarChanged(); + }catch(std::exception e) { + pango_print_error("%s\n", e.what()); + } +} + +struct PyVar { + static PyTypeObject Py_type; + PyObject_HEAD + + PyVar(PyTypeObject *type) + { +#if PY_MAJOR_VERSION >= 3 + ob_base.ob_refcnt = 1; + ob_base.ob_type = type; +#else + ob_refcnt = 1; + ob_type = type; +#endif + } + + static void Py_dealloc(PyVar* self) + { + delete self; + } + + static PyObject * Py_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) + { + PyVar* self = new PyVar(type); + return (PyObject *)self; + } + + static int Py_init(PyVar *self, PyObject *args, PyObject * /*kwds*/) + { + char* cNamespace = 0; + if (!PyArg_ParseTuple(args, "s", &cNamespace)) + return -1; + + self->ns = std::string(cNamespace); + + return 0; + } + + static PyObject* Py_getattr(PyVar *self, char* name) + { + const std::string prefix = self->ns + "."; + const std::string full_name = self->ns.empty() ? name : prefix + std::string(name); + + if( !strcmp(name, "__call__") || + !strcmp(name, "__dict__") || + !strcmp(name, "__methods__") || + !strcmp(name, "__class__") ) + { + // Default behaviour +#if PY_MAJOR_VERSION >= 3 + return PyObject_GenericGetAttr((PyObject*)self, PyUnicode_FromString(name)); +#else + return PyObject_GenericGetAttr((PyObject*)self, PyString_FromString(name)); +#endif + } else if( !strcmp(name, "__members__") ) { + const int nss = prefix.size(); + PyObject* l = PyList_New(0); + for(const std::string& s : VarState::I().var_adds) { + if(!s.compare(0, nss, prefix)) { + size_t dot = s.find_first_of('.', nss); + std::string val = (dot != std::string::npos) ? s.substr(nss, dot - nss) : s.substr(nss); +#if PY_MAJOR_VERSION >= 3 + PyList_Append(l, PyUnicode_FromString(val.c_str())); +#else + PyList_Append(l, PyString_FromString(val.c_str())); +#endif + } + } + + return l; + }else if( pangolin::VarState::I().Exists(full_name) ) { + return GetPangoVarAsPython(full_name); + }else{ + PyVar* obj = (PyVar*)PyVar::Py_new(&PyVar::Py_type,NULL,NULL); + if(obj) { + obj->ns = full_name; + return PyObject_Init((PyObject *)obj,&PyVar::Py_type); + } + return (PyObject *)obj; + } + + Py_RETURN_NONE; + } + + static int Py_setattr(PyVar *self, char* name, PyObject* val) + { + const std::string full_name = self->ns.empty() ? name : self->ns + "." + std::string(name); + SetPangoVarFromPython(full_name, val); + return 0; + } + + std::string ns; +}; + + PyTypeObject PyVar::Py_type = { + PyVarObject_HEAD_INIT(NULL,0) + "pangolin.Var", /* tp_name*/ + sizeof(PyVar), /* tp_basicsize*/ + 0, /* tp_itemsize*/ + (destructor)PyVar::Py_dealloc, /* tp_dealloc*/ + 0, /* tp_print*/ + (getattrfunc)PyVar::Py_getattr, /* tp_getattr*/ + (setattrfunc)PyVar::Py_setattr, /* tp_setattr*/ + 0, /* tp_compare*/ + 0, /* tp_repr*/ + 0, /* tp_as_number*/ + 0, /* tp_as_sequence*/ + 0, /* tp_as_mapping*/ + 0, /* tp_hash */ + 0, /* tp_call*/ + 0, /* tp_str*/ + 0, /* tp_getattro*/ + 0, /* tp_setattro*/ + 0, /* tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/ + "PyVar object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyVar::Py_init, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)PyVar::Py_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0 /* tp_version_tag */ +}; + +} diff --git a/include/pangolin/scene/axis.h b/include/pangolin/scene/axis.h new file mode 100644 index 0000000..15a750c --- /dev/null +++ b/include/pangolin/scene/axis.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#ifdef HAVE_EIGEN +# include +#endif + +namespace pangolin { + +struct Axis : public Renderable, public Interactive +{ + Axis() + : axis_length(1.0), + label_x(InteractiveIndex::I().Store(this)), + label_y(InteractiveIndex::I().Store(this)), + label_z(InteractiveIndex::I().Store(this)) + { + } + + void Render(const RenderParams&) override { + glColor4f(1,0,0,1); + glPushName(label_x.Id()); + glDrawLine(0,0,0, axis_length,0,0); + glPopName(); + + glColor4f(0,1,0,1); + glPushName(label_y.Id()); + glDrawLine(0,0,0, 0,axis_length,0); + glPopName(); + + glColor4f(0,0,1,1); + glPushName(label_z.Id()); + glDrawLine(0,0,0, 0,0,axis_length); + glPopName(); + } + + bool Mouse( + int button, + const GLprecision /*win*/[3], const GLprecision /*obj*/[3], const GLprecision /*normal*/[3], + bool /*pressed*/, int button_state, int pickId + ) override + { +#ifdef HAVE_EIGEN + if((button == MouseWheelUp || button == MouseWheelDown) ) { + float scale = (button == MouseWheelUp) ? 0.01f : -0.01f; + if(button_state & KeyModifierShift) scale /= 10; + + Eigen::Vector3d rot = Eigen::Vector3d::Zero(); + Eigen::Vector3d xyz = Eigen::Vector3d::Zero(); + + + if(button_state & KeyModifierCtrl) { + // rotate + if(pickId == label_x.Id()) { + rot << 1,0,0; + }else if(pickId == label_y.Id()) { + rot << 0,1,0; + }else if(pickId == label_z.Id()) { + rot << 0,0,1; + }else{ + return false; + } + }else if(button_state & KeyModifierShift){ + // translate + if(pickId == label_x.Id()) { + xyz << 1,0,0; + }else if(pickId == label_y.Id()) { + xyz << 0,1,0; + }else if(pickId == label_z.Id()) { + xyz << 0,0,1; + }else{ + return false; + } + }else{ + return false; + } + + // old from new + Eigen::Matrix T_on = Eigen::Matrix::Identity(); + T_on.block<3,3>(0,0) = Eigen::AngleAxis(scale,rot).toRotationMatrix(); + T_on.block<3,1>(0,3) = scale*xyz; + + // Update + T_pc = (ToEigen(T_pc) * T_on.inverse()).eval(); + + return true; + } +#endif // HAVE_EIGEN + + return false; + } + + virtual bool MouseMotion( + const GLprecision /*win*/[3], const GLprecision /*obj*/[3], const GLprecision /*normal*/[3], + int /*button_state*/, int /*pickId*/ + ) override + { + return false; + } + + float axis_length; + const InteractiveIndex::Token label_x; + const InteractiveIndex::Token label_y; + const InteractiveIndex::Token label_z; +}; + +} diff --git a/include/pangolin/scene/interactive.h b/include/pangolin/scene/interactive.h new file mode 100644 index 0000000..272282d --- /dev/null +++ b/include/pangolin/scene/interactive.h @@ -0,0 +1,67 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin { + + +struct Interactive +{ + static __thread GLuint current_id; + + virtual bool Mouse( + int button, + const GLprecision win[3], const GLprecision obj[3], const GLprecision normal[3], + bool pressed, int button_state, int pickId + ) = 0; + + virtual bool MouseMotion( + const GLprecision win[3], const GLprecision obj[3], const GLprecision normal[3], + int button_state, int pickId + ) = 0; +}; + +struct RenderParams +{ + RenderParams() + : render_mode(GL_RENDER) + { + } + + GLint render_mode; +}; + +struct Manipulator : public Interactive +{ + virtual void Render(const RenderParams& params) = 0; +}; + +} diff --git a/include/pangolin/scene/interactive_index.h b/include/pangolin/scene/interactive_index.h new file mode 100644 index 0000000..af9862f --- /dev/null +++ b/include/pangolin/scene/interactive_index.h @@ -0,0 +1,115 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include + +namespace pangolin { + +class InteractiveIndex +{ +public: + class Token + { + public: + friend class InteractiveIndex; + + Token() + : id(0) + { + } + + Token(Token&& o) + : id(o.id) + { + o.id = 0; + } + + GLint Id() const + { + return id; + } + + ~Token() + { + if(id) { + InteractiveIndex::I().Unstore(*this); + } + } + + private: + Token(GLint id) + : id(id) + { + } + + + GLint id; + }; + + static InteractiveIndex& I() + { + static InteractiveIndex instance; + return instance; + } + + Interactive* Find(GLuint id) + { + auto kv = index.find(id); + if(kv != index.end()) { + return kv->second; + } + return nullptr; + } + + Token Store(Interactive* r) + { + index[next_id] = r; + return Token(next_id++); + } + + void Unstore(Token& t) + { + index.erase(t.id); + t.id = 0; + } + +private: + // Private constructor. + InteractiveIndex() + : next_id(1) + { + } + + GLint next_id; + std::map index; +}; + +} diff --git a/include/pangolin/scene/renderable.h b/include/pangolin/scene/renderable.h new file mode 100644 index 0000000..4f5772e --- /dev/null +++ b/include/pangolin/scene/renderable.h @@ -0,0 +1,118 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace pangolin { + +class Renderable +{ +public: + using guid_t = GLuint; + + static guid_t UniqueGuid() + { + static std::random_device rd; + static std::mt19937 gen(rd()); + return (guid_t)gen(); + } + + Renderable(const std::weak_ptr& parent = std::weak_ptr()) + : guid(UniqueGuid()), parent(parent), T_pc(IdentityMatrix()), should_show(true) + { + } + + virtual ~Renderable() + { + } + + // Default implementation simply renders children. + virtual void Render(const RenderParams& params = RenderParams()) { + RenderChildren(params); + } + + void RenderChildren(const RenderParams& params) + { + for(auto& p : children) { + Renderable& r = *p.second; + if(r.should_show) { + glPushMatrix(); + r.T_pc.Multiply(); + r.Render(params); + if(r.manipulator) { + r.manipulator->Render(params); + } + glPopMatrix(); + } + } + } + + std::shared_ptr FindChild(guid_t guid) + { + auto o = children.find(guid); + if(o != children.end()) { + return o->second; + } + + for(auto& kv : children ) { + std::shared_ptr c = kv.second->FindChild(guid); + if(c) return c; + } + + return std::shared_ptr(); + } + + Renderable& Add(const std::shared_ptr& child) + { + if(child) { + children[child->guid] = child; + }; + return *this; + } + + // Renderable properties + const guid_t guid; + std::weak_ptr parent; + pangolin::OpenGlMatrix T_pc; + std::shared_ptr child; + bool should_show; + + // Children + std::map> children; + + // Manipulator (handler, thing) + std::shared_ptr manipulator; +}; + +} diff --git a/include/pangolin/scene/scenehandler.h b/include/pangolin/scene/scenehandler.h new file mode 100644 index 0000000..c23f130 --- /dev/null +++ b/include/pangolin/scene/scenehandler.h @@ -0,0 +1,182 @@ +#pragma once + +#include +#include +#include + +namespace pangolin { + +inline void gluPickMatrix( + GLdouble x, GLdouble y, + GLdouble width, GLdouble height, + GLint viewport[4] +) { + GLfloat m[16]; + GLfloat sx, sy; + GLfloat tx, ty; + sx = viewport[2] / (GLfloat)width; + sy = viewport[3] / (GLfloat)height; + tx = (viewport[2] + 2.0f * (viewport[0] - (GLfloat)x)) / (GLfloat)width; + ty = (viewport[3] + 2.0f * (viewport[1] - (GLfloat)y)) / (GLfloat)height; +#define M(row, col) m[col*4+row] + M(0, 0) = sx; + M(0, 1) = 0.0f; + M(0, 2) = 0.0f; + M(0, 3) = tx; + M(1, 0) = 0.0f; + M(1, 1) = sy; + M(1, 2) = 0.0f; + M(1, 3) = ty; + M(2, 0) = 0.0f; + M(2, 1) = 0.0f; + M(2, 2) = 1.0f; + M(2, 3) = 0.0f; + M(3, 0) = 0.0f; + M(3, 1) = 0.0f; + M(3, 2) = 0.0f; + M(3, 3) = 1.0f; +#undef M + glMultMatrixf(m); +} + + + +struct SceneHandler : public Handler3D +{ + SceneHandler( + Renderable& scene, + OpenGlRenderState& cam_state + ) : Handler3D(cam_state), scene(scene) + { + + } + + void ProcessHitBuffer(GLint hits, GLuint* buf, std::map& hit_map ) + { + GLuint* closestNames = 0; + GLuint closestNumNames = 0; + GLuint closestZ = std::numeric_limits::max(); + for (int i = 0; i < hits; i++) { + if (buf[1] < closestZ) { + closestNames = buf + 3; + closestNumNames = buf[0]; + closestZ = buf[1]; + } + buf += buf[0] + 3; + } + for (unsigned int i = 0; i < closestNumNames; i++) { + const int pickId = closestNames[i]; + hit_map[pickId] = InteractiveIndex::I().Find(pickId); + } + } + + void ComputeHits(pangolin::View& view, + const pangolin::OpenGlRenderState& cam_state, + int x, int y, int grab_width, + std::map& hit_objects ) + { + // Get views viewport / modelview /projection + GLint viewport[4] = {view.v.l, view.v.b, view.v.w, view.v.h}; + pangolin::OpenGlMatrix mv = cam_state.GetModelViewMatrix(); + pangolin::OpenGlMatrix proj = cam_state.GetProjectionMatrix(); + + // Prepare hit buffer object + const unsigned int MAX_SEL_SIZE = 64; + GLuint vSelectBuf[MAX_SEL_SIZE]; + glSelectBuffer(MAX_SEL_SIZE, vSelectBuf); + + // Load and adjust modelview projection matrices + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPickMatrix(x, y, grab_width, grab_width, viewport); + proj.Multiply(); + glMatrixMode(GL_MODELVIEW); + mv.Load(); + + // Render scenegraph in 'select' mode + glRenderMode(GL_SELECT); + glInitNames(); + RenderParams select; + select.render_mode = GL_SELECT; + scene.Render(select); + glFlush(); + + GLint nHits = glRenderMode(GL_RENDER); + // std::cout << " -- Number of Hits are: " << nHits << std::endl; + // std::cout << " -- size of hitobjects: " << hit_objects.size() << std::endl; + if (nHits > 0) { + ProcessHitBuffer(nHits, vSelectBuf, hit_objects); + } + } + + void Mouse(pangolin::View& view, pangolin::MouseButton button, + int x, int y, bool pressed, int button_state) + { + GetPosNormal(view, x, y, p, Pw, Pc, n); + bool handled = false; + + if (pressed) { + m_selected_objects.clear(); + ComputeHits(view, *cam_state, x, y, 2*hwin+1, m_selected_objects); + } + + for (auto kv : m_selected_objects) + { + Interactive* ir = dynamic_cast(kv.second); + handled |= ir && ir->Mouse( button, p, Pw, n, pressed, button_state, kv.first); + } + + if (!handled) { + Handler3D::Mouse(view, button, x, y, pressed, button_state); + } + } + + void MouseMotion(pangolin::View& view, int x, int y, int button_state) + { + GetPosNormal(view, x, y, p, Pw, Pc, n); + bool handled = false; + for (auto kv : m_selected_objects) + { + Interactive* ir = dynamic_cast(kv.second); + + handled |= ir && ir->MouseMotion( p, Pw, n, button_state, kv.first); + } + if (!handled) { + pangolin::Handler3D::MouseMotion(view, x, y, button_state); + } + } + + void Special(pangolin::View& view, pangolin::InputSpecial inType, + float x, float y, float p1, float p2, float p3, float p4, + int button_state) + { + GetPosNormal(view, (int)x, (int)y, p, Pw, Pc, n); + + bool handled = false; + + if (inType == pangolin::InputSpecialScroll) + { + m_selected_objects.clear(); + ComputeHits(view, *cam_state, (int)x, (int)y, 2*hwin+1, m_selected_objects); + + const MouseButton button = p2 > 0 ? MouseWheelUp : MouseWheelDown; + + for (auto kv : m_selected_objects) + { + Interactive* ir = dynamic_cast(kv.second); + handled |= ir && ir->Mouse( button, p, Pw, n, true, button_state, kv.first); + } + } + + if (!handled) { + pangolin::Handler3D::Special(view, inType, x, y, + p1, p2, p3, p4, button_state); + } + } + + std::map m_selected_objects; + Renderable& scene; + unsigned int grab_width; +}; + +} diff --git a/include/pangolin/tools/video_viewer.h b/include/pangolin/tools/video_viewer.h new file mode 100644 index 0000000..184cb32 --- /dev/null +++ b/include/pangolin/tools/video_viewer.h @@ -0,0 +1,104 @@ +#include +#include +#include + +#include +#include +#include +#include + +namespace pangolin +{ + +PANGOLIN_EXPORT +class VideoViewer +{ +public: + typedef std::function >& images, + const picojson::value& properties)> FrameChangedCallbackFn; + + static constexpr int FRAME_SKIP = 30; + + VideoViewer(const std::string& window_name, const std::string& input_uri, const std::string& output_uri = "video.pango" ); + VideoViewer(const VideoViewer&) = delete; + + virtual ~VideoViewer(); + + void Run(); + void RunAsync(); + + void Quit(); + void QuitAndWait(); + + inline int TotalFrames() const + { + return video_playback ? video_playback->GetTotalFrames() : std::numeric_limits::max(); + } + + // Control playback + void OpenInput(const std::string& input_uri); + void CloseInput(); + + // Control recording + void Record(); + void RecordOneFrame(); + void StopRecording(); + + // Useful for user-control + void TogglePlay(); + void ToggleRecord(); + void ToggleDiscardBufferedFrames(); + void ToggleWaitForFrames(); + void SetDiscardBufferedFrames(bool new_state); + void SetWaitForFrames(bool new_state); + void Skip(int frames); + void ChangeExposure(int delta_us); + void ChangeGain(float delta); + void SetActiveCamera(int delta); + void DrawEveryNFrames(int n); + + + // Register to be notified of new image data + void SetFrameChangedCallback(FrameChangedCallbackFn cb); + + void WaitUntilExit(); + + + VideoInput& Video() {return video;} + const VideoInput& Video() const {return video;} + + void SetRecordNthFrame(int record_nth_frame_) { + record_nth_frame = record_nth_frame_; + } + + +protected: + void RegisterDefaultKeyShortcutsAndPangoVariables(); + + std::mutex control_mutex; + std::string window_name; + std::thread vv_thread; + + VideoInput video; + VideoPlaybackInterface* video_playback; + VideoInterface* video_interface; + + std::string output_uri; + + int current_frame; + int grab_until; + int record_nth_frame; + int draw_nth_frame; + bool video_grab_wait; + bool video_grab_newest; + bool should_run; + uint16_t active_cam; + + FrameChangedCallbackFn frame_changed_callback; +}; + + +void PANGOLIN_EXPORT RunVideoViewerUI(const std::string& input_uri, const std::string& output_uri); + +} diff --git a/include/pangolin/utils/argagg.hpp b/include/pangolin/utils/argagg.hpp new file mode 100644 index 0000000..70e2852 --- /dev/null +++ b/include/pangolin/utils/argagg.hpp @@ -0,0 +1,1548 @@ +/* + * @file + * @brief + * Defines a very simple command line argument parser. + * + * @copyright + * Copyright (c) 2017 Viet The Nguyen + * + * @copyright + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * @copyright + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#pragma once +#ifndef ARGAGG_ARGAGG_ARGAGG_HPP +#define ARGAGG_ARGAGG_ARGAGG_HPP + +#ifdef __unix__ +#include +#include +#endif // #ifdef __unix__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * @brief + * There are only two hard things in Computer Science: cache invalidation and + * naming things (Phil Karlton). + * + * The names of types have to be succint and clear. This has turned out to be a + * more difficult thing than I expected. Here you'll find a quick overview of + * the type names you'll find in this namespace (and thus "library"). + * + * When a program is invoked it is passed a number of "command line arguments". + * Each of these "arguments" is a string (C-string to be more precise). An + * "option" is a command line argument that has special meaning. This library + * recognizes a command line argument as a potential option if it starts with a + * dash ('-') or double-dash ('--'). + * + * A "parser" is a set of "definitions" (not a literal std::set but rather a + * std::vector). A parser is represented by the argagg::parser struct. + * + * A "definition" is a structure with four components that define what + * "options" are recognized. The four components are the name of the option, + * the strings that represent the option, the option's help text, and how many + * arguments the option should expect. "Flags" are the individual strings that + * represent the option ("-v" and "--verbose" are flags for the "verbose" + * option). A definition is represented by the argagg::definition struct. + * + * Note at this point that the word "option" can be used interchangeably to + * mean the notion of an option and the actual instance of an option given a + * set of command line arguments. To be unambiguous we use a "definition" to + * represent the notion of an option and an "option result" to represent an + * actual option parsed from a set of command line arguments. An "option + * result" is represented by the argagg::option_result struct. + * + * There's one more wrinkle to this: an option can show up multiple times in a + * given set of command line arguments. For example, "-n 1 -n 2 -n 3". This + * will parse into three distinct argagg::option_result instances, but all of + * them correspond to the same argagg::definition. We aggregate these into the + * argagg::option_results struct which represents "all parser results for a + * given option definition". This argagg::option_results is basically a + * std::vector of argagg::option_result. + * + * Options aren't the only thing parsed though. Positional arguments are also + * parsed. Thus a parser produces a result that contains both option results + * and positional arguments. The parser results are represented by the + * argagg::parser_results struct. All option results are stored in a mapping + * from option name to the argagg::option_results. All positional arguments are + * simply stored in a vector of C-strings. + */ +namespace argagg { + + +/** + * @brief + * This exception is thrown when a long option is parsed and is given an + * argument using the "=" syntax but the option doesn't expect an argument. + */ +struct unexpected_argument_error +: public std::runtime_error { + using std::runtime_error::runtime_error; +}; + + +/** + * @brief + * This exception is thrown when an option is parsed unexpectedly such as when + * an argument was expected for a previous option or if an option was found + * that has not been defined. + */ +struct unexpected_option_error +: public std::runtime_error { + using std::runtime_error::runtime_error; +}; + + +/** + * @brief + * This exception is thrown when an option requires an argument but is not + * provided one. This can happen if another flag was found after the option or + * if we simply reach the end of the command line arguments. + */ +struct option_lacks_argument_error +: public std::runtime_error { + using std::runtime_error::runtime_error; +}; + + +/** + * @brief + * This exception is thrown when an option's flag is invalid. This can be the + * case if the flag is not prefixed by one or two hyphens or contains non + * alpha-numeric characters after the hypens. See is_valid_flag_definition() + * for more details. + */ +struct invalid_flag +: public std::runtime_error { + using std::runtime_error::runtime_error; +}; + + +/** + * @brief + * The set of template instantiations that convert C-strings to other types for + * the option_result::as(), option_results::as(), parser_results::as(), and + * parser_results::all_as() methods are placed in this namespace. + */ +namespace convert { + + /** + * @brief + * Explicit instantiations of this function are used to convert arguments to + * types. + */ + template + T arg(const char* arg); + +} + + +/** + * @brief + * Represents a single option parse result. + * + * You can check if this has an argument by using the implicit boolean + * conversion. + */ +struct option_result { + + /** + * @brief + * Argument parsed for this single option. If no argument was parsed this + * will be set to nullptr. + */ + const char* arg; + + /** + * @brief + * Converts the argument parsed for this single option instance into the + * given type using the type matched conversion function + * argagg::convert::arg(). If there was not an argument parsed for this + * single option instance then a argagg::option_lacks_argument_error + * exception is thrown. The specific conversion function may throw other + * exceptions. + */ + template + T as() const; + + /** + * @brief + * Converts the argument parsed for this single option instance into the + * given type using the type matched conversion function + * argagg::convert::arg(). If there was not an argument parsed for this + * single option instance then the provided default value is returned + * instead. If the conversion function throws an exception then it is ignored + * and the default value is returned. + */ + template + T as(const T& t) const; + + /** + * @brief + * Since we have the argagg::option_result::as() API we might as well alias + * it as an implicit conversion operator. This performs implicit conversion + * using the argagg::option_result::as() method. + * + * @note + * An implicit boolean conversion specialization exists which returns false + * if there is no argument for this single option instance and true + * otherwise. This specialization DOES NOT convert the argument to a bool. If + * you need to convert the argument to a bool then use the as() API. + */ + template + operator T () const; + +}; + + +/** + * @brief + * Represents multiple option parse results for a single option. If treated as + * a single parse result it defaults to the last parse result. Note that an + * instance of this struct is always created even if no option results are + * parsed for a given definition. In that case it will simply be empty. + * + * To check if the associated option showed up at all simply use the implicit + * boolean conversion or check if count() is greater than zero. + */ +struct option_results { + + /** + * @brief + * All option parse results for this option. + */ + std::vector all; + + /** + * @brief + * Gets the number of times the option shows up. + */ + std::size_t count() const; + + /** + * @brief + * Gets a single option parse result by index. + */ + option_result& operator [] (std::size_t index); + + /** + * @brief + * Gets a single option result by index. + */ + const option_result& operator [] (std::size_t index) const; + + /** + * @brief + * Converts the argument parsed for the LAST option parse result for the + * parent definition to the provided type. For example, if this was for "-f 1 + * -f 2 -f 3" then calling this method for an integer type will return 3. If + * there are no option parse results then a std::out_of_range exception is + * thrown. Any exceptions thrown by option_result::as() are not + * handled. + */ + template + T as() const; + + /** + * @brief + * Converts the argument parsed for the LAST option parse result for the + * parent definition to the provided type. For example, if this was for "-f 1 + * -f 2 -f 3" then calling this method for an integer type will return 3. If + * there are no option parse results then the provided default value is + * returned instead. + */ + template + T as(const T& t) const; + + /** + * @brief + * Since we have the option_results::as() API we might as well alias + * it as an implicit conversion operator. This performs implicit conversion + * using the option_results::as() method. + * + * @note + * An implicit boolean conversion specialization exists which returns false + * if there is no argument for this single option instance and true + * otherwise. This specialization DOES NOT convert the argument to a bool. If + * you need to convert the argument to a bool then use the as() API. + */ + template + operator T () const; + +}; + + +/** + * @brief + * Represents all results of the parser including options and positional + * arguments. + */ +struct parser_results { + + /** + * @brief + * Returns the name of the program from the original arguments list. This is + * always the first argument. + */ + const char* program; + + /** + * @brief + * Maps from definition name to the structure which contains the parser + * results for that definition. + */ + std::unordered_map options; + + /** + * @brief + * Vector of positional arguments. + */ + std::vector pos; + + /** + * @brief + * Used to check if an option was specified at all. + */ + bool has_option(const std::string& name) const; + + /** + * @brief + * Get the parser results for the given definition. If the definition never + * showed up then the exception from the unordered_map access will bubble + * through so check if the flag exists in the first place with has_option(). + */ + option_results& operator [] (const std::string& name); + + /** + * @brief + * Get the parser results for the given definition. If the definition never + * showed up then the exception from the unordered_map access will bubble + * through so check if the flag exists in the first place with has_option(). + */ + const option_results& operator [] (const std::string& name) const; + + /** + * @brief + * Gets the number of positional arguments. + */ + std::size_t count() const; + + /** + * @brief + * Gets a positional argument by index. + */ + const char* operator [] (std::size_t index) const; + + /** + * @brief + * Gets a positional argument converted to the given type. + */ + template + T as(std::size_t i = 0) const; + + /** + * @brief + * Gets all positional arguments converted to the given type. + */ + template + std::vector all_as() const; + +}; + + +/** + * @brief + * An option definition which essentially represents what an option is. + */ +struct definition { + + /** + * @brief + * Name of the option. Option parser results are keyed by this name. + */ + const std::string name; + + /** + * @brief + * List of strings to match that correspond to this option. Should be fully + * specified with hyphens (e.g. "-v" or "--verbose"). + */ + std::vector flags; + + /** + * @brief + * Help string for this option. + */ + std::string help; + + /** + * @brief + * Number of arguments this option requires. Must be 0 or 1. All other values + * have undefined behavior. Okay, the code actually works with positive + * values in general, but it's unorthodox command line behavior. + */ + unsigned int num_args; + + /** + * @brief + * Returns true if this option does not want any arguments. + */ + bool wants_no_arguments() const; + + /** + * @brief + * Returns true if this option requires arguments. + */ + bool requires_arguments() const; + +}; + + +/** + * @brief + * Checks whether or not a command line argument should be processed as an + * option flag. This is very similar to is_valid_flag_definition() but must + * allow for short flag groups (e.g. "-abc") and equal-assigned long flag + * arguments (e.g. "--output=foo.txt"). + */ +bool cmd_line_arg_is_option_flag( + const char* s); + + +/** + * @brief + * Checks whether a flag in an option definition is valid. I suggest reading + * through the function source to understand what dictates a valid. + */ +bool is_valid_flag_definition( + const char* s); + + +/** + * @brief + * Tests whether or not a valid flag is short. Assumes the provided cstring is + * already a valid flag. + */ +bool flag_is_short( + const char* s); + + +/** + * @brief + * Contains two maps which aid in option parsing. The first map, @ref + * short_map, maps from a short flag (just a character) to a pointer to the + * original @ref definition that the flag represents. The second map, @ref + * long_map, maps from a long flag (an std::string) to a pointer to the + * original @ref definition that the flag represents. + * + * This object is usually a temporary that only exists during the parsing + * operation. It is typically constructed using @ref validate_definitions(). + */ +struct parser_map { + + /** + * @brief + * Maps from a short flag (just a character) to a pointer to the original + * @ref definition that the flag represents. + */ + std::array short_map; + + /** + * @brief + * Maps from a long flag (an std::string) to a pointer to the original @ref + * definition that the flag represents. + */ + std::unordered_map long_map; + + /** + * @brief + * Returns true if the provided short flag exists in the map object. + */ + bool known_short_flag( + const char flag) const; + + /** + * @brief + * If the short flag exists in the map object then it is returned by this + * method. If it doesn't then nullptr will be returned. + */ + const definition* get_definition_for_short_flag( + const char flag) const; + + /** + * @brief + * Returns true if the provided long flag exists in the map object. + */ + bool known_long_flag( + const std::string& flag) const; + + /** + * @brief + * If the long flag exists in the map object then it is returned by this + * method. If it doesn't then nullptr will be returned. + */ + const definition* get_definition_for_long_flag( + const std::string& flag) const; + +}; + + +/** + * @brief + * Validates a collection (specifically an std::vector) of @ref definition + * objects by checking if the contained flags are valid. If the set of @ref + * definition objects is not valid then an exception is thrown. Upon successful + * validation a @ref parser_map object is returned. + */ +parser_map validate_definitions( + const std::vector& definitions); + + +/** + * @brief + * A list of option definitions used to inform how to parse arguments. + */ +struct parser { + + /** + * @brief + * Vector of the option definitions which inform this parser how to parse + * the command line arguments. + */ + std::vector definitions; + + /** + * @brief + * Parses the provided command line arguments and returns the results as + * @ref parser_results. + * + * @note + * This method is not thread-safe and assumes that no modifications are made + * to the definitions member field during the extent of this method call. + */ + parser_results parse(int argc, const char** argv) const; + + /** + * @brief + * Through strict interpretation of pointer casting rules, despite this being + * a safe operation, C++ doesn't allow implicit casts from char** to + * const char** so here's an overload that performs a const_cast, + * which is typically frowned upon but is safe here. + */ + parser_results parse(int argc, char** argv) const; + +}; + + +/** + * @brief + * A convenience output stream that will accumulate what is streamed to it and + * then, on destruction, format the accumulated string using the fmt program + * (via the argagg::fmt_string() function) to the provided std::ostream. + * + * Example use: + * + * @code + * { + * argagg::fmt_ostream f(std::cerr); + * f << "Usage: " << really_long_string << std::endl; + * } // on destruction here the formatted string will be streamed to std::cerr + * @endcode + * + * @note + * This only has formatting behavior if the __unix__ preprocessor + * definition is defined since formatting relies on the POSIX API for forking, + * executing a process, and reading/writing to/from file descriptors. If that + * preprocessor definition is not defined then this class has the same overall + * behavior except the output string is not formatted (basically streams + * whatever the accumulated string is). See arggg::fmt_string(). + */ +struct fmt_ostream : public std::ostringstream { + + /** + * @brief + * Reference to the final output stream that the formatted string will be + * streamed to. + */ + std::ostream& output; + + /** + * @brief + * Construct to output to the provided output stream when this object is + * destroyed. + */ + fmt_ostream(std::ostream& output); + + /** + * @brief + * Special destructor that will format the accumulated string using fmt (via + * the argagg::fmt_string() function) and stream it to the std::ostream + * stored. + */ + ~fmt_ostream(); + +}; + + +/** + * @brief + * Processes the provided string using the fmt util and returns the resulting + * output as a string. Not the most efficient (in time or space) but gets the + * job done. + * + * This function is cowardly so if there are any errors encountered such as a + * syscall returning -1 then the input string is returned. + * + * @note + * This only has formatting behavior if the __unix__ preprocessor + * definition is defined since it relies on the POSIX API for forking, + * executing a process, reading/writing to/from file descriptors, and the + * existence of the fmt util. + */ +std::string fmt_string(const std::string& s); + + +} // namespace argagg + + +/** + * @brief + * Writes the option help to the given stream. + */ +std::ostream& operator << (std::ostream& os, const argagg::parser& x); + + +// ---- end of declarations, header-only implementations follow ---- + + +namespace argagg { + + +template +T option_result::as() const +{ + if (this->arg) { + return convert::arg(this->arg); + } else { + throw option_lacks_argument_error("option has no argument"); + } +} + + +template +T option_result::as(const T& t) const +{ + if (this->arg) { + try { + return convert::arg(this->arg); + } catch (...) { + return t; + } + } else { + // I actually think this will never happen. To call this method you have + // to access a specific option_result for an option. If there's a + // specific option_result then the option was found. If the option + // requires an argument then it will definitely have an argument + // otherwise the parser would have complained. + return t; + } +} + + +template +option_result::operator T () const +{ + return this->as(); +} + + +template <> inline +option_result::operator bool () const +{ + return this->arg != nullptr; +} + + +inline +std::size_t option_results::count() const +{ + return this->all.size(); +} + + +inline +option_result& option_results::operator [] (std::size_t index) +{ + return this->all[index]; +} + + +inline +const option_result& option_results::operator [] (std::size_t index) const +{ + return this->all[index]; +} + + +template +T option_results::as() const +{ + if (this->all.size() == 0) { + throw std::out_of_range("no option arguments to convert"); + } + return this->all.back().as(); +} + + +template +T option_results::as(const T& t) const +{ + if (this->all.size() == 0) { + return t; + } + return this->all.back().as(t); +} + + +template +option_results::operator T () const +{ + return this->as(); +} + + +template <> inline +option_results::operator bool () const +{ + return this->all.size() > 0; +} + + +inline +bool parser_results::has_option(const std::string& name) const +{ + const auto it = this->options.find(name); + return ( it != this->options.end()) && it->second.all.size() > 0; +} + + +inline +option_results& parser_results::operator [] (const std::string& name) +{ + return this->options.at(name); +} + + +inline +const option_results& +parser_results::operator [] (const std::string& name) const +{ + return this->options.at(name); +} + + +inline +std::size_t parser_results::count() const +{ + return this->pos.size(); +} + + +inline +const char* parser_results::operator [] (std::size_t index) const +{ + return this->pos[index]; +} + + +template +T parser_results::as(std::size_t i) const +{ + return convert::arg(this->pos[i]); +} + + +template +std::vector parser_results::all_as() const +{ + std::vector v(this->pos.size()); + std::transform( + this->pos.begin(), this->pos.end(), v.begin(), + [](const char* arg) { + return convert::arg(arg); + }); + return v; +} + + +inline +bool definition::wants_no_arguments() const +{ + return this->num_args == 0; +} + + +inline +bool definition::requires_arguments() const +{ + return this->num_args > 0; +} + + +inline +bool cmd_line_arg_is_option_flag( + const char* s) +{ + auto len = std::strlen(s); + + // The shortest possible flag has two characters: a hyphen and an + // alpha-numeric character. + if (len < 2) { + return false; + } + + // All flags must start with a hyphen. + if (s[0] != '-') { + return false; + } + + // Shift the name forward by a character to account for the initial hyphen. + // This means if s was originally "-v" then name will be "v". + const char* name = s + 1; + + // Check if we're dealing with a long flag. + bool is_long = false; + if (s[1] == '-') { + is_long = true; + + // Just -- is not a valid flag. + if (len == 2) { + return false; + } + + // Shift the name forward to account for the extra hyphen. This means if s + // was originally "--output" then name will be "output". + name = s + 2; + } + + // The first character of the flag name must be alpha-numeric. This is to + // prevent things like "---a" from being valid flags. + len = std::strlen(name); + if (!std::isalnum(name[0])) { + return false; + } + + // At this point in is_valid_flag_definition() we would check if the short + // flag has only one character. At command line specification you can group + // short flags together or even add an argument to a short flag without a + // space delimiter. Thus we don't check if this has only one character + // because it might not. + + // If this is a long flag then we expect all characters *up to* an equal sign + // to be alpha-numeric or a hyphen. After the equal sign you are specify the + // argument to a long flag which can be basically anything. + if (is_long) { + bool encountered_equal = false; + return std::all_of(name, name + len, [&](const char& c) { + if (encountered_equal) { + return true; + } else { + if (c == '=') { + encountered_equal = true; + return true; + } + return std::isalnum(c) || c == '-'; + } + }); + } + + // At this point we are not dealing with a long flag. We already checked that + // the first character is alpha-numeric so we've got the case of a single + // short flag covered. This might be a short flag group though and we might + // be tempted to check that each character of the short flag group is + // alpha-numeric. However, you can specify the argument for a short flag + // without a space delimiter (e.g. "-I/usr/local/include") so you can't tell + // if the rest of a short flag group is part of the argument or not unless + // you know what is a defined flag or not. We leave that kind of processing + // to the parser. + return true; +} + + +inline +bool is_valid_flag_definition( + const char* s) +{ + auto len = std::strlen(s); + + // The shortest possible flag has two characters: a hyphen and an + // alpha-numeric character. + if (len < 2) { + return false; + } + + // All flags must start with a hyphen. + if (s[0] != '-') { + return false; + } + + // Shift the name forward by a character to account for the initial hyphen. + // This means if s was originally "-v" then name will be "v". + const char* name = s + 1; + + // Check if we're dealing with a long flag. + bool is_long = false; + if (s[1] == '-') { + is_long = true; + + // Just -- is not a valid flag. + if (len == 2) { + return false; + } + + // Shift the name forward to account for the extra hyphen. This means if s + // was originally "--output" then name will be "output". + name = s + 2; + } + + // The first character of the flag name must be alpha-numeric. This is to + // prevent things like "---a" from being valid flags. + len = std::strlen(name); + if (!std::isalnum(name[0])) { + return false; + } + + // If this is a short flag then it must only have one character. + if (!is_long && len > 1) { + return false; + } + + // The rest of the characters must be alpha-numeric, but long flags are + // allowed to have hyphens too. + return std::all_of(name + 1, name + len, [&](const char& c) { + return std::isalnum(c) || (c == '-' && is_long); + }); +} + + +inline +bool flag_is_short( + const char* s) +{ + return s[0] == '-' && std::isalnum(s[1]); +} + + +inline +bool parser_map::known_short_flag( + const char flag) const +{ + return this->short_map[flag] != nullptr; +} + + +inline +const definition* parser_map::get_definition_for_short_flag( + const char flag) const +{ + return this->short_map[flag]; +} + + +inline +bool parser_map::known_long_flag( + const std::string& flag) const +{ + const auto existing_long_flag = this->long_map.find(flag); + return existing_long_flag != long_map.end(); +} + + +inline +const definition* parser_map::get_definition_for_long_flag( + const std::string& flag) const +{ + const auto existing_long_flag = this->long_map.find(flag); + if (existing_long_flag == long_map.end()) { + return nullptr; + } + return existing_long_flag->second; +} + + +inline +parser_map validate_definitions( + const std::vector& definitions) +{ + std::unordered_map long_map; + parser_map map {{{nullptr}}, std::move(long_map)}; + + for (auto& defn : definitions) { + + if (defn.flags.size() == 0) { + std::ostringstream msg; + msg << "option \"" << defn.name << "\" has no flag definitions"; + throw invalid_flag(msg.str()); + } + + for (auto& flag : defn.flags) { + + if (!is_valid_flag_definition(flag.data())) { + std::ostringstream msg; + msg << "flag \"" << flag << "\" specified for option \"" << defn.name + << "\" is invalid"; + throw invalid_flag(msg.str()); + } + + if (flag_is_short(flag.data())) { + const int short_flag_letter = flag[1]; + const auto existing_short_flag = map.short_map[short_flag_letter]; + bool short_flag_already_exists = (existing_short_flag != nullptr); + if (short_flag_already_exists) { + std::ostringstream msg; + msg << "duplicate short flag \"" << flag + << "\" found, specified by both option \"" << defn.name + << "\" and option \"" << existing_short_flag->name; + throw invalid_flag(msg.str()); + } + map.short_map[short_flag_letter] = &defn; + continue; + } + + // If we're here then this is a valid, long-style flag. + if (map.known_long_flag(flag)) { + const auto existing_long_flag = map.get_definition_for_long_flag(flag); + std::ostringstream msg; + msg << "duplicate long flag \"" << flag + << "\" found, specified by both option \"" << defn.name + << "\" and option \"" << existing_long_flag->name; + throw invalid_flag(msg.str()); + } + map.long_map.insert(std::make_pair(flag, &defn)); + } + } + + return map; +} + + +inline +parser_results parser::parse(int argc, const char** argv) const +{ + // Inspect each definition to see if its valid. You may wonder "why don't + // you do this validation on construction?" I had thought about it but + // realized that since I've made the parser an aggregate type (granted it + // just "aggregates" a single vector) I would need to track any changes to + // the definitions vector and re-run the validity check in order to + // maintain this expected "validity invariant" on the object. That would + // then require hiding the definitions vector as a private entry and then + // turning the parser into a thin interface (by re-exposing setters and + // getters) to the vector methods just so that I can catch when the + // definition has been modified. It seems much simpler to just enforce the + // validity when you actually want to parser because it's at the moment of + // parsing that you know the definitions are complete. + parser_map map = validate_definitions(this->definitions); + + // Initialize the parser results that we'll be returning. Store the program + // name (assumed to be the first command line argument) and initialize + // everything else as empty. + std::unordered_map options {}; + std::vector pos; + parser_results results {argv[0], std::move(options), std::move(pos)}; + + // Add an empty option result for each definition. + for (const auto& defn : this->definitions) { + option_results opt_results {{}}; + results.options.insert( + std::make_pair(defn.name, opt_results)); + } + + // Don't start off ignoring flags. We only ignore flags after a -- shows up + // in the command line arguments. + bool ignore_flags = false; + + // Keep track of any options that are expecting arguments. + const char* last_flag_expecting_args = nullptr; + option_result* last_option_expecting_args = nullptr; + unsigned int num_option_args_to_consume = 0; + + // Get pointers to pointers so we can treat the raw pointer array as an + // iterator for standard library algorithms. This isn't used yet but can be + // used to template this function to work on iterators over strings or + // C-strings. + const char** arg_i = argv + 1; + const char** arg_end = argv + argc; + + while (arg_i != arg_end) { + auto arg_i_cstr = *arg_i; + auto arg_i_len = std::strlen(arg_i_cstr); + + // Some behavior to note: if the previous option is expecting an argument + // then the next entry will be treated as a positional argument even if + // it looks like a flag. + bool treat_as_positional_argument = ( + ignore_flags + || num_option_args_to_consume > 0 + || !cmd_line_arg_is_option_flag(arg_i_cstr) + ); + if (treat_as_positional_argument) { + + // If last option is expecting some specific positive number of + // arguments then give this argument to that option, *regardless of + // whether or not the argument looks like a flag or is the special "--" + // argument*. + if (num_option_args_to_consume > 0) { + last_option_expecting_args->arg = arg_i_cstr; + --num_option_args_to_consume; + ++arg_i; + continue; + } + + // Now we check if this is just "--" which is a special argument that + // causes all following arguments to be treated as non-options and is + // itselve discarded. + if (std::strncmp(arg_i_cstr, "--", 2) == 0 && arg_i_len == 2) { + ignore_flags = true; + ++arg_i; + continue; + } + + // If there are no expectations for option arguments then simply use + // this argument as a positional argument. + results.pos.push_back(arg_i_cstr); + ++arg_i; + continue; + } + + // Reset the "expecting argument" state. + last_flag_expecting_args = nullptr; + last_option_expecting_args = nullptr; + num_option_args_to_consume = 0; + + // If we're at this point then we're definitely dealing with something + // that is flag-like and has hyphen as the first character and has a + // length of at least two characters. How we handle this potential flag + // depends on whether or not it is a long-option so we check that first. + bool is_long_flag = (arg_i_cstr[1] == '-'); + + if (is_long_flag) { + + // Long flags have a complication: their arguments can be specified + // using an '=' character right inside the argument. That means an + // argument like "--output=foobar.txt" is actually an option with flag + // "--output" and argument "foobar.txt". So we look for the first + // instance of the '=' character and keep it in long_flag_arg. If + // long_flag_arg is nullptr then we didn't find '='. We need the + // flag_len to construct long_flag_str below. + auto long_flag_arg = std::strchr(arg_i_cstr, '='); + std::size_t flag_len = arg_i_len; + if (long_flag_arg != nullptr) { + flag_len = long_flag_arg - arg_i_cstr; + } + std::string long_flag_str(arg_i_cstr, flag_len); + + if (!map.known_long_flag(long_flag_str)) { + std::ostringstream msg; + msg << "found unexpected flag: " << long_flag_str; + throw unexpected_option_error(msg.str()); + } + + const auto defn = map.get_definition_for_long_flag(long_flag_str); + + if (long_flag_arg != nullptr && defn->num_args == 0) { + std::ostringstream msg; + msg << "found argument for option not expecting an argument: " + << arg_i_cstr; + throw unexpected_argument_error(msg.str()); + } + + // We've got a legitimate, known long flag option so we add an option + // result. This option result initially has an arg of nullptr, but that + // might change in the following block. + auto& opt_results = results.options[defn->name]; + option_result opt_result {nullptr}; + opt_results.all.push_back(std::move(opt_result)); + + if (defn->requires_arguments()) { + bool there_is_an_equal_delimited_arg = (long_flag_arg != nullptr); + if (there_is_an_equal_delimited_arg) { + // long_flag_arg would be "=foo" in the "--output=foo" case so we + // increment by 1 to get rid of the equal sign. + opt_results.all.back().arg = long_flag_arg + 1; + } else { + last_flag_expecting_args = arg_i_cstr; + last_option_expecting_args = &(opt_results.all.back()); + num_option_args_to_consume = defn->num_args; + } + } + + ++arg_i; + continue; + } + + // If we've made it here then we're looking at either a short flag or a + // group of short flags. Short flags can be grouped together so long as + // they don't require any arguments unless the option that does is the + // last in the group ("-o x -v" is okay, "-vo x" is okay, "-ov x" is + // not). So starting after the dash we're going to process each character + // as if it were a separate flag. Note "sf_idx" stands for "short flag + // index". + for (std::size_t sf_idx = 1; sf_idx < arg_i_len; ++sf_idx) { + const auto short_flag = arg_i_cstr[sf_idx]; + + if (!std::isalnum(short_flag)) { + std::ostringstream msg; + msg << "found non-alphanumeric character '" << arg_i_cstr[sf_idx] + << "' in flag group '" << arg_i_cstr << "'"; + throw std::domain_error(msg.str()); + } + + if (!map.known_short_flag(short_flag)) { + std::ostringstream msg; + msg << "found unexpected flag '" << arg_i_cstr[sf_idx] + << "' in flag group '" << arg_i_cstr << "'"; + throw unexpected_option_error(msg.str()); + } + + auto defn = map.get_definition_for_short_flag(short_flag); + auto& opt_results = results.options[defn->name]; + + // Create an option result with an empty argument (for now) and add it + // to this option's results. + option_result opt_result {nullptr}; + opt_results.all.push_back(std::move(opt_result)); + + if (defn->requires_arguments()) { + + // If this short flag's option requires an argument and we're the + // last flag in the short flag group then just put the parser into + // "expecting argument for last option" state and move onto the next + // command line argument. + bool is_last_short_flag_in_group = (sf_idx == arg_i_len - 1); + if (is_last_short_flag_in_group) { + last_flag_expecting_args = arg_i_cstr; + last_option_expecting_args = &(opt_results.all.back()); + num_option_args_to_consume = defn->num_args; + break; + } + + // If this short flag's option requires an argument and we're NOT the + // last flag in the short flag group then we automatically consume + // the rest of the short flag group as the argument for this flag. + // This is how we get the POSIX behavior of being able to specify a + // flag's arguments without a white space delimiter (e.g. + // "-I/usr/local/include"). + opt_results.all.back().arg = arg_i_cstr + sf_idx + 1; + break; + } + } + + ++arg_i; + continue; + } + + // If we're done with all of the arguments but are still expecting + // arguments for a previous option then we haven't satisfied that option. + // This is an error. + if (num_option_args_to_consume > 0) { + std::ostringstream msg; + msg << "last option \"" << last_flag_expecting_args + << "\" expects an argument but the parser ran out of command line " + << "arguments to parse"; + throw option_lacks_argument_error(msg.str()); + } + + return results; +} + + +inline +parser_results parser::parse(int argc, char** argv) const +{ + return parse(argc, const_cast(argv)); +} + + +namespace convert { + + + /** + * @brief + * Templated function for conversion to T using the @ref std::strtol() + * function. This is used for anything long length or shorter (long, int, + * short, char). + */ + template inline + T long_(const char* arg) + { + char* endptr = nullptr; + errno = 0; + T ret = static_cast(std::strtol(arg, &endptr, 0)); + if (endptr == arg) { + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); + } + if (errno == ERANGE) { + throw std::out_of_range("argument numeric value out of range"); + } + return ret; + } + + + /** + * @brief + * Templated function for conversion to T using the @ref std::strtoll() + * function. This is used for anything long long length or shorter (long + * long). + */ + template inline + T long_long_(const char* arg) + { + char* endptr = nullptr; + errno = 0; + T ret = static_cast(std::strtoll(arg, &endptr, 0)); + if (endptr == arg) { + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); + } + if (errno == ERANGE) { + throw std::out_of_range("argument numeric value out of range"); + } + return ret; + } + + +#define DEFINE_CONVERSION_FROM_LONG_(TYPE) \ + template <> inline \ + TYPE arg(const char* arg) \ + { \ + return long_(arg); \ + } + + DEFINE_CONVERSION_FROM_LONG_(char) + DEFINE_CONVERSION_FROM_LONG_(unsigned char) + DEFINE_CONVERSION_FROM_LONG_(signed char) + DEFINE_CONVERSION_FROM_LONG_(short) + DEFINE_CONVERSION_FROM_LONG_(unsigned short) + DEFINE_CONVERSION_FROM_LONG_(int) + DEFINE_CONVERSION_FROM_LONG_(unsigned int) + DEFINE_CONVERSION_FROM_LONG_(long) + DEFINE_CONVERSION_FROM_LONG_(unsigned long) + +#undef DEFINE_CONVERSION_FROM_LONG_ + + +#define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE) \ + template <> inline \ + TYPE arg(const char* arg) \ + { \ + return long_long_(arg); \ + } + + DEFINE_CONVERSION_FROM_LONG_LONG_(long long) + DEFINE_CONVERSION_FROM_LONG_LONG_(unsigned long long) + +#undef DEFINE_CONVERSION_FROM_LONG_LONG_ + + + template <> inline + bool arg(const char* arg) + { + return argagg::convert::arg(arg) != 0; + } + + + template <> inline + float arg(const char* arg) + { + char* endptr = nullptr; + errno = 0; + float ret = std::strtof(arg, &endptr); + if (endptr == arg) { + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); + } + if (errno == ERANGE) { + throw std::out_of_range("argument numeric value out of range"); + } + return ret; + } + + + template <> inline + double arg(const char* arg) + { + char* endptr = nullptr; + errno = 0; + double ret = std::strtod(arg, &endptr); + if (endptr == arg) { + std::ostringstream msg; + msg << "unable to convert argument to integer: \"" << arg << "\""; + throw std::invalid_argument(msg.str()); + } + if (errno == ERANGE) { + throw std::out_of_range("argument numeric value out of range"); + } + return ret; + } + + + template <> inline + const char* arg(const char* arg) + { + return arg; + } + + + template <> inline + std::string arg(const char* arg) + { + return std::string(arg); + } + +} + + +inline +fmt_ostream::fmt_ostream(std::ostream& output) +: std::ostringstream(), output(output) +{ +} + + +inline +fmt_ostream::~fmt_ostream() +{ + output << fmt_string(this->str()); +} + + +#ifdef __unix__ + + +inline +std::string fmt_string(const std::string& s) +{ + constexpr int read_end = 0; + constexpr int write_end = 1; + + // TODO (vnguyen): This function overall needs to handle possible error + // returns from the various syscalls. + + int read_pipe[2]; + int write_pipe[2]; + if (pipe(read_pipe) == -1) { + return s; + } + if (pipe(write_pipe) == -1) { + return s; + } + + auto parent_pid = fork(); + bool is_fmt_proc = (parent_pid == 0); + if (is_fmt_proc) { + dup2(write_pipe[read_end], STDIN_FILENO); + dup2(read_pipe[write_end], STDOUT_FILENO); + close(write_pipe[read_end]); + close(write_pipe[write_end]); + close(read_pipe[read_end]); + close(read_pipe[write_end]); + const char* argv[] = {"fmt", NULL}; + execvp(const_cast(argv[0]), const_cast(argv)); + } + + close(write_pipe[read_end]); + close(read_pipe[write_end]); + auto fmt_write_fd = write_pipe[write_end]; + auto write_result = write(fmt_write_fd, s.c_str(), s.length()); + if (write_result != static_cast(s.length())) { + return s; + } + close(fmt_write_fd); + + auto fmt_read_fd = read_pipe[read_end]; + std::ostringstream os; + char buf[64]; + while (true) { + auto read_count = read( + fmt_read_fd, reinterpret_cast(buf), sizeof(buf)); + if (read_count <= 0) { + break; + } + os.write(buf, static_cast(read_count)); + } + close(fmt_read_fd); + + return os.str(); +} + + +#else // #ifdef __unix__ + + +inline +std::string fmt_string(const std::string& s) +{ + return s; +} + + +#endif // #ifdef __unix__ + + +} // namespace argagg + + +inline +std::ostream& operator << (std::ostream& os, const argagg::parser& x) +{ + for (auto& definition : x.definitions) { + os << " "; + for (auto& flag : definition.flags) { + os << flag; + if (flag != definition.flags.back()) { + os << ", "; + } + } + os << std::endl; + os << " " << definition.help << std::endl; + } + return os; +} + + +#endif // ARGAGG_ARGAGG_ARGAGG_HPP diff --git a/include/pangolin/utils/assert.h b/include/pangolin/utils/assert.h new file mode 100644 index 0000000..f0e7097 --- /dev/null +++ b/include/pangolin/utils/assert.h @@ -0,0 +1,60 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Hauke Strasdat, Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#ifdef __GNUC__ +# define PANGO_FUNCTION __PRETTY_FUNCTION__ +#elif (_MSC_VER >= 1310) +# define PANGO_FUNCTION __FUNCTION__ +#else +# define PANGO_FUNCTION "unknown" +#endif + +namespace pangolin { + +template PANGO_HOST_DEVICE +void abort(const char* function, const char* file, int line, Args&&... args) +{ + std::printf("pangolin::abort() in function '%s', file '%s', line %d.\n", function, file, line); +#ifndef __CUDACC__ + std::cout << FormatString(std::forward(args)...) << std::endl; + std::abort(); +#endif +} + +} + +// Always check, even in debug +#define PANGO_ENSURE(expr, ...) ((expr) ? ((void)0) : pangolin::abort(PANGO_FUNCTION, __FILE__, __LINE__, ##__VA_ARGS__)) + +// May be disabled for optimisation +#define PANGO_ASSERT(expr, ...) ((expr) ? ((void)0) : pangolin::abort(PANGO_FUNCTION, __FILE__, __LINE__, ##__VA_ARGS__)) diff --git a/include/pangolin/utils/compontent_cast.h b/include/pangolin/utils/compontent_cast.h new file mode 100644 index 0000000..9482f23 --- /dev/null +++ b/include/pangolin/utils/compontent_cast.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#ifdef HAVE_EIGEN +# include +#endif + +namespace pangolin +{ + +// Scalar / Vector agnostic static_cast-like thing +// +// e.g. Promote float to double: +// ComponentCast::cast(0.14f); +// +// e.g. Promote Eigen::Vector2f to Eigen::Vector2d: +// ComponentCast::cast(Eigen::Vector2f(0.1,0.2); + +template +struct ComponentCast +{ + PANGO_HOST_DEVICE + static To cast(const From& val) + { + return static_cast(val); + } +}; + +#ifdef HAVE_EIGEN +template +struct ComponentCast > +{ + PANGO_HOST_DEVICE + static To cast(const Eigen::MatrixBase& val) + { + return val.template cast(); + } +}; +#endif + +} diff --git a/include/pangolin/utils/file_extension.h b/include/pangolin/utils/file_extension.h new file mode 100644 index 0000000..4daf274 --- /dev/null +++ b/include/pangolin/utils/file_extension.h @@ -0,0 +1,70 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +enum ImageFileType +{ + ImageFileTypePpm, + ImageFileTypeTga, + ImageFileTypePng, + ImageFileTypeJpg, + ImageFileTypeTiff, + ImageFileTypeGif, + ImageFileTypeExr, + ImageFileTypePango, + ImageFileTypePvn, + ImageFileTypeZstd, + ImageFileTypeUnknown +}; + + +PANGOLIN_EXPORT +std::string ImageFileTypeToName(ImageFileType); + +PANGOLIN_EXPORT +ImageFileType NameToImageFileType(const std::string&); + +PANGOLIN_EXPORT +std::string FileLowercaseExtention(const std::string& filename); + +PANGOLIN_EXPORT +ImageFileType FileTypeMagic(const unsigned char data[], size_t bytes); + +PANGOLIN_EXPORT +ImageFileType FileTypeExtension(const std::string& ext); + +PANGOLIN_EXPORT +ImageFileType FileType(const std::string& filename); + +} diff --git a/include/pangolin/utils/file_utils.h b/include/pangolin/utils/file_utils.h new file mode 100644 index 0000000..b3a6fb1 --- /dev/null +++ b/include/pangolin/utils/file_utils.h @@ -0,0 +1,145 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace pangolin +{ + +PANGOLIN_EXPORT +std::vector& Split(const std::string& s, char delim, std::vector& elements); + +PANGOLIN_EXPORT +std::vector Split(const std::string &s, char delim); + +PANGOLIN_EXPORT +std::vector Expand(const std::string &s, char open='[', char close=']', char delim=','); + +PANGOLIN_EXPORT +std::string SanitizePath(const std::string& path); + +PANGOLIN_EXPORT +std::string PathParent(const std::string& path, int levels = 1); + +PANGOLIN_EXPORT +bool FileExists(const std::string& filename); + +PANGOLIN_EXPORT +std::string FindPath(const std::string& child_path, const std::string& signature_path); + +PANGOLIN_EXPORT +std::string PathExpand(const std::string& sPath); + +PANGOLIN_EXPORT +bool MatchesWildcard(const std::string& str, const std::string& wildcard); + +PANGOLIN_EXPORT +bool FilesMatchingWildcard(const std::string& wildcard_file_path, std::vector& file_vec); + +PANGOLIN_EXPORT +std::string MakeUniqueFilename(const std::string& filename); + +PANGOLIN_EXPORT +bool IsPipe(const std::string& file); + +PANGOLIN_EXPORT +bool IsPipe(int fd); + +PANGOLIN_EXPORT +int WritablePipeFileDescriptor(const std::string& file); + +/** + * Open the file for reading. Note that it is opened with O_NONBLOCK. The pipe + * open is done in two stages so that the producer knows a reader is waiting + * (but not blocked). The reader then checks PipeHasDataToRead() until it + * returns true. The file can then be opened. Note that the file descriptor + * should be closed after the read stream has been created so that the write + * side of the pipe does not get signaled. + */ +PANGOLIN_EXPORT +int ReadablePipeFileDescriptor(const std::string& file); + +PANGOLIN_EXPORT +bool PipeHasDataToRead(int fd); + +PANGOLIN_EXPORT +void FlushPipe(const std::string& file); + +// TODO: Tidy these inlines up / move them + +inline bool StartsWith(const std::string& str, const std::string& prefix) +{ + return !str.compare(0, prefix.size(), prefix); +} + +inline bool EndsWith(const std::string& str, const std::string& prefix) +{ + return !str.compare(str.size() - prefix.size(), prefix.size(), prefix); +} + +inline std::string Trim(const std::string& str, const std::string& delimiters = " \f\n\r\t\v" ) +{ + const size_t f = str.find_first_not_of( delimiters ); + return f == std::string::npos ? + "" : + str.substr( f, str.find_last_not_of( delimiters ) + 1 ); +} + +inline void ToUpper( std::string& str ) +{ + std::transform(str.begin(), str.end(), str.begin(), ::toupper); +} + +inline void ToLower( std::string& str ) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); +} + +inline std::string ToUpperCopy( const std::string& str ) +{ + std::string out; + out.resize(str.size()); + std::transform(str.begin(), str.end(), out.begin(), ::toupper); + return out; +} + +inline std::string ToLowerCopy( const std::string& str ) +{ + std::string out; + out.resize(str.size()); + std::transform(str.begin(), str.end(), out.begin(), ::tolower); + return out; +} + + +} diff --git a/include/pangolin/utils/fix_size_buffer_queue.h b/include/pangolin/utils/fix_size_buffer_queue.h new file mode 100644 index 0000000..ce4d932 --- /dev/null +++ b/include/pangolin/utils/fix_size_buffer_queue.h @@ -0,0 +1,153 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace pangolin +{ + +template +class FixSizeBuffersQueue +{ + +public: + FixSizeBuffersQueue() {} + + ~FixSizeBuffersQueue() { + } + + BufPType getNewest() { + std::lock_guard vlock(vMtx); + std::lock_guard elock(eMtx); + if(validBuffers.size() == 0) { + // Empty queue. + return 0; + } else { + // Requeue all but newest buffers. + while(validBuffers.size() > 1) { + emptyBuffers.push_back(std::move(validBuffers.front())); + validBuffers.pop_front(); + } + // Return newest buffer. + BufPType bp = std::move(validBuffers.front()); + validBuffers.pop_front(); + return bp; + } + } + + BufPType getNext() { + std::lock_guard vlock(vMtx); + if(validBuffers.size() == 0) { + // Empty queue. + return 0; + } else { + // Return oldest buffer. + BufPType bp = std::move(validBuffers.front()); + validBuffers.pop_front(); + return bp; + } + } + + BufPType getFreeBuffer() { + std::lock_guard vlock(vMtx); + std::lock_guard elock(eMtx); + if(emptyBuffers.size() > 0) { + // Simply get a free buffer from the free buffers list. + BufPType bp = std::move(emptyBuffers.front()); + emptyBuffers.pop_front(); + return bp; + } else { + if(validBuffers.size() == 0) { + // Queue not yet initialized. + throw std::runtime_error("Queue not yet initialised."); + } else { + std::cerr << "Out of free buffers." << std::endl; + // No free buffers return oldest among the valid buffers. + BufPType bp = std::move(validBuffers.front()); + validBuffers.pop_front(); + return bp; + } + } + } + + void addValidBuffer(BufPType bp) { + // Add buffer to valid buffers queue. + std::lock_guard vlock(vMtx); + validBuffers.push_back(std::move(bp)); + } + + void returnOrAddUsedBuffer(BufPType bp) { + // Add buffer back to empty buffers queue. + std::lock_guard elock(eMtx); + emptyBuffers.push_back(std::move(bp)); + } + + size_t AvailableFrames() const { + std::lock_guard vlock(vMtx); + return validBuffers.size(); + } + + size_t EmptyBuffers() const { + std::lock_guard elock(eMtx); + return emptyBuffers.size(); + } + + bool DropNFrames(size_t n) { + std::lock_guard vlock(vMtx); + if(validBuffers.size() < n) { + return false; + } else { + std::lock_guard elock(eMtx); + // Requeue all but newest buffers. + for(unsigned int i=0; i validBuffers; + std::list emptyBuffers; + mutable std::mutex vMtx; + mutable std::mutex eMtx; +// unsigned int maxNumBuffers; +// unsigned int bufferSizeBytes; +}; + +} diff --git a/include/pangolin/utils/format_string.h b/include/pangolin/utils/format_string.h new file mode 100644 index 0000000..7b39f9a --- /dev/null +++ b/include/pangolin/utils/format_string.h @@ -0,0 +1,87 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Hauke Strasdat, Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin { +namespace details { + +// Following: http://stackoverflow.com/a/22759544 +template +class IsStreamable { +private: + template + static auto test(int) -> decltype( (std::declval() << std::declval(), std::true_type()) ); + + template + static auto test(...) -> std::false_type; + +public: + static const bool value = decltype(test(0))::value; +}; + +inline void FormatStream(std::stringstream& stream, const char* text) +{ + stream << text; +} + +// Following: http://en.cppreference.com/w/cpp/language/parameter_pack +template +void FormatStream(std::stringstream& stream, const char* text, T arg, Args... args) +{ + static_assert(IsStreamable::value, + "One of the args has not an ostream overload!"); + for (; *text != '\0'; ++text) { + if (*text == '%') { + stream << arg; + FormatStream(stream, text + 1, args...); + return; + } + stream << *text; + } + stream << "\nFormat-Warning: There are " << sizeof...(Args) + 1 + << " args unused."; +} + +} // namespace details + +template +std::string FormatString(const char* text, Args... args) +{ + std::stringstream stream; + details::FormatStream(stream, text, args...); + return stream.str(); +} + +inline std::string FormatString() +{ + return std::string(); +} + +} diff --git a/include/pangolin/utils/log.h b/include/pangolin/utils/log.h new file mode 100644 index 0000000..e9e3536 --- /dev/null +++ b/include/pangolin/utils/log.h @@ -0,0 +1,44 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +// TODO: Something a bit more useful here, probably using format_string.h + +#pragma once + +#ifndef _ANDROID_ +# include +# define pango_print_debug(...) printf(__VA_ARGS__) +# define pango_print_info(...) printf(__VA_ARGS__) +# define pango_print_error(...) fprintf(stderr, __VA_ARGS__) +# define pango_print_warn(...) fprintf(stderr, __VA_ARGS__) +#else +# include +# define pango_print_debug(...) __android_log_print(ANDROID_LOG_DEBUG, "pango", __VA_ARGS__ ); +# define pango_print_info(...) __android_log_print(ANDROID_LOG_INFO, "pango", __VA_ARGS__ ); +# define pango_print_error(...) __android_log_print(ANDROID_LOG_ERROR, "pango", __VA_ARGS__ ); +# define pango_print_warn(...) __android_log_print(ANDROID_LOG_ERROR, "pango", __VA_ARGS__ ); +#endif diff --git a/include/pangolin/utils/memstreambuf.h b/include/pangolin/utils/memstreambuf.h new file mode 100644 index 0000000..ce03819 --- /dev/null +++ b/include/pangolin/utils/memstreambuf.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace pangolin { + +// A simple streambuf wrapper around std::vector for memory buffer use +struct memstreambuf : public std::streambuf +{ +public: + memstreambuf(size_t initial_buffer_size) + { + buffer.reserve(initial_buffer_size); + } + + size_t size() const + { + return buffer.size(); + } + + const unsigned char* data() const + { + return buffer.data(); + } + +protected: + std::streamsize xsputn(const char_type* __s, std::streamsize __n) override + { + buffer.insert(buffer.end(), __s, __s + __n); + return __n; + } + + int_type overflow(int_type __c) override + { + buffer.push_back( static_cast(__c) ); + return __c; + } + + std::vector buffer; +}; + +} diff --git a/include/pangolin/utils/params.h b/include/pangolin/utils/params.h new file mode 100644 index 0000000..beebeae --- /dev/null +++ b/include/pangolin/utils/params.h @@ -0,0 +1,80 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT Params +{ +public: + typedef std::vector> ParamMap; + + Params() + { + } + + Params(std::initializer_list> l) + : params(l) + { + } + + bool Contains(const std::string& key) const + { + for(ParamMap::const_iterator it = params.begin(); it!=params.end(); ++it) { + if(it->first == key) return true; + } + return false; + } + + template + T Get(const std::string& key, T default_val) const + { + // Return last value passed to the key. + for(ParamMap::const_reverse_iterator it = params.rbegin(); it!=params.rend(); ++it) { + if(it->first == key) return Convert::Do(it->second); + } + return default_val; + } + + template + void Set(const std::string& key, const T& val) + { + params.push_back(std::pair(key,Convert::Do(val))); + } + + ParamMap params; +}; + +} diff --git a/include/pangolin/utils/picojson.h b/include/pangolin/utils/picojson.h new file mode 100644 index 0000000..0a5cb9f --- /dev/null +++ b/include/pangolin/utils/picojson.h @@ -0,0 +1,1408 @@ +/* This is a (butchered) derivative of picojson, incorporated into the + * Pangolin Project (http://github.com/stevenlovegrove/Pangolin) + * + * Modifications Copyright (c) 2014 Steven Lovegrove, + * Original licence applies (below), https://github.com/kazuho/picojson + */ + +/* + * Copyright 2009-2010 Cybozu Labs, Inc. + * Copyright 2011-2014 Kazuho Oku + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef picojson_h +#define picojson_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Enable INT64 support +#define PICOJSON_USE_INT64 + +// for isnan/isinf +#if __cplusplus>=201103L +# include +#else +extern "C" { +# ifdef _MSC_VER +# include +# elif defined(__INTEL_COMPILER) +# include +# else +# include +# endif +} +#endif + +// experimental support for int64_t (see README.mkdn for detail) +#ifdef PICOJSON_USE_INT64 +# define __STDC_FORMAT_MACROS +# include +# include +#endif // PICOJSON_USE_INT64 + +// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 +#ifndef PICOJSON_USE_LOCALE +# define PICOJSON_USE_LOCALE 1 +#endif // PICOJSON_USE_LOCALE + +#if PICOJSON_USE_LOCALE +extern "C" { +# include +} +#endif // PICOJSON_USE_LOCALE + +#ifndef PICOJSON_ASSERT +# define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0) +#endif + +#ifdef _MSC_VER +#define SNPRINTF _snprintf_s +#pragma warning(push) +#pragma warning(disable : 4244) // conversion from int to char +#else +#define SNPRINTF snprintf +#endif + +#ifdef _MSC_VER +#define PICOJSON_FALSE_TEMPLATE_TYPE nullptr +#else +#define PICOJSON_FALSE_TEMPLATE_TYPE ((void*)0) +#endif + +namespace picojson { + +enum { + null_type, + boolean_type, + number_type, + string_type, + array_type, + object_type +#ifdef PICOJSON_USE_INT64 + , int64_type +#endif +}; + +enum { + INDENT_WIDTH = 2 +}; + +struct null {}; + +class value { +public: + typedef std::vector array; + typedef std::map object; + + union _storage { + bool boolean_; + double number_; +#ifdef PICOJSON_USE_INT64 + int64_t int64_; +#endif + std::string* string_; + array* array_; + object* object_; + }; +protected: + int type_; + _storage u_; +public: + + + ///////////////////////////// + // Constructors / Destructor + ///////////////////////////// + + ~value(); + value(); + value(int type, bool); + + ///////////////////////////// + // Implicit type constructors + ///////////////////////////// + + value(bool b) : type_(boolean_type) { + u_.boolean_ = b; + } + +#ifdef PICOJSON_USE_INT64 + value(short v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(unsigned short v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(int v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(unsigned int v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(long v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(unsigned long v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(long long v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } + value(unsigned long long v) : type_(int64_type) { + u_.int64_ = static_cast(v); + } +#endif + + value(float n) : type_(number_type) { + if ( + #ifdef _MSC_VER + ! _finite(n) + #elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf)) + std::isnan(n) || std::isinf(n) + #else + isnan(n) || isinf(n) + #endif + ) { + throw std::overflow_error(""); + } + u_.number_ = static_cast(n); + } + + value(double n) : type_(number_type) { + if ( + #ifdef _MSC_VER + ! _finite(n) + #elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf)) + std::isnan(n) || std::isinf(n) + #else + isnan(n) || isinf(n) + #endif + ) { + throw std::overflow_error(""); + } + u_.number_ = n; + } + + value(const array& a); + value(const object& o); + + value(const std::string& s); + value(const char* s); + value(const char* s, size_t len); + + value(const value& x); + value& operator=(const value& x); + + ///////////////////////////// + // Query / get type + ///////////////////////////// + + void swap(value& x); + template bool is() const; + template const T& get() const; + template T& get(); + bool evaluate_as_boolean() const; + size_t size() const; + + ///////////////////////////// + // Usage as array + ///////////////////////////// + + value& operator[](size_t idx); + const value& operator[](size_t idx) const; + bool contains(size_t idx) const; + value& push_back(const value& = value() ); + + ///////////////////////////// + // Usage as object + ///////////////////////////// + + value& operator[](const std::string& key); + const value& operator[](const std::string& key) const; + bool contains(const std::string& key) const; + + template + T get_value(const std::string& key, T default_value) const; + + ///////////////////////////// + // Serialization + ///////////////////////////// + + std::string to_str() const; + template void serialize(Iter os, bool prettify = false) const; + std::string serialize(bool prettify = false) const; +private: + template value(const T*); // intentionally defined to block implicit conversion of pointer to bool + template static void _indent(Iter os, int indent); + template void _serialize(Iter os, int indent) const; + std::string _serialize(int indent) const; +}; + +typedef value::array array; +typedef value::object object; + +inline value::value() : type_(null_type) {} + +inline value::value(int type, bool) : type_(type) { + switch (type) { +#define INIT(p, v) case p##type: u_.p = v; break + INIT(boolean_, false); + INIT(number_, 0.0); +#ifdef PICOJSON_USE_INT64 + INIT(int64_, 0); +#endif + INIT(string_, new std::string()); + INIT(array_, new array()); + INIT(object_, new object()); +#undef INIT + default: break; + } +} + +inline value::value(const std::string& s) : type_(string_type) { + u_.string_ = new std::string(s); +} + +inline value::value(const array& a) : type_(array_type) { + u_.array_ = new array(a); +} + +inline value::value(const object& o) : type_(object_type) { + u_.object_ = new object(o); +} + +inline value::value(const char* s) : type_(string_type) { + u_.string_ = new std::string(s); +} + +inline value::value(const char* s, size_t len) : type_(string_type) { + u_.string_ = new std::string(s, len); +} + +inline value::~value() { + switch (type_) { +#define DEINIT(p) case p##type: delete u_.p; break + DEINIT(string_); + DEINIT(array_); + DEINIT(object_); +#undef DEINIT + default: break; + } +} + +inline value::value(const value& x) : type_(x.type_) { + switch (type_) { +#define INIT(p, v) case p##type: u_.p = v; break + INIT(string_, new std::string(*x.u_.string_)); + INIT(array_, new array(*x.u_.array_)); + INIT(object_, new object(*x.u_.object_)); +#undef INIT + default: + u_ = x.u_; + break; + } +} + +inline value& value::operator=(const value& x) { + if (this != &x) { + this->~value(); + new (this) value(x); + } + return *this; +} + +inline void value::swap(value& x) { + std::swap(type_, x.type_); + std::swap(u_, x.u_); +} + +#define IS(ctype, jtype) \ + template <> inline bool value::is() const { \ + return type_ == jtype##_type; \ +} +IS(null, null) +IS(bool, boolean) +#ifdef PICOJSON_USE_INT64 +IS(int64_t, int64) +#endif +IS(std::string, string) +IS(array, array) +IS(object, object) +#undef IS +template <> inline bool value::is() const { + return type_ == number_type + #ifdef PICOJSON_USE_INT64 + || type_ == int64_type + #endif + ; +} + +#define GET(ctype, var) \ + template <> inline const ctype& value::get() const { \ + PICOJSON_ASSERT("type mismatch! call is() before get()" \ + && is()); \ + return var; \ +} \ + template <> inline ctype& value::get() { \ + PICOJSON_ASSERT("type mismatch! call is() before get()" \ + && is()); \ + return var; \ +} +GET(bool, u_.boolean_) +GET(std::string, *u_.string_) +GET(array, *u_.array_) +GET(object, *u_.object_) +#ifdef PICOJSON_USE_INT64 +GET(double, (type_ == int64_type && (const_cast(this)->type_ = number_type, const_cast(this)->u_.number_ = u_.int64_), u_.number_)) +GET(int64_t, u_.int64_) +#else +GET(double, u_.number_) +#endif +#undef GET + +inline bool value::evaluate_as_boolean() const { + switch (type_) { + case null_type: + return false; + case boolean_type: + return u_.boolean_; + case number_type: + return u_.number_ != 0; + case string_type: + return ! u_.string_->empty(); + default: + return true; + } +} + +inline size_t value::size() const +{ + PICOJSON_ASSERT("Type mismatch! Not array." && is()); + return u_.array_->size(); +} + +inline const value& value::operator[](size_t idx) const { + PICOJSON_ASSERT("Type mismatch! Not array." && is()); + PICOJSON_ASSERT("Out of bounds." && idx < u_.array_->size() ); + return (*u_.array_)[idx]; +} + +inline value& value::operator[](size_t idx) { + if( type_ == null_type ) { + // instantiate as array + type_ = array_type; + u_.array_ = new array(); + } + + PICOJSON_ASSERT("Type mismatch! Not array." && is()); + PICOJSON_ASSERT("Out of bounds." && idx < u_.array_->size() ); + return (*u_.array_)[idx]; +} + +inline bool value::contains(size_t idx) const { + if( type_ == array_type) { + return idx < u_.array_->size(); + }else{ + return false; + } +} + +inline value& value::push_back(const value& val) +{ + if( type_ == null_type ) { + // instantiate as array + type_ = array_type; + u_.array_ = new array(); + } + PICOJSON_ASSERT("Type mismatch! Not array." && is()); + u_.array_->push_back( val ); + return u_.array_->back(); +} + +inline const value& value::operator[](const std::string& key) const { + PICOJSON_ASSERT("Type mismatch! Not object." && is() ); + object::const_iterator i = u_.object_->find(key); + PICOJSON_ASSERT("Key not found." && i != u_.object_->end() ); + return i->second; +} + +inline value& value::operator[](const std::string& key) { + if( type_ == null_type ) { + // instantiate as object + type_ = object_type; + u_.object_ = new object(); + } + PICOJSON_ASSERT("Type mismatch! Not object." && is()); + return u_.object_->operator [](key); +} + +inline bool value::contains(const std::string& key) const { + if( type_ == object_type) { + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end(); + }else{ + return false; + } +} + +template +inline T value::get_value(const std::string& key, T default_value) const { + if(contains(key)) { + return (*this)[key].get(); + }else{ + return default_value; + } +} + +inline std::string value::to_str() const { + switch (type_) { + case null_type: return "null"; + case boolean_type: return u_.boolean_ ? "true" : "false"; +#ifdef PICOJSON_USE_INT64 + case int64_type: { + char buf[sizeof("-9223372036854775808")]; + SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); + return buf; + } +#endif + case number_type: { + char buf[256]; + double tmp; + SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); +#if PICOJSON_USE_LOCALE + char *decimal_point = localeconv()->decimal_point; + if (strcmp(decimal_point, ".") != 0) { + size_t decimal_point_len = strlen(decimal_point); + for (char *p = buf; *p != '\0'; ++p) { + if (strncmp(p, decimal_point, decimal_point_len) == 0) { + return std::string(buf, p) + "." + (p + decimal_point_len); + } + } + } +#endif + return buf; + } + case string_type: return *u_.string_; + case array_type: return "array"; + case object_type: return "object"; + default: PICOJSON_ASSERT("value::to_str() assert failed." && 0); +#ifdef _MSC_VER + __assume(0); +#endif + } +} + +template void copy(const std::string& s, Iter oi) { + std::copy(s.begin(), s.end(), oi); +} + +template void serialize_str(const std::string& s, Iter oi) { + *oi++ = '"'; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { + switch (*i) { +#define MAP(val, sym) case val: copy(sym, oi); break + MAP('"', "\\\""); + MAP('\\', "\\\\"); + MAP('/', "\\/"); + MAP('\b', "\\b"); + MAP('\f', "\\f"); + MAP('\n', "\\n"); + MAP('\r', "\\r"); + MAP('\t', "\\t"); +#undef MAP + default: + if (static_cast(*i) < 0x20 || *i == 0x7f) { + char buf[7]; + SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); + copy(buf, buf + 6, oi); + } else { + *oi++ = *i; + } + break; + } + } + *oi++ = '"'; +} + +template void value::serialize(Iter oi, bool prettify) const { + return _serialize(oi, prettify ? 0 : -1); +} + +inline std::string value::serialize(bool prettify) const { + return _serialize(prettify ? 0 : -1); +} + +template void value::_indent(Iter oi, int indent) { + *oi++ = '\n'; + for (int i = 0; i < indent * INDENT_WIDTH; ++i) { + *oi++ = ' '; + } +} + +template void value::_serialize(Iter oi, int indent) const { + switch (type_) { + case string_type: + serialize_str(*u_.string_, oi); + break; + case array_type: { + *oi++ = '['; + if (indent != -1) { + ++indent; + } + for (array::const_iterator i = u_.array_->begin(); + i != u_.array_->end(); + ++i) { + if (i != u_.array_->begin()) { + *oi++ = ','; + } + if (indent != -1) { + _indent(oi, indent); + } + i->_serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (! u_.array_->empty()) { + _indent(oi, indent); + } + } + *oi++ = ']'; + break; + } + case object_type: { + *oi++ = '{'; + if (indent != -1) { + ++indent; + } + for (object::const_iterator i = u_.object_->begin(); + i != u_.object_->end(); + ++i) { + if (i != u_.object_->begin()) { + *oi++ = ','; + } + if (indent != -1) { + _indent(oi, indent); + } + serialize_str(i->first, oi); + *oi++ = ':'; + if (indent != -1) { + *oi++ = ' '; + } + i->second._serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (! u_.object_->empty()) { + _indent(oi, indent); + } + } + *oi++ = '}'; + break; + } + default: + copy(to_str(), oi); + break; + } + if (indent == 0) { + *oi++ = '\n'; + } +} + +inline std::string value::_serialize(int indent) const { + std::string s; + _serialize(std::back_inserter(s), indent); + return s; +} + +template class input { +protected: + Iter cur_, end_; + int last_ch_; + bool ungot_; + int line_; +public: + input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} + int getc() { + if (ungot_) { + ungot_ = false; + return last_ch_; + } + if (cur_ == end_) { + last_ch_ = -1; + return -1; + } + if (last_ch_ == '\n') { + line_++; + } + last_ch_ = *cur_ & 0xff; + ++cur_; + return last_ch_; + } + void ungetc() { + if (last_ch_ != -1) { + PICOJSON_ASSERT(! ungot_); + ungot_ = true; + } + } + Iter cur() const { return cur_; } + int line() const { return line_; } + void skip_ws() { + while (1) { + int ch = getc(); + if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { + ungetc(); + break; + } + } + } + bool expect(int expect) { + skip_ws(); + if (getc() != expect) { + ungetc(); + return false; + } + return true; + } + bool match(const std::string& pattern) { + for (std::string::const_iterator pi(pattern.begin()); + pi != pattern.end(); + ++pi) { + if (getc() != *pi) { + ungetc(); + return false; + } + } + return true; + } +}; + +template inline int _parse_quadhex(input &in) { + int uni_ch = 0, hex; + for (int i = 0; i < 4; i++) { + if ((hex = in.getc()) == -1) { + return -1; + } + if ('0' <= hex && hex <= '9') { + hex -= '0'; + } else if ('A' <= hex && hex <= 'F') { + hex -= 'A' - 0xa; + } else if ('a' <= hex && hex <= 'f') { + hex -= 'a' - 0xa; + } else { + in.ungetc(); + return -1; + } + uni_ch = uni_ch * 16 + hex; + } + return uni_ch; +} + +template inline bool _parse_codepoint(String& out, input& in) { + int uni_ch; + if ((uni_ch = _parse_quadhex(in)) == -1) { + return false; + } + if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { + if (0xdc00 <= uni_ch) { + // a second 16-bit of a surrogate pair appeared + return false; + } + // first 16-bit of surrogate pair, get the next one + if (in.getc() != '\\' || in.getc() != 'u') { + in.ungetc(); + return false; + } + int second = _parse_quadhex(in); + if (! (0xdc00 <= second && second <= 0xdfff)) { + return false; + } + uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); + uni_ch += 0x10000; + } + if (uni_ch < 0x80) { + out.push_back(uni_ch); + } else { + if (uni_ch < 0x800) { + out.push_back(0xc0 | (uni_ch >> 6)); + } else { + if (uni_ch < 0x10000) { + out.push_back(0xe0 | (uni_ch >> 12)); + } else { + out.push_back(0xf0 | (uni_ch >> 18)); + out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); + } + out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); + } + out.push_back(0x80 | (uni_ch & 0x3f)); + } + return true; +} + +template inline bool _parse_string(String& out, input& in, int delim='"') { + while (1) { + int ch = in.getc(); + if (ch < ' ') { + in.ungetc(); + return false; + } else if (ch == delim) { + return true; + } else if (ch == '\\') { + if ((ch = in.getc()) == -1) { + return false; + } + switch (ch) { +#define MAP(sym, val) case sym: out.push_back(val); break + MAP('"', '\"'); + MAP('\\', '\\'); + MAP('/', '/'); + MAP('b', '\b'); + MAP('f', '\f'); + MAP('n', '\n'); + MAP('r', '\r'); + MAP('t', '\t'); +#undef MAP + case 'u': + if (! _parse_codepoint(out, in)) { + return false; + } + break; + default: + return false; + } + } else { + out.push_back(ch); + } + } +} + +template inline bool _parse_array(Context& ctx, input& in) { + if (! ctx.parse_array_start()) { + return false; + } + size_t idx = 0; + if (in.expect(']')) { + return ctx.parse_array_stop(idx); + } + do { + if (! ctx.parse_array_item(in, idx)) { + return false; + } + idx++; + } while (in.expect(',')); + return in.expect(']') && ctx.parse_array_stop(idx); +} + +template inline bool _parse_object(Context& ctx, input& in) { + if (! ctx.parse_object_start()) { + return false; + } + if (in.expect('}')) { + return true; + } + do { + std::string key; + // Support " and ' delimited strings + if( in.expect('"') ) { + if( !_parse_string(key, in, '"') ) + return false; + }else if (!in.expect('\'') || !_parse_string(key, in,'\'') ) { + return false; + } + if (!in.expect(':') || !ctx.parse_object_item(in, key)) { + return false; + } + } while (in.expect(',')); + return in.expect('}'); +} + +template inline std::string _parse_number(input& in) { + std::string num_str; + while (1) { + int ch = in.getc(); + if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' + || ch == 'e' || ch == 'E') { + num_str.push_back(ch); + } else if (ch == '.') { +#if PICOJSON_USE_LOCALE + num_str += localeconv()->decimal_point; +#else + num_str.push_back('.'); +#endif + } else { + in.ungetc(); + break; + } + } + return num_str; +} + +template inline bool _parse(Context& ctx, input& in) { + in.skip_ws(); + int ch = in.getc(); + switch (ch) { +#define IS(ch, text, op) case ch: \ + if (in.match(text) && op) { \ + return true; \ + } else { \ + return false; \ + } + IS('n', "ull", ctx.set_null()); + IS('f', "alse", ctx.set_bool(false)); + IS('t', "rue", ctx.set_bool(true)); +#undef IS + case '"': + return ctx.parse_string(in); + case '[': + return _parse_array(ctx, in); + case '{': + return _parse_object(ctx, in); + default: + if (('0' <= ch && ch <= '9') || ch == '-') { + double f; + char *endp; + in.ungetc(); + std::string num_str = _parse_number(in); + if (num_str.empty()) { + return false; + } +#ifdef PICOJSON_USE_INT64 + { + errno = 0; + intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); + if (errno == 0 + && std::numeric_limits::min() <= ival + && ival <= std::numeric_limits::max() + && endp == num_str.c_str() + num_str.size()) { + ctx.set_int64(ival); + return true; + } + } +#endif + f = strtod(num_str.c_str(), &endp); + if (endp == num_str.c_str() + num_str.size()) { + ctx.set_number(f); + return true; + } + return false; + } + break; + } + in.ungetc(); + return false; +} + +class deny_parse_context { +public: + bool set_null() { return false; } + bool set_bool(bool) { return false; } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { return false; } +#endif + bool set_number(double) { return false; } + template bool parse_string(input&) { return false; } + bool parse_array_start() { return false; } + template bool parse_array_item(input&, size_t) { + return false; + } + bool parse_array_stop(size_t) { return false; } + bool parse_object_start() { return false; } + template bool parse_object_item(input&, const std::string&) { + return false; + } +}; + +class default_parse_context { +protected: + value* out_; +public: + default_parse_context(value* out) : out_(out) {} + bool set_null() { + *out_ = value(); + return true; + } + bool set_bool(bool b) { + *out_ = value(b); + return true; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t i) { + *out_ = value(i); + return true; + } +#endif + bool set_number(double f) { + *out_ = value(f); + return true; + } + template bool parse_string(input& in) { + *out_ = value(string_type, false); + return _parse_string(out_->get(), in); + } + bool parse_array_start() { + *out_ = value(array_type, false); + return true; + } + template bool parse_array_item(input& in, size_t) { + array& a = out_->get(); + a.push_back(value()); + default_parse_context ctx(&a.back()); + return _parse(ctx, in); + } + bool parse_array_stop(size_t) { return true; } + bool parse_object_start() { + *out_ = value(object_type, false); + return true; + } + template bool parse_object_item(input& in, const std::string& key) { + object& o = out_->get(); + default_parse_context ctx(&o[key]); + return _parse(ctx, in); + } +private: + default_parse_context(const default_parse_context&); + default_parse_context& operator=(const default_parse_context&); +}; + +class null_parse_context { +public: + struct dummy_str { + void push_back(int) {} + }; +public: + null_parse_context() {} + bool set_null() { return true; } + bool set_bool(bool) { return true; } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { return true; } +#endif + bool set_number(double) { return true; } + template bool parse_string(input& in) { + dummy_str s; + return _parse_string(s, in); + } + bool parse_array_start() { return true; } + template bool parse_array_item(input& in, size_t) { + return _parse(*this, in); + } + bool parse_array_stop(size_t) { return true; } + bool parse_object_start() { return true; } + template bool parse_object_item(input& in, const std::string&) { + return _parse(*this, in); + } +private: + null_parse_context(const null_parse_context&); + null_parse_context& operator=(const null_parse_context&); +}; + +// obsolete, use the version below +template inline std::string parse(value& out, Iter& pos, const Iter& last) { + std::string err; + pos = parse(out, pos, last, &err); + return err; +} + +template inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { + input in(first, last); + if (! _parse(ctx, in) && err != NULL) { + char buf[64]; + SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); + *err = buf; + while (1) { + int ch = in.getc(); + if (ch == -1 || ch == '\n') { + break; + } else if (ch >= ' ') { + err->push_back(ch); + } + } + } + return in.cur(); +} + +template inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { + default_parse_context ctx(&out); + return _parse(ctx, first, last, err); +} + +inline std::string parse(value& out, std::istream& is) { + std::string err; + parse(out, std::istreambuf_iterator(is.rdbuf()), + std::istreambuf_iterator(), &err); + return err; +} + +template struct last_error_t { + static std::string s; +}; +template std::string last_error_t::s; + +inline void set_last_error(const std::string& s) { + last_error_t::s = s; +} + +inline const std::string& get_last_error() { + return last_error_t::s; +} + +inline bool operator==(const value& x, const value& y) { + if (x.is()) + return y.is(); +#define PICOJSON_CMP(type) \ + if (x.is()) \ + return y.is() && x.get() == y.get() + PICOJSON_CMP(bool); + PICOJSON_CMP(double); + PICOJSON_CMP(std::string); + PICOJSON_CMP(array); + PICOJSON_CMP(object); +#undef PICOJSON_CMP + PICOJSON_ASSERT(0); +#ifdef _MSC_VER + __assume(0); +#endif +} + +inline bool operator!=(const value& x, const value& y) { + return ! (x == y); +} + +} // namespace json + +inline std::istream& operator>>(std::istream& is, picojson::value& x) +{ + picojson::set_last_error(std::string()); + std::string err = picojson::parse(x, is); + if (! err.empty()) { + picojson::set_last_error(err); + is.setstate(std::ios::failbit); + } + return is; +} + +inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) +{ + x.serialize(std::ostream_iterator(os)); + return os; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // picojson_h + + + + +#ifdef TEST_PICOJSON + +#ifdef _MSC_VER +#pragma warning(disable : 4127) // conditional expression is constant +#endif // _MSC_VER + +using namespace std; + +static bool success = true; +static int test_num = 0; + +static void ok(bool b, const char* name = "") +{ + if (! b) + success = false; + printf("%s %d - %s\n", b ? "ok" : "ng", ++test_num, name); +} + +static void done_testing() +{ + printf("1..%d\n", test_num); +} + +template void is(const T& x, const T& y, const char* name = "") +{ + if (x == y) { + ok(true, name); + } else { + ok(false, name); + } +} + +#include +#include +#include +#include + +int main(void) +{ +#if PICOJSON_USE_LOCALE + setlocale(LC_ALL, ""); +#endif // PICOJSON_USE_LOCALE + + // constructors +#define TEST(expr, expected) \ + is(picojson::value expr .serialize(), string(expected), "picojson::value" #expr) + + TEST( (true), "true"); + TEST( (false), "false"); + TEST( (42.0), "42"); + TEST( (string("hello")), "\"hello\""); + TEST( ("hello"), "\"hello\""); + TEST( ("hello", 4), "\"hell\""); + + { + double a = 1; + for (int i = 0; i < 1024; i++) { + picojson::value vi(a); + std::stringstream ss; + ss << vi; + picojson::value vo; + ss >> vo; + double b = vo.get(); + if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) { + printf("ng i=%d a=%.18e b=%.18e\n", i, a, b); + } + a *= 2; + } + } + +#undef TEST + +#define TEST(in, type, cmp, serialize_test) { \ + picojson::value v; \ + const char* s = in; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + ok(err.empty(), in " no error"); \ + ok(v.is(), in " check type"); \ + is(v.get(), cmp, in " correct output"); \ + is(*s, '\0', in " read to eof"); \ + if (serialize_test) { \ + is(v.serialize(), string(in), in " serialize"); \ +} \ +} + TEST("false", bool, false, true); + TEST("true", bool, true, true); + TEST("90.5", double, 90.5, false); + TEST("1.7976931348623157e+308", double, DBL_MAX, false); + TEST("\"hello\"", string, string("hello"), true); + TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"), + true); + TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string, + string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), false); + TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false); +#ifdef PICOJSON_USE_INT64 + TEST("0", int64_t, 0, true); + TEST("-9223372036854775808", int64_t, std::numeric_limits::min(), true); + TEST("9223372036854775807", int64_t, std::numeric_limits::max(), true); +#endif // PICOJSON_USE_INT64 + +#undef TEST + +#define TEST(type, expr) { \ + picojson::value v; \ + const char *s = expr; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + ok(err.empty(), "empty " #type " no error"); \ + ok(v.is(), "empty " #type " check type"); \ + ok(v.get().empty(), "check " #type " array size"); \ +} + TEST(array, "[]"); + TEST(object, "{}"); +#undef TEST + + { + picojson::value v; + const char *s = "[1,true,\"hello\"]"; + string err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "array no error"); + ok(v.is(), "array check type"); + is(v.get().size(), size_t(3), "check array size"); + ok(v.contains(0), "check contains array[0]"); + ok(v.get(0).is(), "check array[0] type"); + is(v.get(0).get(), 1.0, "check array[0] value"); + ok(v.contains(1), "check contains array[1]"); + ok(v.get(1).is(), "check array[1] type"); + ok(v.get(1).get(), "check array[1] value"); + ok(v.contains(2), "check contains array[2]"); + ok(v.get(2).is(), "check array[2] type"); + is(v.get(2).get(), string("hello"), "check array[2] value"); + ok(!v.contains(3), "check not contains array[3]"); + } + + { + picojson::value v; + const char *s = "{ \"a\": true }"; + string err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "object no error"); + ok(v.is(), "object check type"); + is(v.get().size(), size_t(1), "check object size"); + ok(v.contains("a"), "check contains property"); + ok(v.get("a").is(), "check bool property exists"); + is(v.get("a").get(), true, "check bool property value"); + is(v.serialize(), string("{\"a\":true}"), "serialize object"); + ok(!v.contains("z"), "check not contains property"); + } + +#define TEST(json, msg) do { \ + picojson::value v; \ + const char *s = json; \ + string err = picojson::parse(v, s, s + strlen(s)); \ + is(err, string("syntax error at line " msg), msg); \ +} while (0) + TEST("falsoa", "1 near: oa"); + TEST("{]", "1 near: ]"); + TEST("\n\bbell", "2 near: bell"); + TEST("\"abc\nd\"", "1 near: "); +#undef TEST + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 == v2), "check == operator in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 != v2), "check != operator for array in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 != v2), "check != operator for object in deep comparison"); + } + + { + picojson::value v1, v2; + const char *s; + string err; + s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; + err = picojson::parse(v1, s, s + strlen(s)); + picojson::object& o = v1.get(); + o.erase("b"); + picojson::array& a = o["a"].get(); + picojson::array::iterator i; + i = std::remove(a.begin(), a.end(), picojson::value(std::string("three"))); + a.erase(i, a.end()); + s = "{ \"a\": [1,2], \"d\": 2 }"; + err = picojson::parse(v2, s, s + strlen(s)); + ok((v1 == v2), "check erase()"); + } + + ok(picojson::value(3.0).serialize() == "3", + "integral number should be serialized as a integer"); + + { + const char* s = "{ \"a\": [1,2], \"d\": 2 }"; + picojson::null_parse_context ctx; + string err; + picojson::_parse(ctx, s, s + strlen(s), &err); + ok(err.empty(), "null_parse_context"); + } + + { + picojson::value v; + const char *s = "{ \"a\": 1, \"b\": [ 2, { \"b1\": \"abc\" } ], \"c\": {}, \"d\": [] }"; + string err; + err = picojson::parse(v, s, s + strlen(s)); + ok(err.empty(), "parse test data for prettifying output"); + ok(v.serialize() == "{\"a\":1,\"b\":[2,{\"b1\":\"abc\"}],\"c\":{},\"d\":[]}", "non-prettifying output"); + ok(v.serialize(true) == "{\n \"a\": 1,\n \"b\": [\n 2,\n {\n \"b1\": \"abc\"\n }\n ],\n \"c\": {},\n \"d\": []\n}\n", "prettifying output"); + } + + try { + picojson::value v(std::numeric_limits::quiet_NaN()); + ok(false, "should not accept NaN"); + } catch (std::overflow_error e) { + ok(true, "should not accept NaN"); + } + + try { + picojson::value v(std::numeric_limits::infinity()); + ok(false, "should not accept infinity"); + } catch (std::overflow_error e) { + ok(true, "should not accept infinity"); + } + + try { + picojson::value v(123.); + ok(! v.is(), "is() should return false"); + v.get(); + ok(false, "get() should raise an error"); + } catch (std::runtime_error e) { + ok(true, "get() should raise an error"); + } + +#ifdef PICOJSON_USE_INT64 + { + picojson::value v1((int64_t)123); + ok(v1.is(), "is int64_t"); + ok(v1.is(), "is double as well"); + ok(v1.serialize() == "123", "serialize the value"); + ok(v1.get() == 123, "value is correct as int64_t"); + ok(v1.get(), "value is correct as double"); + + ok(! v1.is(), "is no more int64_type once get() is called"); + ok(v1.is(), "and is still a double"); + + const char *s = "-9223372036854775809"; + ok(picojson::parse(v1, s, s + strlen(s)).empty(), "parse underflowing int64_t"); + ok(! v1.is(), "underflowing int is not int64_t"); + ok(v1.is(), "underflowing int is double"); + ok(v1.get() + 9.22337203685478e+18 < 65536, "double value is somewhat correct"); + } +#endif // PICOJSON_USE_INT64 + + done_testing(); + + return success ? 0 : 1; +} + +#endif // TEST_PICOJSON diff --git a/include/pangolin/utils/posix/condition_variable.h b/include/pangolin/utils/posix/condition_variable.h new file mode 100644 index 0000000..77e1b9f --- /dev/null +++ b/include/pangolin/utils/posix/condition_variable.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +namespace pangolin +{ + +class ConditionVariableInterface +{ +public: + virtual ~ConditionVariableInterface() + { + } + + virtual void wait() = 0; + virtual bool wait(timespec t) = 0; + virtual void signal() = 0; + virtual void broadcast() = 0; +}; + +std::shared_ptr create_named_condition_variable(const + std::string& name); +std::shared_ptr open_named_condition_variable(const + std::string& name); +} diff --git a/include/pangolin/utils/posix/semaphore.h b/include/pangolin/utils/posix/semaphore.h new file mode 100644 index 0000000..8563d2b --- /dev/null +++ b/include/pangolin/utils/posix/semaphore.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +namespace pangolin +{ + + class SemaphoreInterface + { + public: + + virtual ~SemaphoreInterface() { + } + + virtual bool tryAcquire() = 0; + virtual void acquire() = 0; + virtual void release() = 0; + }; + + std::shared_ptr create_named_semaphore(const std::string& name, + unsigned int value); + std::shared_ptr open_named_semaphore(const std::string& name); + +} diff --git a/include/pangolin/utils/posix/shared_memory_buffer.h b/include/pangolin/utils/posix/shared_memory_buffer.h new file mode 100644 index 0000000..8ece2bb --- /dev/null +++ b/include/pangolin/utils/posix/shared_memory_buffer.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include + +namespace pangolin +{ + class SharedMemoryBufferInterface + { + public: + virtual ~SharedMemoryBufferInterface() { + } + virtual bool tryLock() = 0; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual unsigned char *ptr() = 0; + virtual std::string name() = 0; + }; + + std::shared_ptr create_named_shared_memory_buffer(const + std::string& name, size_t size); + std::shared_ptr open_named_shared_memory_buffer(const + std::string& name, bool readwrite); +} diff --git a/include/pangolin/utils/registration.h b/include/pangolin/utils/registration.h new file mode 100644 index 0000000..70a3cae --- /dev/null +++ b/include/pangolin/utils/registration.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +namespace pangolin { + +template +class Registration +{ +public: + using UnregisterFunc = std::function; + + Registration() + : token() + { + } + + Registration(TokenType token, UnregisterFunc unregister) + : token(token), unregister(unregister) + { + + } + + // No copy constructor + Registration(const Registration&) = delete; + + // Default move constructor + Registration(Registration&& o) + { + *this = std::move(o); + } + + Registration& operator =(Registration&& o) + { + token = o.token; + unregister = std::move(o.unregister); + o.unregister = nullptr; + return *this; + } + + ~Registration() + { + Release(); + } + + void Release() + { + if(unregister) { + unregister(token); + token = TokenType(); + } + } + + const TokenType& Token() + { + return token; + } + +private: + TokenType token; + UnregisterFunc unregister; +}; + +} diff --git a/include/pangolin/utils/signal_slot.h b/include/pangolin/utils/signal_slot.h new file mode 100644 index 0000000..2d20c39 --- /dev/null +++ b/include/pangolin/utils/signal_slot.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +#include + +namespace pangolin { + +// Based on http://simmesimme.github.io/tutorials/2015/09/20/signal-slot +template +class Signal +{ +public: + using Id = size_t; + using Reg = Registration; + + Signal() + : current_id_(0) + { + } + + Signal(const Signal&) = delete; + + Signal(Signal&&) = default; + + // connects a std::function to the signal. The returned + // value can be used to disconnect the function again + Reg Connect(const std::function& slot) const { + slots_.insert(std::make_pair(++current_id_, slot)); + return Reg(current_id_, [this](Id id){ Disconnect(id);}); + } + + // disconnects a previously connected function + void Disconnect(Id id) const { + slots_.erase(id); + } + + // disconnects all previously connected functions + void DisconnectAll() const { + slots_.clear(); + } + + // calls all connected functions + void operator()(Args... p) { + for(auto it : slots_) { + it.second(p...); + } + } + +private: + mutable std::map> slots_; + mutable Id current_id_; +}; + +} diff --git a/include/pangolin/utils/sigstate.h b/include/pangolin/utils/sigstate.h new file mode 100644 index 0000000..0ea68fd --- /dev/null +++ b/include/pangolin/utils/sigstate.h @@ -0,0 +1,75 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifndef SIGPIPE +# define SIGPIPE 13 +#endif // !SIGPIPE + +namespace pangolin +{ + +typedef void (*SigCallbackFn)(int); + +struct PANGOLIN_EXPORT SigCallback +{ + SigCallback(const int & sig, SigCallbackFn fn, void* data) + : sig(sig), fn(fn), data(data), value(false) + { + std::signal(sig, fn); + } + + int sig; + SigCallbackFn fn; + void * data; + volatile sig_atomic_t value; +}; + +class PANGOLIN_EXPORT SigState +{ +public: + static SigState& I(); + + SigState(); + ~SigState(); + + void Clear(); + + std::map sig_callbacks; +}; + +PANGOLIN_EXPORT +void RegisterNewSigCallback(SigCallbackFn callback, void* data, const int signal); + +} diff --git a/include/pangolin/utils/simple_math.h b/include/pangolin/utils/simple_math.h new file mode 100644 index 0000000..2fd3e66 --- /dev/null +++ b/include/pangolin/utils/simple_math.h @@ -0,0 +1,437 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace pangolin +{ + +static const double Identity3d[] = {1,0,0, 0,1,0, 0,0,1}; +static const double Zero3d[] = {0,0,0, 0,0,0, 0,0,0}; +static const double Identity4d[] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; +static const double Zero4d[] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + +static const float Identity3f[] = {1,0,0, 0,1,0, 0,0,1}; +static const float Zero3f[] = {0,0,0, 0,0,0, 0,0,0}; +static const float Identity4f[] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; +static const float Zero4f[] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + +template +void MatPrint(const P m[R*C]) +{ + for( int r=0; r < R; ++r) + { + for( int c=0; c < C; ++c ) + std::cout << m[R*c+r] << " "; + std::cout << std::endl; + } + std::cout << std::endl; +} + +template +void MatPrint(const P m[R*C], std::string name) +{ + std::cout << name << " = [" << std::endl; + for( int r=0; r < R; ++r) + { + for( int c=0; c < C; ++c ) + std::cout << m[R*c+r] << " "; + std::cout << std::endl; + } + std::cout << std::endl << "]" << std::endl; +} + +// Set array using varadic arguments +template +void MatSet(P m[R*C], ...) +{ + va_list ap; + va_start(ap,m); + for( int i=0; i< R*C; ++i ) + { + *m = (P)va_arg(ap,double); + ++m; + } +} + +// m = zeroes(N) +template +void SetZero(P m[R*C] ) +{ + std::fill_n(m,R*C,0); +} + +// m = identity(N) +template +void SetIdentity(P m[N*N] ) +{ + std::fill_n(m,N*N,0); + for( int i=0; i< N; ++i ) + m[N*i+i] = 1; +} + +// mo = m1 * m2 +template +void MatMul(P mo[R*C], const P m1[R*M], const P m2[M*C] ) +{ + for( int r=0; r < R; ++r) + for( int c=0; c < C; ++c ) + { + mo[R*c+r] = 0; + for( int m=0; m < M; ++ m) mo[R*c+r] += m1[R*m+r] * m2[M*c+m]; + } +} + +// mo = m1 * m2 * s +template +void MatMul(P mo[R*C], const P m1[R*M], const P m2[M*C], P s ) +{ + for( int r=0; r < R; ++r) + for( int c=0; c < C; ++c ) + { + mo[R*c+r] = 0; + for( int m=0; m < M; ++ m) mo[R*c+r] += m1[R*m+r] * m2[M*c+m] * s; + } +} + +// mo = m1 * transpose(m2) +template +void MatMulTranspose(P mo[R*C], const P m1[R*M], const P m2[C*M] ) +{ + for( int r=0; r < R; ++r) + for( int c=0; c < C; ++c ) + { + mo[R*c+r] = 0; + for( int m=0; m < M; ++ m) mo[R*c+r] += m1[R*m+r] * m2[C*m+c]; + } +} + +// m = m1 + m2 +template +void MatAdd(P m[R*C], const P m1[R*C], const P m2[R*C]) +{ + for( int i=0; i< R*C; ++i ) + m[i] = m1[i] + m2[i]; +} + +// m = m1 - m2 +template +void MatSub(P m[R*C], const P m1[R*C], const P m2[R*C]) +{ + for( int i=0; i< R*C; ++i ) + m[i] = m1[i] - m2[i]; +} + +// m = m1 * scalar +template +void MatMul(P m[R*C], const P m1[R*C], P scalar) +{ + for( int i=0; i< R*C; ++i ) + m[i] = m1[i] * scalar; +} + +// m = m1 + m2 +template +void MatMul(P m[R*C], P scalar) +{ + for( int i=0; i< R*C; ++i ) + m[i] *= scalar; +} + +template +void MatTranspose(P out[N*N], const P in[N*N] ) +{ + for( int c=0; c +void MatTranspose(P m[N*N] ) +{ + for( int c=0; c(m[N*c+r],m[N*r+c]); +} + +// s = skewSymetrixMatrix(v) +template +void MatSkew(P s[3*3], const P v[3] ) +{ + s[0] = 0; + s[1] = v[2]; + s[2] = -v[1]; + s[3] = -v[2]; + s[4] = 0; + s[5] = v[0]; + s[6] = v[1]; + s[7] = -v[0]; + s[8] = 0; +} + +template +void MatOrtho( P m[N*N] ) +{ + // http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/index.htm + P Itimes3[N*N]; + SetIdentity(Itimes3); + MatMul(Itimes3,(P)3.0); + + P mmT[N*N]; + MatMulTranspose(mmT,m,m); + + P _c[N*N]; + MatSub(_c,Itimes3,mmT); + P _m[N*N]; + MatMul(_m,m,_c,(P)0.5); + std::copy(_m,_m+(N*N),m); +} + +template +void Rotation(P R[3*3], P x, P y, P z) +{ + P sx = sin(x); + P sy = sin(y); + P sz = sin(z); + P cx = cos(x); + P cy = cos(y); + P cz = cos(z); + // P cx = sqrt( (P)1.0 - sx * sx); + // P cy = sqrt( (P)1.0 - sy * sy); + // P cz = sqrt( (P)1.0 - sz * sz); + R[0] = cy * cz; + R[1] = sx * sy * cz + cx * sz; + R[2] = -cx * sy * cz + sx * sz; + R[3] = -cy * sz; + R[4] = -sx * sy * sz + cx * cz; + R[5] = cx * sy * sz + sx * cz; + R[6] = sy; + R[7] = -sx * cy; + R[8] = cx * cy; +} + +template +void LieSetIdentity(P T_ba[3*4] ) +{ + SetIdentity<3>(T_ba); + std::fill_n(T_ba+(3*3),3,0); +} + +template +void LieSetRotation(P T_ba[3*4], const P R_ba[3*3] ) +{ + std::copy(R_ba,R_ba+(3*3),T_ba); +} + +template +void LieSetTranslation(P T_ba[3*4], const P a_b[3] ) +{ + std::copy(a_b, a_b+3, T_ba+(3*3)); +} + +template +void LieSetSE3(P T_ba[3*4], const P R_ba[3*3], const P a_b[3] ) +{ + LieSetRotation

    (T_ba,R_ba); + LieSetTranslation

    (T_ba,a_b); +} + +template +void LieGetRotation(P R_ba[3*3], const P T_ba[3*4] ) +{ + std::copy(T_ba,T_ba+(3*3),R_ba); +} + +template +void LieApplySO3( P out[3], const P R_ba[3*3], const P in[3] ) +{ + MatMul<3,3,1,P>(out,R_ba,in); +} + +template +void LieApplySE3vec( P x_b[3], const P T_ba[3*4], const P x_a[3] ) +{ + P rot[3]; + MatMul<3,3,1,P>(rot,T_ba,x_a); + MatAdd<3,1,P>(x_b,rot,T_ba+(3*3)); +} + +template +void LieApplySE34x4vec3( P x_b[3], const P T_ba[4*4], const P x_a[3] ) +{ + x_b[0] = T_ba[0]*x_a[0] + T_ba[4]*x_a[1] + T_ba[8]*x_a[2] + T_ba[12]; + x_b[1] = T_ba[1]*x_a[0] + T_ba[5]*x_a[1] + T_ba[9]*x_a[2] + T_ba[13]; + x_b[2] = T_ba[2]*x_a[0] + T_ba[6]*x_a[1] + T_ba[10]*x_a[2] + T_ba[14]; +} + +template +void LieMulSO3( P R_ca[3*3], const P R_cb[3*3], const P R_ba[3*3] ) +{ + MatMul<3,3,3>(R_ca,R_cb,R_ba); +} + +template +void LieMulSE3( P T_ca[3*4], const P T_cb[3*4], const P T_ba[3*4] ) +{ + LieMulSO3<>(T_ca,T_cb,T_ba); + P R_cb_times_a_b[3]; + LieApplySO3<>(R_cb_times_a_b,T_cb,T_ba+(3*3)); + MatAdd<3,1>(T_ca+(3*3),R_cb_times_a_b,T_cb+(3*3)); +} + +template +void LiePutSE3in4x4(P out[4*4], const P in[3*4] ) +{ + SetIdentity<4>(out); + std::copy(in,in+3, out); + std::copy(in+3,in+6, out+4); + std::copy(in+6,in+9, out+8); + std::copy(in+9,in+12, out+12); +} + +template +void LieSE3from4x4(P out[3*4], const P in[4*4] ) +{ + std::copy(in,in+3, out); + std::copy(in+4,in+7, out+3); + std::copy(in+8,in+11, out+6); + std::copy(in+12,in+15, out+9); +} + +template +void LieMul4x4bySE3( P T_ca[4*4], const P T_cb[3*4], const P T_ba[4*4] ) +{ + // TODO: fast + P T_cb4[4*4]; + LiePutSE3in4x4<>(T_cb4,T_cb); + P res[4*4]; + MatMul<4,4,4>(res,T_cb4,T_ba); + std::copy(res,res+(4*4),T_ca); +} + +template +void LieTransposeSO3( P R_ab[3*3], const P R_ba[3*3] ) +{ + MatTranspose<3,P>(R_ab,R_ba); +} + +template +void LieInverseSE3( P T_ab[3*4], const P T_ba[3*4] ) +{ + LieTransposeSO3

    (T_ab,T_ba); + P minus_b_a[3]; + LieApplySO3(minus_b_a, T_ab, T_ba+(3*3)); + MatMul<3,1,P>(T_ab+(3*3),minus_b_a, -1); +} + +// c = a x b +template +void CrossProduct( P c[3], const P a[3], const P b[3] ) +{ + c[0] = a[1] * b[2] - a[2] * b[1]; + c[1] = a[2] * b[0] - a[0] * b[2]; + c[2] = a[0] * b[1] - a[1] * b[0]; +} + +template +P Length( P v[R] ) +{ + P sum_sq = 0; + + for(size_t r = 0; r < R; ++r ) { + sum_sq += v[r] * v[r]; + } + + return sqrt(sum_sq); +} + + +template +void Normalise( P v[R] ) +{ + const P length = Length(v); + + for(size_t r = 0; r < R; ++r ) { + v[r] /= length; + } +} + +template +void EnforceUpT_wc(P T_wc[3*4], const P up_w[3]) +{ + // Copy R_wc + P R_wc[3*3]; + std::copy(T_wc,T_wc+3*3,R_wc); + + // New R_wc should go into T_wc + P* NR_wc = T_wc; + + // // cx_w,cy_w,cz_w are camera axis in world coordinates + // // Calculate new camera coordinates (ncx_w,ncy_w,ncz_w) + + // ncx_w = up_w x cz_w + CrossProduct(NR_wc + 0*3, up_w, R_wc + 2*3); + + // ncy_w = cz_w x ncx_w + CrossProduct(NR_wc + 1*3, R_wc + 2*3, NR_wc + 0*3); + + // ncz_w = cz_w + std::copy(R_wc + 2*3, R_wc + 3*3, NR_wc + 2*3); + + Normalise<3,P>(NR_wc + 0*3); + Normalise<3,P>(NR_wc + 1*3); + Normalise<3,P>(NR_wc + 2*3); +} + +template +void EnforceUpT_cw(P T_cw_4x4[4*4], const P up_w[3]) +{ + // 3x4 from 4x4 + P T_cw[3*4]; + LieSE3from4x4

    (T_cw,T_cw_4x4); + + // Invert T_cw + P T_wc[3*4]; + LieInverseSE3

    (T_wc, T_cw); + + // Enforce up for T_wc + EnforceUpT_wc

    (T_wc, up_w); + + // Invert + LieInverseSE3

    (T_cw, T_wc); + + // 4x4 from 3x4 + LiePutSE3in4x4

    (T_cw_4x4,T_cw); +} + +} diff --git a/include/pangolin/utils/threadedfilebuf.h b/include/pangolin/utils/threadedfilebuf.h new file mode 100644 index 0000000..1673bce --- /dev/null +++ b/include/pangolin/utils/threadedfilebuf.h @@ -0,0 +1,87 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT threadedfilebuf : public std::streambuf +{ +public: + ~threadedfilebuf(); + threadedfilebuf(); + threadedfilebuf(const std::string& filename, size_t buffer_size_bytes); + + void open(const std::string& filename, size_t buffer_size_bytes); + void close(); + void force_close(); + + void operator()(); + +protected: + void soft_close(); + + //! Override streambuf::xsputn for asynchronous write + std::streamsize xsputn(const char * s, std::streamsize n) override; + + //! Override streambuf::overflow for asynchronous write + int overflow(int c) override; + + std::streampos seekoff( + std::streamoff off, std::ios_base::seekdir way, + std::ios_base::openmode which = std::ios_base::in | std::ios_base::out + ) override; + + std::filebuf file; + char* mem_buffer; + std::streamsize mem_size; + std::streamsize mem_max_size; + std::streamsize mem_start; + std::streamsize mem_end; + + std::streampos input_pos; + + std::mutex update_mutex; + std::condition_variable cond_queued; + std::condition_variable cond_dequeued; + std::thread write_thread; + + bool should_run; + bool is_pipe; +}; + +} diff --git a/include/pangolin/utils/timer.h b/include/pangolin/utils/timer.h new file mode 100644 index 0000000..0bb824b --- /dev/null +++ b/include/pangolin/utils/timer.h @@ -0,0 +1,115 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +namespace pangolin +{ + +// These methods exist for backwards compatibility. +// They are deprecated in favour of direct use of std::chrono in C++11 + +using baseclock = std::chrono::system_clock; +using basetime = baseclock::time_point; + +inline basetime TimeNow() +{ + return std::chrono::system_clock::now(); +} + +inline double Time_s(basetime t) +{ + using namespace std::chrono; + return duration_cast( t.time_since_epoch() ).count(); +} + +inline int64_t Time_us(basetime t) +{ + using namespace std::chrono; + return duration_cast( t.time_since_epoch() ).count(); +} + +inline double TimeDiff_s(basetime start, basetime end) +{ + const baseclock::duration d = end - start; + return Time_s( basetime() + d); +} + +inline int64_t TimeDiff_us(basetime start, basetime end) +{ + const baseclock::duration d = end - start; + return Time_us( basetime() + d); +} + +inline basetime TimeAdd(basetime t1, basetime t2) +{ + + return t1 + t2.time_since_epoch(); +} + +inline double TimeNow_s() +{ + return Time_s(TimeNow()); +} + +inline int64_t TimeNow_us() +{ + return Time_us(TimeNow()); +} + +inline basetime WaitUntil(basetime t) +{ + std::this_thread::sleep_until(t); + return TimeNow(); +} + +struct Timer +{ + Timer() { + Reset(); + } + + void Reset() + { + start = TimeNow(); + } + + double Elapsed_s() + { + basetime currtime = TimeNow(); + return TimeDiff_s(start,currtime); + } + + basetime start; +}; + +} diff --git a/include/pangolin/utils/transform.h b/include/pangolin/utils/transform.h new file mode 100644 index 0000000..b29c1e9 --- /dev/null +++ b/include/pangolin/utils/transform.h @@ -0,0 +1,102 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +// Find the open brace preceeded by '$' +inline const char* FirstOpenBrace(const char* str, char token = '$', char open = '{') +{ + bool symbol = false; + + for(; *str != '\0'; ++str ) { + if( *str == token) { + symbol = true; + }else{ + if( symbol ) { + if( *str == open ) { + return str; + } else { + symbol = false; + } + } + } + } + return 0; +} + +// Find the first matching end brace. str includes open brace +inline const char* MatchingEndBrace(const char* str, char open = '{', char close = '}') +{ + int b = 0; + for(; *str != '\0'; ++str ) { + if( *str == open ) { + ++b; + }else if( *str == close ) { + --b; + if( b == 0 ) { + return str; + } + } + } + return 0; +} + +inline std::string Transform(const std::string& val, std::function dictionary, char token = '$', char open = '{', char close = '}') +{ + std::string expanded = val; + + while(true) + { + const char* brace = FirstOpenBrace(expanded.c_str(), token, open); + if(brace) + { + const char* endbrace = MatchingEndBrace(brace); + if( endbrace ) + { + std::ostringstream oss; + oss << std::string(expanded.c_str(), brace-1); + + const std::string inexpand = Transform( std::string(brace+1,endbrace), dictionary, token, open, close ); + oss << dictionary(inexpand); + oss << std::string(endbrace+1, expanded.c_str() + expanded.length() ); + expanded = oss.str(); + continue; + } + } + break; + } + + return expanded; +} + +} diff --git a/include/pangolin/utils/type_convert.h b/include/pangolin/utils/type_convert.h new file mode 100644 index 0000000..93d026f --- /dev/null +++ b/include/pangolin/utils/type_convert.h @@ -0,0 +1,210 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin +{ +struct BadInputException : std::exception { + char const* what() const throw() { return "Failed to serialise type"; } +}; +} + +namespace std +{ +// Dummy methods to serialise functions / functors / lambdas etc +#ifdef CALLEE_HAS_VARIADIC_TEMPLATES +template +inline std::istream& operator>>(std::istream& /*is*/, std::function& /*f*/) { + throw pangolin::BadInputException(); +} +template +inline std::ostream& operator<<(std::ostream& /*os*/, const std::function& /*f*/) { + throw pangolin::BadInputException(); +} +#else +template +inline std::istream& operator>>(std::istream& /*is*/, std::function& /*f*/) { + throw pangolin::BadInputException(); +} +template +inline std::ostream& operator<<(std::ostream& /*os*/, const std::function& /*f*/) { + throw pangolin::BadInputException(); +} +inline std::istream& operator>>(std::istream& /*is*/, std::function& /*f*/) { + throw pangolin::BadInputException(); +} +inline std::ostream& operator<<(std::ostream& /*os*/, const std::function& /*f*/) { + throw pangolin::BadInputException(); +} +#endif +} + +namespace pangolin +{ + +template +struct Convert; + +// Generic conversion through serialisation from / to string +template +struct Convert { + static T Do(const S& src) + { + std::ostringstream oss; + oss << src; + std::istringstream iss(oss.str()); + T target; + iss >> target; + + if(iss.fail()) + throw BadInputException(); + + return target; + } +}; + +// Between the same types is just a copy +template +struct Convert { + static T Do(const T& src) + { + return src; + } +}; + +// Apply bool alpha IO manipulator for bool types +template<> +struct Convert { + static bool Do(const std::string& src) + { + bool target; + std::istringstream iss(src); + iss >> target; + + if(iss.fail()) + { + std::istringstream iss2(src); + iss2 >> std::boolalpha >> target; + if( iss2.fail()) + throw BadInputException(); + } + + return target; + } +}; + +// From strings +template +struct Convert::value + >::type > { + static T Do(const std::string& src) + { + T target; + std::istringstream iss(src); + iss >> target; + + if(iss.fail()) + throw BadInputException(); + + return target; + } +}; + +// To strings +template +struct Convert::value + >::type > { + static std::string Do(const S& src) + { + std::ostringstream oss; + oss << src; + return oss.str(); + } +}; + +// Between scalars +template +struct Convert::value && !std::is_same::value && + std::is_scalar::value && !std::is_same::value && + !std::is_same::value + >::type > { + static T Do(const S& src) + { + return static_cast(src); + } +}; + +// From Scalars to bool (different than scalar definition to avoid MSVC Warnings) +template +struct Convert::value && + std::is_scalar::value && + !std::is_same::value +>::type > { + static T Do(const S& src) + { + return src != static_cast(0); + } +}; + +// From bool to Scalars (different than scalar definition to avoid MSVC Warnings) +template +struct Convert::value && + std::is_same::value && + !std::is_same::value +>::type > { + static T Do(const S& src) + { + return src ? static_cast(0) : static_cast(1); + } +}; + +template +std::string ToString(const S& src) +{ + return Convert::Do(src); +} + +template +T FromString(const std::string& src) +{ + return Convert::Do(src); +} + + +} diff --git a/include/pangolin/utils/uri.h b/include/pangolin/utils/uri.h new file mode 100644 index 0000000..0ea376e --- /dev/null +++ b/include/pangolin/utils/uri.h @@ -0,0 +1,49 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT Uri : public Params +{ +public: + std::string scheme; + std::string url; + std::string full_uri; +}; + +//! Parse string as Video URI +PANGOLIN_EXPORT +Uri ParseUri(const std::string& str_uri); + +} diff --git a/include/pangolin/utils/xml/license.txt b/include/pangolin/utils/xml/license.txt new file mode 100755 index 0000000..0095bc7 --- /dev/null +++ b/include/pangolin/utils/xml/license.txt @@ -0,0 +1,52 @@ +Use of this software is granted under one of the following two licenses, +to be chosen freely by the user. + +1. Boost Software License - Version 1.0 - August 17th, 2003 +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +2. The MIT License +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/include/pangolin/utils/xml/rapidxml.hpp b/include/pangolin/utils/xml/rapidxml.hpp new file mode 100755 index 0000000..68fa052 --- /dev/null +++ b/include/pangolin/utils/xml/rapidxml.hpp @@ -0,0 +1,2635 @@ +#ifndef RAPIDXML_HPP_INCLUDED +#define RAPIDXML_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new + #include // for conversion between types +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

    + //! This function cannot return. If it does, the results are undefined. + //!

    + //! A very simple definition might look like that: + //!

    +    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
    +    //! {
    +    //!     std::cout << "Parse error: " << what << "\n";
    +    //!     std::abort();
    +    //! }
    +    //! 
    + //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} + +#else + +#include // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

    + //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

    + //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

    + //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

    + //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
      + //!
    • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
    • + //!
    • entities will not be translated
    • + //!
    • whitespace will not be normalized
    • + //!
    + //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

    + //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

    + //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Command option default defaults + + //! Default case sensitivity when searching for node / attribute names. + const bool default_case_sensitivity = false; + + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

    + //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

    + //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

    + //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

    + //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

    + //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
    (align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

    + //! User defined allocation functions must have the following forms: + //!
    + //!
    void *allocate(std::size_t size); + //!
    void free(void *pointer); + //!

    + //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
    (pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

    + //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

    + //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + //! Gets value of node converted to type T. + //! \return Value of node converted to type T or default_val if no value exists. + //! \throws parse_error iff conversion to type T fails. + template + T valueT(T default_val = T() ) + { + if(m_value) { + std::istringstream ss( std::string(m_value, m_value_size) ); + T ret_val; + ss >> ret_val; + if(ss.fail()) { + RAPIDXML_PARSE_ERROR("error converting value in valueT()", m_value); + } + return ret_val; + } + return default_val; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

    + //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

    + //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

    + //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

    + //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

    + //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

    + //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(0) + , m_first_attribute(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = default_case_sensitivity) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + //! Gets first attribute of node, optionally matching attribute name, converting to type T + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param default_val value to return if attribute is not found or if it has no value + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Converted value of attribute, or default_val if attribute not found or it has no value + template + T first_attribute_value(const Ch *name = 0, std::size_t name_size = 0, T default_val = T(), bool case_sensitive = default_case_sensitivity ) + { + rapidxml::xml_attribute<>* a = first_attribute( name, name_size, case_sensitive ); + return a ? a->valueT(default_val) : default_val; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

    + //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

    + //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); + + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/include/pangolin/utils/xml/rapidxml_iterators.hpp b/include/pangolin/utils/xml/rapidxml_iterators.hpp new file mode 100755 index 0000000..85c5894 --- /dev/null +++ b/include/pangolin/utils/xml/rapidxml_iterators.hpp @@ -0,0 +1,174 @@ +#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED +#define RAPIDXML_ITERATORS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_iterators.hpp This file contains rapidxml iterators + +#include "rapidxml.hpp" + +namespace rapidxml +{ + + //! Iterator of child nodes of xml_node + template + class node_iterator + { + + public: + + typedef typename xml_node value_type; + typedef typename xml_node &reference; + typedef typename xml_node *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + node_iterator() + : m_node(0) + { + } + + node_iterator(xml_node *node) + : m_node(node->first_node()) + { + } + + reference operator *() const + { + assert(m_node); + return *m_node; + } + + pointer operator->() const + { + assert(m_node); + return m_node; + } + + node_iterator& operator++() + { + assert(m_node); + m_node = m_node->next_sibling(); + return *this; + } + + node_iterator operator++(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + node_iterator& operator--() + { + assert(m_node && m_node->previous_sibling()); + m_node = m_node->previous_sibling(); + return *this; + } + + node_iterator operator--(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const node_iterator &rhs) + { + return m_node == rhs.m_node; + } + + bool operator !=(const node_iterator &rhs) + { + return m_node != rhs.m_node; + } + + private: + + xml_node *m_node; + + }; + + //! Iterator of child attributes of xml_node + template + class attribute_iterator + { + + public: + + typedef typename xml_attribute value_type; + typedef typename xml_attribute &reference; + typedef typename xml_attribute *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + attribute_iterator() + : m_attribute(0) + { + } + + attribute_iterator(xml_node *node) + : m_attribute(node->first_attribute()) + { + } + + reference operator *() const + { + assert(m_attribute); + return *m_attribute; + } + + pointer operator->() const + { + assert(m_attribute); + return m_attribute; + } + + attribute_iterator& operator++() + { + assert(m_attribute); + m_attribute = m_attribute->next_attribute(); + return *this; + } + + attribute_iterator operator++(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + attribute_iterator& operator--() + { + assert(m_attribute && m_attribute->previous_attribute()); + m_attribute = m_attribute->previous_attribute(); + return *this; + } + + attribute_iterator operator--(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const attribute_iterator &rhs) + { + return m_attribute == rhs.m_attribute; + } + + bool operator !=(const attribute_iterator &rhs) + { + return m_attribute != rhs.m_attribute; + } + + private: + + xml_attribute *m_attribute; + + }; + +} + +#endif diff --git a/include/pangolin/utils/xml/rapidxml_print.hpp b/include/pangolin/utils/xml/rapidxml_print.hpp new file mode 100755 index 0000000..d03d5f5 --- /dev/null +++ b/include/pangolin/utils/xml/rapidxml_print.hpp @@ -0,0 +1,421 @@ +#ifndef RAPIDXML_PRINT_HPP_INCLUDED +#define RAPIDXML_PRINT_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_print.hpp This file contains rapidxml printer implementation + +#include "rapidxml.hpp" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS + #include + #include +#endif + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + + // Return modified iterator + return out; + } + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} + +#endif diff --git a/include/pangolin/utils/xml/rapidxml_utils.hpp b/include/pangolin/utils/xml/rapidxml_utils.hpp new file mode 100755 index 0000000..f2824bf --- /dev/null +++ b/include/pangolin/utils/xml/rapidxml_utils.hpp @@ -0,0 +1,122 @@ +#ifndef RAPIDXML_UTILS_HPP_INCLUDED +#define RAPIDXML_UTILS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful +//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. + +#include "rapidxml.hpp" +#include +#include +#include +#include + +namespace rapidxml +{ + + //! Represents data loaded from a file + template + class file + { + + public: + + //! Loads file into the memory. Data will be automatically destroyed by the destructor. + //! \param filename Filename to load. + file(const char *filename) + { + using namespace std; + + // Open stream + basic_ifstream stream(filename, ios::binary); + if (!stream) + throw runtime_error(string("cannot open file ") + filename); + stream.unsetf(ios::skipws); + + // Determine stream size + stream.seekg(0, ios::end); + size_t size = (size_t)stream.tellg(); + stream.seekg(0); + + // Load data and add terminating 0 + m_data.resize(size + 1); + stream.read(&m_data.front(), static_cast(size)); + m_data[size] = 0; + } + + //! Loads file into the memory. Data will be automatically destroyed by the destructor + //! \param stream Stream to load from + file(std::basic_istream &stream) + { + using namespace std; + + // Load data and add terminating 0 + stream.unsetf(ios::skipws); + m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); + if (stream.fail() || stream.bad()) + throw runtime_error("error reading stream"); + m_data.push_back(0); + } + + //! Gets file data. + //! \return Pointer to data of file. + Ch *data() + { + return &m_data.front(); + } + + //! Gets file data. + //! \return Pointer to data of file. + const Ch *data() const + { + return &m_data.front(); + } + + //! Gets file data size. + //! \return Size of file data, in characters. + std::size_t size() const + { + return m_data.size(); + } + + private: + + std::vector m_data; // File data + + }; + + //! Counts children of node. Time complexity is O(n). + //! \return Number of children of node + template + inline std::size_t count_children(xml_node *node) + { + xml_node *child = node->first_node(); + std::size_t count = 0; + while (child) + { + ++count; + child = child->next_sibling(); + } + return count; + } + + //! Counts attributes of node. Time complexity is O(n). + //! \return Number of attributes of node + template + inline std::size_t count_attributes(xml_node *node) + { + xml_attribute *attr = node->first_attribute(); + std::size_t count = 0; + while (attr) + { + ++count; + attr = attr->next_attribute(); + } + return count; + } + +} + +#endif diff --git a/include/pangolin/var/input_record_repeat.h b/include/pangolin/var/input_record_repeat.h new file mode 100644 index 0000000..b8df0c5 --- /dev/null +++ b/include/pangolin/var/input_record_repeat.h @@ -0,0 +1,86 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace pangolin +{ + +struct FrameInput +{ + int index; + std::string var; + std::string val; +}; + +struct PANGOLIN_EXPORT InputRecordRepeat +{ + InputRecordRepeat(const std::string& var_record_prefix); + ~InputRecordRepeat(); + + void SetIndex(int id); + + void Record(); + void Stop(); + + void LoadBuffer(const std::string& filename); + void SaveBuffer(const std::string& filename); + void ClearBuffer(); + + void PlayBuffer(); + void PlayBuffer(size_t start, size_t end); + + void UpdateVariable(const std::string& name ); + + template + inline void UpdateVariable(const Var& var ) { + GuiVarChanged((void*)this, var.var->Meta().full_name, *var.var); + } + + size_t Size(); + +protected: + bool record; + bool play; + + int index; + std::ofstream file; + std::string filename; + + std::list play_queue; + std::list record_queue; + + static void GuiVarChanged(void* data, const std::string& name, VarValueGeneric& var); +}; + +} diff --git a/include/pangolin/var/var.h b/include/pangolin/var/var.h new file mode 100644 index 0000000..b5088f2 --- /dev/null +++ b/include/pangolin/var/var.h @@ -0,0 +1,328 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace pangolin +{ + +template +inline void InitialiseNewVarMetaGeneric( + VarValue& v, const std::string& name +) { + // Initialise meta parameters + const std::vector parts = pangolin::Split(name,'.'); + v.Meta().full_name = name; + v.Meta().friendly = parts.size() > 0 ? parts[parts.size()-1] : ""; + v.Meta().range[0] = 0.0; + v.Meta().range[1] = 0.0; + v.Meta().increment = 0.0; + v.Meta().flags = 0; + v.Meta().logscale = false; + v.Meta().generic = true; + + VarState::I().NotifyNewVar(name, v); +} + +template +inline void InitialiseNewVarMeta( + VarValue& v, const std::string& name, + double min = 0, double max = 0, int flags = 1, bool logscale = false +) { + // Initialise meta parameters + const std::vector parts = pangolin::Split(name,'.'); + v.Meta().full_name = name; + v.Meta().friendly = parts.size() > 0 ? parts[parts.size()-1] : ""; + v.Meta().range[0] = min; + v.Meta().range[1] = max; + if (std::is_integral::value) { + v.Meta().increment = 1.0; + } else { + v.Meta().increment = (max - min) / 100.0; + } + v.Meta().flags = flags; + v.Meta().logscale = logscale; + v.Meta().generic = false; + + VarState::I().NotifyNewVar(name, v); +} + +template +class Var +{ +public: + static T& Attach( + const std::string& name, T& variable, + double min, double max, bool logscale = false + ) { + // Find name in VarStore + VarValueGeneric*& v = VarState::I()[name]; + //std::shared_ptr& v = VarState::I()[name]; + if(v) { + throw std::runtime_error("Var with that name already exists."); + }else{ + // new VarRef (owned by VarStore) + VarValue* nv = new VarValue(variable); + v = nv; + InitialiseNewVarMeta(*nv,name,min,max,1,logscale); + } + return variable; + } + + static T& Attach( + const std::string& name, T& variable, bool toggle = false + ) { + // Find name in VarStore + VarValueGeneric*& v = VarState::I()[name]; + //std::shared_ptr& v = VarState::I()[name]; + if (v) { + throw std::runtime_error("Var with that name already exists."); + } + else{ + // new VarRef (owned by VarStore) + VarValue* nv = new VarValue(variable); + v = nv; + InitialiseNewVarMeta(*nv, name, 0.0, 0.0, toggle); + } + return variable; + } + + ~Var() + { + delete ptr; + } + + Var( VarValueGeneric& v ) + : ptr(0) + { + InitialiseFromGeneric(&v); + } + + + Var( const std::string& name ) + : ptr(0) + { + // Find name in VarStore + VarValueGeneric*& v = VarState::I()[name]; + //std::shared_ptr& v = VarState::I()[name]; + if(v && !v->Meta().generic) { + InitialiseFromGeneric(v); + }else{ + // new VarValue (owned by VarStore) + VarValue* nv; + if(v) { + // Specialise generic variable + nv = new VarValue( Convert::Do( v->str->Get() ) ); + delete v; + }else{ + nv = new VarValue( T() ); + } + v = nv; + var = nv; + InitialiseNewVarMeta(*nv, name); + } + } + + Var( const std::string& name, const T& value, bool toggle = false ) + : ptr(0) + { + // Find name in VarStore + VarValueGeneric*& v = VarState::I()[name]; + //std::shared_ptr& v = VarState::I()[name]; + if(v && !v->Meta().generic) { + InitialiseFromGeneric(v); + }else{ + // new VarValue (owned by VarStore) + VarValue* nv; + if(v) { + // Specialise generic variable + nv = new VarValue( Convert::Do( v->str->Get() ) ); + delete v; + }else{ + nv = new VarValue(value); + } + v = nv; + var = nv; + InitialiseNewVarMeta(*nv, name, 0, 1, toggle); + } + } + + Var( + const std::string& name, const T& value, + double min, double max, bool logscale = false + ) : ptr(0) + { + // Find name in VarStore + VarValueGeneric*& v = VarState::I()[name]; + //std::shared_ptr& v = VarState::I()[name]; + if(v && !v->Meta().generic) { + InitialiseFromGeneric(v); + }else{ + // new VarValue (owned by VarStore) + VarValue* nv; + if(v) { + // Specialise generic variable + nv = new VarValue( Convert::Do( v->str->Get() ) ); + delete v; + }else{ + nv = new VarValue(value); + } + var = nv; + v = nv; + if(logscale) { + if (min <= 0 || max <= 0) { + throw std::runtime_error("LogScale: range of numbers must be positive!"); + } + InitialiseNewVarMeta(*nv, name, std::log(min), std::log(max), 1, true); + }else{ + InitialiseNewVarMeta(*nv, name, min, max); + } + } + } + + void Reset() + { + var->Reset(); + } + + const T& Get() const + { + try{ + return var->Get(); + }catch(BadInputException) + { + const_cast *>(this)->Reset(); + return var->Get(); + } + } + + operator const T& () const + { + return Get(); + } + + const T* operator->() + { + try{ + return &(var->Get()); + }catch(BadInputException) + { + Reset(); + return &(var->Get()); + } + } + + void operator=(const T& val) + { + var->Set(val); + } + + void operator=(const Var& v) + { + var->Set(v.var->Get()); + } + + VarMeta& Meta() + { + return var->Meta(); + } + + bool GuiChanged() + { + if(var->Meta().gui_changed) { + var->Meta().gui_changed = false; + return true; + } + return false; + } + + VarValueT& Ref() + { + return *var; + } + +protected: + // Initialise from existing variable, obtain data / accessor + void InitialiseFromGeneric(VarValueGeneric* v) + //void InitialiseFromGeneric(std::shared_ptr v) + { + if( !strcmp(v->TypeId(), typeid(T).name()) ) { + // Same type + var = (VarValueT*)(v); + }else if( std::is_same::value ) { + // Use types string accessor + var = (VarValueT*)(v->str); + }else if( !strcmp(v->TypeId(), typeid(bool).name() ) ) { + // Wrapper, owned by this object + ptr = new VarWrapper( *(VarValueT*)v ); + var = ptr; + }else if( !strcmp(v->TypeId(), typeid(short).name() ) ) { + // Wrapper, owned by this object + ptr = new VarWrapper( *(VarValueT*)v ); + var = ptr; + }else if( !strcmp(v->TypeId(), typeid(int).name() ) ) { + // Wrapper, owned by this object + ptr = new VarWrapper( *(VarValueT*)v ); + var = ptr; + }else if( !strcmp(v->TypeId(), typeid(long).name() ) ) { + // Wrapper, owned by this object + ptr = new VarWrapper( *(VarValueT*)v ); + var = ptr; + }else if( !strcmp(v->TypeId(), typeid(float).name() ) ) { + // Wrapper, owned by this object + ptr = new VarWrapper( *(VarValueT*)v ); + var = ptr; + }else if( !strcmp(v->TypeId(), typeid(double).name() ) ) { + // Wrapper, owned by this object + ptr = new VarWrapper( *(VarValueT*)v ); + var = ptr; + }else{ + // other types: have to go via string + // Wrapper, owned by this object + ptr = new VarWrapper( *(v->str) ); + var = ptr; + } + } + + // Holds reference to stored variable object + // N.B. mutable because it is a cached value and Get() is advertised as const. + mutable VarValueT* var; + + // ptr is non-zero if this object owns the object variable (a wrapper) + VarValueT* ptr; +}; + +} diff --git a/include/pangolin/var/varextra.h b/include/pangolin/var/varextra.h new file mode 100644 index 0000000..6388262 --- /dev/null +++ b/include/pangolin/var/varextra.h @@ -0,0 +1,92 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace pangolin +{ + +PANGOLIN_EXPORT +void ParseVarsFile(const std::string& filename); + +PANGOLIN_EXPORT +void LoadJsonFile(const std::string& filename, const std::string& prefix=""); + +PANGOLIN_EXPORT +void SaveJsonFile(const std::string& filename, const std::string& prefix=""); + +PANGOLIN_EXPORT +void ProcessHistoricCallbacks(NewVarCallbackFn callback, void* data, const std::string& filter = ""); + +PANGOLIN_EXPORT +void RegisterNewVarCallback(NewVarCallbackFn callback, void* data, const std::string& filter = ""); + +PANGOLIN_EXPORT +void RegisterGuiVarChangedCallback(GuiVarChangedCallbackFn callback, void* data, const std::string& filter = ""); + +template +struct SetVarFunctor +{ + SetVarFunctor(const std::string& name, T val) : varName(name), setVal(val) {} + void operator()() { Var(varName).Ref().Set(setVal); } + std::string varName; + T setVal; +}; + +struct ToggleVarFunctor +{ + ToggleVarFunctor(const std::string& name) : varName(name) {} + void operator()() { Var val(varName,false); val = !val; } + std::string varName; +}; + +inline bool Pushed(Var& button) +{ + bool val = button; + button = false; + return val; +} + +inline bool Pushed(bool& button) +{ + bool val = button; + button = false; + return val; +} + +template +inline std::ostream& operator<<(std::ostream& s, Var& rhs) +{ + s << rhs.operator const T &(); + return s; +} + +} diff --git a/include/pangolin/var/varstate.h b/include/pangolin/var/varstate.h new file mode 100644 index 0000000..8072b23 --- /dev/null +++ b/include/pangolin/var/varstate.h @@ -0,0 +1,133 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace pangolin +{ + +typedef void (*NewVarCallbackFn)(void* data, const std::string& name, VarValueGeneric& var, bool brand_new); +typedef void (*GuiVarChangedCallbackFn)(void* data, const std::string& name, VarValueGeneric& var); + +struct PANGOLIN_EXPORT NewVarCallback +{ + NewVarCallback(const std::string& filter, NewVarCallbackFn fn, void* data) + :filter(filter),fn(fn),data(data) {} + std::string filter; + NewVarCallbackFn fn; + void* data; +}; + +struct PANGOLIN_EXPORT GuiVarChangedCallback +{ + GuiVarChangedCallback(const std::string& filter, GuiVarChangedCallbackFn fn, void* data) + :filter(filter),fn(fn),data(data) {} + std::string filter; + GuiVarChangedCallbackFn fn; + void* data; +}; + +class PANGOLIN_EXPORT VarState +{ +public: + static VarState& I(); + + VarState(); + ~VarState(); + + void Clear(); + + template + void NotifyNewVar(const std::string& name, VarValue& var ) + { + var_adds.push_back(name); + + // notify those watching new variables + for(std::vector::iterator invc = new_var_callbacks.begin(); invc != new_var_callbacks.end(); ++invc) { + if( StartsWith(name,invc->filter) ) { + invc->fn( invc->data, name, var, true); + } + } + } + + VarValueGeneric*& operator[](const std::string& str) + //std::shared_ptr& operator[](const std::string& str) + { + VarStoreContainer::iterator it = vars.find(str); + if (it == vars.end()) { + vars[str] = nullptr; + } + return vars[str]; + } + + bool Exists(const std::string& str) const + { + return vars.find(str) != vars.end(); + } + + void FlagVarChanged() + { + varHasChanged = true; + } + + bool VarHasChanged() + { + const bool has_changed = varHasChanged; + varHasChanged = false; + return has_changed; + } + +//protected: + typedef std::map VarStoreContainer; + //typedef std::map> VarStoreContainer; + typedef std::vector VarStoreAdditions; + + VarStoreContainer vars; + VarStoreAdditions var_adds; + + std::vector new_var_callbacks; + std::vector gui_var_changed_callbacks; + + bool varHasChanged; +}; + +inline bool GuiVarHasChanged() { + return VarState::I().VarHasChanged(); +} + +inline void FlagVarChanged() { + VarState::I().FlagVarChanged(); +} + +} diff --git a/include/pangolin/var/varvalue.h b/include/pangolin/var/varvalue.h new file mode 100644 index 0000000..ee3193d --- /dev/null +++ b/include/pangolin/var/varvalue.h @@ -0,0 +1,114 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +template +class VarValue : public VarValueT::type> +{ +public: + typedef typename std::remove_reference::type VarT; + + ~VarValue() + { + delete str_ptr; + } + + VarValue() + { + Init(); + } + + VarValue(const T& value) + : value(value), default_value(value) + { + Init(); + } + + VarValue(const T& value, const VarT& default_value) + : value(value), default_value(default_value) + { + Init(); + } + + const char* TypeId() const + { + return typeid(VarT).name(); + } + + void Reset() + { + value = default_value; + } + + VarMeta& Meta() + { + return meta; + } + + const VarT& Get() const + { + return value; + } + + VarT& Get() + { + return value; + } + + void Set(const VarT& val) + { + value = val; + } + +protected: + void Init() + { + if(std::is_same::value) { + str_ptr = 0; + this->str = (VarValueT*)this; + }else{ + str_ptr = new VarWrapper(*this); + this->str = str_ptr; + } + } + + // If non-zero, this class owns this str pointer in the base-class. + VarValueT* str_ptr; + + T value; + VarT default_value; + VarMeta meta; +}; + +} diff --git a/include/pangolin/var/varvaluegeneric.h b/include/pangolin/var/varvaluegeneric.h new file mode 100644 index 0000000..01356c9 --- /dev/null +++ b/include/pangolin/var/varvaluegeneric.h @@ -0,0 +1,84 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin +{ + +struct VarMeta +{ + VarMeta() : + increment(0.), + flags(0), + gui_changed(false), + logscale(false), + generic(false) + { + range[0] = 0.; + range[1] = 0.; + } + + std::string full_name; + std::string friendly; + double range[2]; + double increment; + int flags; + bool gui_changed; + bool logscale; + bool generic; +}; + +// Forward declaration +template +class VarValueT; + +//! Abstract base class for named Pangolin variables +class VarValueGeneric +{ +public: + VarValueGeneric() + : str(0) + { + } + + virtual ~VarValueGeneric() + { + } + + virtual const char* TypeId() const = 0; + virtual void Reset() = 0; + virtual VarMeta& Meta() = 0; + +//protected: + // String serialisation object. + VarValueT* str; +}; + +} diff --git a/include/pangolin/var/varvaluet.h b/include/pangolin/var/varvaluet.h new file mode 100644 index 0000000..5b7a45c --- /dev/null +++ b/include/pangolin/var/varvaluet.h @@ -0,0 +1,46 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +template +class VarValueT : public VarValueGeneric +{ +public: + typedef typename std::remove_reference::type VarT; + + virtual const VarT& Get() const = 0; + virtual void Set(const VarT& val) = 0; +}; + +} diff --git a/include/pangolin/var/varwrapper.h b/include/pangolin/var/varwrapper.h new file mode 100644 index 0000000..579da13 --- /dev/null +++ b/include/pangolin/var/varwrapper.h @@ -0,0 +1,91 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace pangolin +{ + +template +class VarWrapper : public VarValueT +{ +public: + typedef typename std::remove_reference::type VarS; + + VarWrapper(VarValueT& src) + : src(src) + { + this->str = src.str; + } + + const char* TypeId() const + { + return typeid(T).name(); + } + + void Reset() + { + src.Reset(); + + // If reset throws, it will go up to the user, since there is nothing + // more we can do + cache = Convert::Do(src.Get()); + } + + VarMeta& Meta() + { + return src.Meta(); + } + + const T& Get() const + { + // This might throw, but we can't reset because this is a const method + cache = Convert::Do(src.Get()); + return cache; + } + + void Set(const T& val) + { + cache = val; + try { + src.Set( Convert::Do(val) ); + }catch(BadInputException) { + pango_print_warn("Unable to set variable with type %s from %s. Resetting.", typeid(VarS).name(), typeid(T).name() ); + Reset(); + } + } + +protected: + mutable T cache; + VarValueT& src; +}; + +} diff --git a/include/pangolin/video/drivers/debayer.h b/include/pangolin/video/drivers/debayer.h new file mode 100644 index 0000000..4d4d9bf --- /dev/null +++ b/include/pangolin/video/drivers/debayer.h @@ -0,0 +1,116 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +// Enum to match libdc1394's dc1394_bayer_method_t +typedef enum { + BAYER_METHOD_NEAREST = 0, + BAYER_METHOD_SIMPLE, + BAYER_METHOD_BILINEAR, + BAYER_METHOD_HQLINEAR, + BAYER_METHOD_DOWNSAMPLE_, + BAYER_METHOD_EDGESENSE, + BAYER_METHOD_VNG, + BAYER_METHOD_AHD, + + // Pangolin custom defines + BAYER_METHOD_NONE = 512, + BAYER_METHOD_DOWNSAMPLE, + BAYER_METHOD_DOWNSAMPLE_MONO +} bayer_method_t; + +// Enum to match libdc1394's dc1394_color_filter_t +typedef enum { + DC1394_COLOR_FILTER_RGGB = 512, + DC1394_COLOR_FILTER_GBRG, + DC1394_COLOR_FILTER_GRBG, + DC1394_COLOR_FILTER_BGGR +} color_filter_t; + +// Video class that debayers its video input using the given method. +class PANGOLIN_EXPORT DebayerVideo : + public VideoInterface, + public VideoFilterInterface, + public BufferAwareVideoInterface +{ +public: + DebayerVideo(std::unique_ptr& videoin, const std::vector &method, color_filter_t tile); + ~DebayerVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + std::vector& InputStreams(); + + static color_filter_t ColorFilterFromString(std::string str); + + static bayer_method_t BayerMethodFromString(std::string str); + + uint32_t AvailableFrames() const; + + bool DropNFrames(uint32_t n); + +protected: + void ProcessStreams(unsigned char* out, const unsigned char* in); + + std::unique_ptr src; + std::vector videoin; + std::vector streams; + + size_t size_bytes; + std::unique_ptr buffer; + + std::vector methods; + color_filter_t tile; + + picojson::value device_properties; + picojson::value frame_properties; +}; + +} diff --git a/include/pangolin/video/drivers/deinterlace.h b/include/pangolin/video/drivers/deinterlace.h new file mode 100644 index 0000000..4098ef3 --- /dev/null +++ b/include/pangolin/video/drivers/deinterlace.h @@ -0,0 +1,62 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT DeinterlaceVideo + : public VideoInterface +{ +public: + DeinterlaceVideo(std::unique_ptr& videoin); + ~DeinterlaceVideo(); + + size_t SizeBytes() const; + + const std::vector& Streams() const; + + void Start(); + + void Stop(); + + bool GrabNext( unsigned char* image, bool wait = true ); + + bool GrabNewest( unsigned char* image, bool wait = true ); + +protected: + std::unique_ptr videoin; + std::vector streams; + unsigned char* buffer; +}; + + +} diff --git a/include/pangolin/video/drivers/depthsense.h b/include/pangolin/video/drivers/depthsense.h new file mode 100644 index 0000000..a0d8626 --- /dev/null +++ b/include/pangolin/video/drivers/depthsense.h @@ -0,0 +1,170 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +// DepthSense SDK for SoftKinetic cameras from Creative +#include + +namespace pangolin +{ + +enum DepthSenseSensorType +{ + DepthSenseUnassigned = -1, + DepthSenseRgb = 0, + DepthSenseDepth +}; + +// Video class that outputs test video signal. +class PANGOLIN_EXPORT DepthSenseVideo : + public VideoInterface, public VideoPropertiesInterface +{ +public: + DepthSenseVideo(DepthSense::Device device, DepthSenseSensorType s1, DepthSenseSensorType s2, ImageDim dim1, ImageDim dim2, unsigned int fps1, unsigned int fps2, const Uri& uri); + ~DepthSenseVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::DeviceProperties() + const picojson::value& DeviceProperties() const { + return device_properties; + } + + //! Implement VideoInput::DeviceProperties() + const picojson::value& FrameProperties() const { + return frame_properties; + } +protected: + void onNewColorSample(DepthSense::ColorNode node, DepthSense::ColorNode::NewSampleReceivedData data); + void onNewDepthSample(DepthSense::DepthNode node, DepthSense::DepthNode::NewSampleReceivedData data); + + struct SensorConfig + { + DepthSenseSensorType type; + ImageDim dim; + unsigned int fps; + }; + + void UpdateParameters(const DepthSense::Node& node, const Uri& uri); + void ConfigureNodes(const Uri& uri); + void ConfigureDepthNode(const SensorConfig& sensorConfig, const Uri& uri); + void ConfigureColorNode(const SensorConfig& sensorConfig, const Uri& uri); + + double GetDeltaTime() const; + + std::vector streams; + picojson::value device_properties; + picojson::value frame_properties; + picojson::value* streams_properties; + + + DepthSense::Device device; + DepthSense::DepthNode g_dnode; + DepthSense::ColorNode g_cnode; + + unsigned char* fill_image; + + int depthmap_stream; + int rgb_stream; + + int gotDepth; + int gotColor; + std::mutex update_mutex; + std::condition_variable cond_image_filled; + std::condition_variable cond_image_requested; + + SensorConfig sensorConfig[2]; + + bool enableDepth; + bool enableColor; + double depthTs; + double colorTs; + + size_t size_bytes; +}; + +class DepthSenseContext +{ + friend class DepthSenseVideo; + +public: + // Singleton Instance + static DepthSenseContext& I(); + + DepthSenseVideo* GetDepthSenseVideo(size_t device_num, DepthSenseSensorType s1, DepthSenseSensorType s2, ImageDim dim1, ImageDim dim2, unsigned int fps1, unsigned int fps2, const Uri& uri); + +protected: + // Protected Constructor + DepthSenseContext(); + ~DepthSenseContext(); + + DepthSense::Context& Context(); + + void NewDeviceRunning(); + void DeviceClosing(); + + void StartNodes(); + void StopNodes(); + + void EventLoop(); + + void onDeviceConnected(DepthSense::Context context, DepthSense::Context::DeviceAddedData data); + void onDeviceDisconnected(DepthSense::Context context, DepthSense::Context::DeviceRemovedData data); + + DepthSense::Context g_context; + + std::thread event_thread; + bool is_running; + + int running_devices; +}; + +} diff --git a/include/pangolin/video/drivers/ffmpeg.h b/include/pangolin/video/drivers/ffmpeg.h new file mode 100644 index 0000000..d84113e --- /dev/null +++ b/include/pangolin/video/drivers/ffmpeg.h @@ -0,0 +1,206 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +extern "C" +{ + +// HACK for some versions of FFMPEG +#ifndef INT64_C +#define INT64_C(c) (c ## LL) +#define UINT64_C(c) (c ## ULL) +#endif + +#include +#include +#include +#include +} + +#ifndef HAVE_FFMPEG_AVPIXELFORMAT +# define AVPixelFormat PixelFormat +#endif + +namespace pangolin +{ + +class PANGOLIN_EXPORT FfmpegVideo : public VideoInterface +{ +public: + FfmpegVideo(const std::string filename, const std::string fmtout = "RGB24", const std::string codec_hint = "", bool dump_info = false, int user_video_stream = -1); + ~FfmpegVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + +protected: + void InitUrl(const std::string filename, const std::string fmtout = "RGB24", const std::string codec_hint = "", bool dump_info = false , int user_video_stream = -1); + + std::vector streams; + + SwsContext *img_convert_ctx; + AVFormatContext *pFormatCtx; + int videoStream; + int audioStream; + AVCodecContext *pVidCodecCtx; + AVCodecContext *pAudCodecCtx; + AVCodec *pVidCodec; + AVCodec *pAudCodec; + AVFrame *pFrame; + AVFrame *pFrameOut; + AVPacket packet; + int numBytesOut; + uint8_t *buffer; + AVPixelFormat fmtout; +}; + +enum FfmpegMethod +{ + FFMPEG_FAST_BILINEAR = 1, + FFMPEG_BILINEAR = 2, + FFMPEG_BICUBIC = 4, + FFMPEG_X = 8, + FFMPEG_POINT = 0x10, + FFMPEG_AREA = 0x20, + FFMPEG_BICUBLIN = 0x40, + FFMPEG_GAUSS = 0x80, + FFMPEG_SINC =0x100, + FFMPEG_LANCZOS =0x200, + FFMPEG_SPLINE =0x400 +}; + +class PANGOLIN_EXPORT FfmpegConverter : public VideoInterface +{ +public: + FfmpegConverter(std::unique_ptr& videoin, const std::string pixelfmtout = "RGB24", FfmpegMethod method = FFMPEG_POINT); + ~FfmpegConverter(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + +protected: + std::vector streams; + + struct ConvertContext + { + SwsContext* img_convert_ctx; + AVPixelFormat fmtsrc; + AVPixelFormat fmtdst; + AVFrame* avsrc; + AVFrame* avdst; + size_t w,h; + size_t src_buffer_offset; + size_t dst_buffer_offset; + + void convert(const unsigned char * src, unsigned char* dst); + + }; + + std::unique_ptr videoin; + std::unique_ptr input_buffer; + + std::vector converters; + //size_t src_buffer_size; + size_t dst_buffer_size; +}; + +#if (LIBAVFORMAT_VERSION_MAJOR > 55) || ((LIBAVFORMAT_VERSION_MAJOR == 55) && (LIBAVFORMAT_VERSION_MINOR >= 7)) +typedef AVCodecID CodecID; +#endif + +// Forward declaration +class FfmpegVideoOutputStream; + +class PANGOLIN_EXPORT FfmpegVideoOutput + : public VideoOutputInterface +{ + friend class FfmpegVideoOutputStream; +public: + FfmpegVideoOutput( const std::string& filename, int base_frame_rate, int bit_rate ); + ~FfmpegVideoOutput(); + + const std::vector& Streams() const override; + + void SetStreams(const std::vector& streams, const std::string& uri, const picojson::value& properties) override; + + int WriteStreams(const unsigned char* data, const picojson::value& frame_properties) override; + + bool IsPipe() const override; + +protected: + void Initialise(std::string filename); + void StartStream(); + void Close(); + + std::string filename; + bool started; + AVFormatContext *oc; + std::vector streams; + std::vector strs; + + int frame_count; + + int base_frame_rate; + int bit_rate; + bool is_pipe; +}; + +} diff --git a/include/pangolin/video/drivers/firewire.h b/include/pangolin/video/drivers/firewire.h new file mode 100644 index 0000000..a631a22 --- /dev/null +++ b/include/pangolin/video/drivers/firewire.h @@ -0,0 +1,254 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +#ifndef _WIN32 +#include +#endif + + + +namespace pangolin +{ + +PANGOLIN_EXPORT +std::string Dc1394ColorCodingToString(dc1394color_coding_t coding); + +PANGOLIN_EXPORT +dc1394color_coding_t Dc1394ColorCodingFromString(std::string coding); + +PANGOLIN_EXPORT +void Dc1394ModeDetails(dc1394video_mode_t mode, unsigned& w, unsigned& h, std::string& format ); + +class PANGOLIN_EXPORT FirewireFrame +{ + friend class FirewireVideo; +public: + bool isValid() { return frame; } + unsigned char* Image() { return frame ? frame->image : 0; } + unsigned Width() const { return frame ? frame->size[0] : 0; } + unsigned Height() const { return frame ? frame->size[1] : 0; } + unsigned ImageBytes() const { return frame ? frame->image_bytes : 0; } + int FramesBehind() const { return frame ? frame->frames_behind : -1; } + unsigned Timestamp() const { return frame ? frame->timestamp : 0; } + int Id() const { return frame ? frame->id : -1; } +protected: + FirewireFrame(dc1394video_frame_t* frame) : frame(frame) {} + dc1394video_frame_t *frame; +}; + +struct PANGOLIN_EXPORT Guid +{ + Guid(uint64_t guid):guid(guid){} + uint64_t guid; +}; + +class PANGOLIN_EXPORT FirewireVideo : public VideoInterface +{ +public: + const static int MAX_FR = -1; + const static int EXT_TRIG = -1; + const static uint32_t GPIO_CTRL_PIN0 = 0x1110; + const static uint32_t GPIO_CTRL_PIN1 = 0x1120; + const static uint32_t GPIO_CTRL_PIN2 = 0x1130; + const static uint32_t GPIO_CTRL_PIN3 = 0x1140; + + FirewireVideo( + unsigned deviceid = 0, + dc1394video_mode_t video_mode = DC1394_VIDEO_MODE_640x480_RGB8, + dc1394framerate_t framerate = DC1394_FRAMERATE_30, + dc1394speed_t iso_speed = DC1394_ISO_SPEED_400, + int dma_buffers = 10 + ); + + FirewireVideo( + Guid guid, + dc1394video_mode_t video_mode = DC1394_VIDEO_MODE_640x480_RGB8, + dc1394framerate_t framerate = DC1394_FRAMERATE_30, + dc1394speed_t iso_speed = DC1394_ISO_SPEED_400, + int dma_buffers = 10 + ); + + FirewireVideo( + Guid guid, + dc1394video_mode_t video_mode, + float framerate, + uint32_t width, uint32_t height, + uint32_t left, uint32_t top, + dc1394speed_t iso_speed, + int dma_buffers, bool reset_at_boot=false + ); + + FirewireVideo( + unsigned deviceid, + dc1394video_mode_t video_mode, + float framerate, + uint32_t width, uint32_t height, + uint32_t left, uint32_t top, + dc1394speed_t iso_speed, + int dma_buffers, bool reset_at_boot=false + ); + + ~FirewireVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + //! (deprecated: use Streams[i].Width()) + //! Return image width + unsigned Width() const { return width; } + + //! (deprecated: use Streams[i].Height()) + //! Return image height + unsigned Height() const { return height; } + + //! Return object containing reference to image data within + //! DMA buffer. The FirewireFrame must be returned to + //! signal that it can be reused with a corresponding PutFrame() + FirewireFrame GetNext(bool wait = true); + + //! Return object containing reference to newest image data within + //! DMA buffer discarding old images. The FirewireFrame must be + //! returned to signal that it can be reused with a corresponding PutFrame() + FirewireFrame GetNewest(bool wait = true); + + //! Return FirewireFrame object. Data held by FirewireFrame is + //! invalidated on return. + void PutFrame(FirewireFrame& frame); + + //! return absolute shutter value + float GetShutterTime() const; + + //! set absolute shutter value + void SetShutterTime(float val); + + //! set auto shutter value + void SetAutoShutterTime(); + + //! return absolute gain value + float GetGain() const; + + //! set absolute gain value + void SetGain(float val); + + //! set auto gain value + void SetAutoGain(); + + //! return absolute brightness value + float GetBrightness() const; + + //! set absolute brightness value + void SetBrightness(float val); + + //! set auto brightness + void SetAutoBrightness(); + + //! return absolute gamma value + float GetGamma() const; + + //! return quantised shutter value + void SetShutterTimeQuant(int shutter); + + //! set the trigger to internal, i.e. determined by video mode + void SetInternalTrigger(); + + //! set the trigger to external + void SetExternalTrigger( + dc1394trigger_mode_t mode=DC1394_TRIGGER_MODE_0, + dc1394trigger_polarity_t polarity=DC1394_TRIGGER_ACTIVE_HIGH, + dc1394trigger_source_t source=DC1394_TRIGGER_SOURCE_0 + ); + + //! set a camera register + void SetRegister(uint64_t offset, uint32_t value); + + //! read camera register + uint32_t GetRegister(uint64_t offset); + + //! set a camera control register + void SetControlRegister(uint64_t offset, uint32_t value); + + //! read camera control register + uint32_t GetControlRegister(uint64_t offset); + +protected: + void init_stream_info(); + + void init_camera( + uint64_t guid, int dma_frames, + dc1394speed_t iso_speed, + dc1394video_mode_t video_mode, + dc1394framerate_t framerate + ); + + void init_format7_camera( + uint64_t guid, int dma_frames, + dc1394speed_t iso_speed, + dc1394video_mode_t video_mode, + float framerate, + uint32_t width, uint32_t height, + uint32_t left, uint32_t top, bool reset_at_boot + ); + + static int nearest_value(int value, int step, int min, int max); + static double bus_period_from_iso_speed(dc1394speed_t iso_speed); + + size_t frame_size_bytes; + std::vector streams; + + bool running; + dc1394camera_t *camera; + unsigned width, height, top, left; + //dc1394featureset_t features; + dc1394_t * d; + dc1394camera_list_t * list; + mutable dc1394error_t err; + +}; + +} diff --git a/include/pangolin/video/drivers/images.h b/include/pangolin/video/drivers/images.h new file mode 100644 index 0000000..bbe4791 --- /dev/null +++ b/include/pangolin/video/drivers/images.h @@ -0,0 +1,111 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace pangolin +{ + +// Video class that outputs test video signal. +class PANGOLIN_EXPORT ImagesVideo : public VideoInterface, public VideoPlaybackInterface +{ +public: + ImagesVideo(const std::string& wildcard_path); + ImagesVideo(const std::string& wildcard_path, const PixelFormat& raw_fmt, size_t raw_width, size_t raw_height); + + // Explicitly delete copy ctor and assignment operator. + // See http://stackoverflow.com/questions/29565299/how-to-use-a-vector-of-unique-pointers-in-a-dll-exported-class-with-visual-studi + // >> It appears adding __declspec(dllexport) forces the compiler to define the implicitly-declared copy constructor and copy assignment operator + ImagesVideo(const ImagesVideo&) = delete; + ImagesVideo& operator=(const ImagesVideo&) = delete; + + ~ImagesVideo(); + + // Implement VideoInterface + + //! Implement VideoInput::Start() + void Start() override; + + //! Implement VideoInput::Stop() + void Stop() override; + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const override; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const override; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ) override; + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ) override; + + // Implement VideoPlaybackInterface + + size_t GetCurrentFrameId() const override; + + size_t GetTotalFrames() const override; + + size_t Seek(size_t frameid) override; + +protected: + typedef std::vector Frame; + + const std::string& Filename(size_t frameNum, size_t channelNum) { + return filenames[channelNum][frameNum]; + } + + void PopulateFilenames(const std::string& wildcard_path); + + bool LoadFrame(size_t i); + + void ConfigureStreamSizes(); + + std::vector streams; + size_t size_bytes; + + size_t num_files; + size_t num_channels; + size_t next_frame_id; + std::vector > filenames; + std::vector loaded; + + bool unknowns_are_raw; + PixelFormat raw_fmt; + size_t raw_width; + size_t raw_height; +}; + +} diff --git a/include/pangolin/video/drivers/images_out.h b/include/pangolin/video/drivers/images_out.h new file mode 100644 index 0000000..99b696f --- /dev/null +++ b/include/pangolin/video/drivers/images_out.h @@ -0,0 +1,59 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT ImagesVideoOutput : public VideoOutputInterface +{ +public: + ImagesVideoOutput(const std::string& image_folder, const std::string& json_file_out); + ~ImagesVideoOutput(); + + const std::vector& Streams() const override; + void SetStreams(const std::vector& streams, const std::string& uri, const picojson::value& device_properties) override; + int WriteStreams(const unsigned char* data, const picojson::value& frame_properties) override; + bool IsPipe() const override; + +protected: + std::vector streams; + std::string input_uri; + picojson::value device_properties; + picojson::value json_frames; + + size_t image_index; + std::string image_folder; + std::ofstream file; +}; + +} diff --git a/include/pangolin/video/drivers/join.h b/include/pangolin/video/drivers/join.h new file mode 100644 index 0000000..8eb383d --- /dev/null +++ b/include/pangolin/video/drivers/join.h @@ -0,0 +1,78 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT JoinVideo + : public VideoInterface, public VideoFilterInterface +{ +public: + JoinVideo(std::vector> &src); + + ~JoinVideo(); + + // Explicitly delete copy ctor and assignment operator. + // See http://stackoverflow.com/questions/29565299/how-to-use-a-vector-of-unique-pointers-in-a-dll-exported-class-with-visual-studi + // >> It appears adding __declspec(dllexport) forces the compiler to define the implicitly-declared copy constructor and copy assignment operator + JoinVideo(const JoinVideo&) = delete; + JoinVideo& operator=(const JoinVideo&) = delete; + + size_t SizeBytes() const; + + const std::vector& Streams() const; + + void Start(); + + void Stop(); + + bool Sync(int64_t tolerance_us, double transfer_bandwidth_gbps = 0); + + bool GrabNext( unsigned char* image, bool wait = true ); + + bool GrabNewest( unsigned char* image, bool wait = true ); + + std::vector& InputStreams(); + +protected: + int64_t GetAdjustedCaptureTime(size_t src_index); + + std::vector> storage; + std::vector src; + std::vector streams; + size_t size_bytes; + + int64_t sync_tolerance_us; + int64_t transfer_bandwidth_bytes_per_us; +}; + + +} diff --git a/include/pangolin/video/drivers/merge.h b/include/pangolin/video/drivers/merge.h new file mode 100644 index 0000000..fd3cc07 --- /dev/null +++ b/include/pangolin/video/drivers/merge.h @@ -0,0 +1,70 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace pangolin +{ + +// Take N streams, and place them into one big buffer. +class PANGOLIN_EXPORT MergeVideo : public VideoInterface, public VideoFilterInterface +{ +public: + MergeVideo(std::unique_ptr& src, const std::vector& stream_pos, size_t w, size_t h); + ~MergeVideo(); + + void Start() override; + + void Stop() override; + + size_t SizeBytes() const override; + + const std::vector& Streams() const override; + + bool GrabNext( unsigned char* image, bool wait = true ) override; + + bool GrabNewest( unsigned char* image, bool wait = true ) override; + + std::vector& InputStreams() override; + +protected: + void CopyBuffer(unsigned char* dst_bytes, unsigned char* src_bytes); + + std::unique_ptr src; + std::vector videoin; + std::unique_ptr buffer; + std::vector stream_pos; + + std::vector streams; + size_t size_bytes; +}; + +} diff --git a/include/pangolin/video/drivers/mirror.h b/include/pangolin/video/drivers/mirror.h new file mode 100644 index 0000000..b7b7000 --- /dev/null +++ b/include/pangolin/video/drivers/mirror.h @@ -0,0 +1,93 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +enum MirrorOptions +{ + MirrorOptionsNone = 0, + MirrorOptionsFlipX, + MirrorOptionsFlipY, + MirrorOptionsFlipXY, +}; + +// Video class that debayers its video input using the given method. +class PANGOLIN_EXPORT MirrorVideo : + public VideoInterface, + public VideoFilterInterface, + public BufferAwareVideoInterface +{ +public: + MirrorVideo(std::unique_ptr& videoin, const std::vector& flips); + ~MirrorVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + //! Implement VideoFilterInterface method + std::vector& InputStreams(); + + uint32_t AvailableFrames() const; + + bool DropNFrames(uint32_t n); + +protected: + void Process(unsigned char* image, const unsigned char* buffer); + + std::unique_ptr videoin; + std::vector inputs; + std::vector streams; + std::vector flips; + size_t size_bytes; + unsigned char* buffer; + + picojson::value device_properties; + picojson::value frame_properties; +}; + +} diff --git a/include/pangolin/video/drivers/openni.h b/include/pangolin/video/drivers/openni.h new file mode 100644 index 0000000..195f1d9 --- /dev/null +++ b/include/pangolin/video/drivers/openni.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include +#include + +// Workaround poor OpenNI Platform test on Linux before including XnCppWrapper.h +// See https://github.com/dennishamester/OpenNI/commit/ca99f6181234c682bba42a6ba +#ifdef _LINUX_ +#define linux 1 +#endif // _LINUX_ + +// OpenNI generates SO MANY warnings, we'll just disable all for this header(!) +// GCC and clang will listen to this pramga. +#ifndef _MSVC_ +#pragma GCC system_header +#endif +#include + +namespace pangolin +{ + +//! Interface to video capture sources +struct PANGOLIN_EXPORT OpenNiVideo : public VideoInterface +{ +public: + OpenNiVideo(OpenNiSensorType s1, OpenNiSensorType s2, ImageDim dim = ImageDim(640,480), int fps = 30); + ~OpenNiVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + void SetAutoExposure(bool enabled) + { +#if XN_MINOR_VERSION > 5 || (XN_MINOR_VERSION == 5 && XN_BUILD_VERSION >= 7) + if(imageNode.IsValid()) { + imageNode.GetAutoExposureCap().Set(enabled ? 1 : 0); + } +#else + throw pangolin::VideoException("SetAutoExposure Not supported for this version of OpenNI."); +#endif + } + +protected: + std::vector streams; + OpenNiSensorType sensor_type[2]; + + xn::Context context; + xn::DepthGenerator depthNode; + xn::ImageGenerator imageNode; + xn::IRGenerator irNode; + + size_t sizeBytes; +}; + +} diff --git a/include/pangolin/video/drivers/openni2.h b/include/pangolin/video/drivers/openni2.h new file mode 100644 index 0000000..d3874aa --- /dev/null +++ b/include/pangolin/video/drivers/openni2.h @@ -0,0 +1,147 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Richard Newcombe + * 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include +#include + +#include + +namespace pangolin +{ +const int MAX_OPENNI2_STREAMS = 2 * ONI_MAX_SENSORS; + +//! Interface to video capture sources +struct OpenNi2Video : public VideoInterface, public VideoPropertiesInterface, public VideoPlaybackInterface +{ +public: + + // Open all RGB and Depth streams from all devices + OpenNi2Video(ImageDim dim=ImageDim(640,480), ImageRoi roi=ImageRoi(0,0,0,0), int fps=30); + + // Open streams specified + OpenNi2Video(std::vector& stream_modes); + + // Open openni file + OpenNi2Video(const std::string& filename); + + // Open openni file with certain params + OpenNi2Video(const std::string& filename, std::vector& stream_modes); + + void UpdateProperties(); + + void SetMirroring(bool enable); + void SetAutoExposure(bool enable); + void SetAutoWhiteBalance(bool enable); + void SetDepthCloseRange(bool enable); + void SetDepthHoleFilter(bool enable); + void SetDepthColorSyncEnabled(bool enable); + void SetFastCrop(bool enable); + void SetRegisterDepthToImage(bool enable); + void SetPlaybackSpeed(float speed); + void SetPlaybackRepeat(bool enabled); + + ~OpenNi2Video(); + + //! Implement VideoInput::Start() + void Start() override; + + //! Implement VideoInput::Stop() + void Stop() override; + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const override; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const override; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ) override; + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ) override; + + //! Implement VideoPropertiesInterface::Properties() + const picojson::value& DeviceProperties() const override{ + return device_properties; + } + + //! Implement VideoPropertiesInterface::Properties() + const picojson::value& FrameProperties() const override{ + return frame_properties; + } + + //! Implement VideoPlaybackInterface::GetCurrentFrameId + size_t GetCurrentFrameId() const override; + + //! Implement VideoPlaybackInterface::GetTotalFrames + size_t GetTotalFrames() const override; + + //! Implement VideoPlaybackInterface::Seek + size_t Seek(size_t frameid) override; + + openni::VideoStream* GetVideoStream(int stream); + +protected: + void InitialiseOpenNI(); + int AddDevice(const std::string& device_uri); + void AddStream(const OpenNiStreamMode& mode); + void SetupStreamModes(); + void PrintOpenNI2Modes(openni::SensorType sensorType); + openni::VideoMode FindOpenNI2Mode(openni::Device &device, openni::SensorType sensorType, int width, int height, int fps, openni::PixelFormat fmt ); + + size_t numDevices; + size_t numStreams; + + openni::Device devices[ONI_MAX_SENSORS]; + OpenNiStreamMode sensor_type[ONI_MAX_SENSORS]; + + openni::VideoStream video_stream[ONI_MAX_SENSORS]; + openni::VideoFrameRef video_frame[ONI_MAX_SENSORS]; + + std::vector streams; + size_t sizeBytes; + + picojson::value device_properties; + picojson::value frame_properties; + picojson::value* streams_properties; + + bool use_depth; + bool use_ir; + bool use_rgb; + bool depth_to_color; + bool use_ir_and_rgb; + + size_t current_frame_index; + size_t total_frames; +}; + +} diff --git a/include/pangolin/video/drivers/openni_common.h b/include/pangolin/video/drivers/openni_common.h new file mode 100644 index 0000000..c93183e --- /dev/null +++ b/include/pangolin/video/drivers/openni_common.h @@ -0,0 +1,153 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * 2015 Richard Newcombe + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +namespace pangolin +{ + +enum OpenNiSensorType +{ + OpenNiUnassigned = -1, + OpenNiRgb = 0, + OpenNiIr, + OpenNiDepth_1mm, + OpenNiDepth_1mm_Registered, + OpenNiDepth_100um, + OpenNiIr8bit, + OpenNiIr24bit, + OpenNiIrProj, + OpenNiIr8bitProj, + OpenNiGrey +}; + +struct PANGOLIN_EXPORT OpenNiStreamMode +{ + OpenNiStreamMode( + OpenNiSensorType sensor_type=OpenNiUnassigned, + ImageDim dim=ImageDim(640,480), ImageRoi roi=ImageRoi(0,0,0,0), int fps=30, int device=0 + ) + : sensor_type(sensor_type), dim(dim), roi(roi), fps(fps), device(device) + { + + } + + OpenNiSensorType sensor_type; + ImageDim dim; + ImageRoi roi; + int fps; + int device; +}; + +inline OpenNiSensorType openni_sensor(const std::string& str) +{ + if( !str.compare("grey") || !str.compare("gray") ) { + return OpenNiGrey; + }else if( !str.compare("rgb") ) { + return OpenNiRgb; + }else if( !str.compare("ir") ) { + return OpenNiIr; + }else if( !str.compare("depth1mm") || !str.compare("depth") ) { + return OpenNiDepth_1mm; + }else if( !str.compare("depth100um") ) { + return OpenNiDepth_100um; + }else if( !str.compare("depth_reg") || !str.compare("reg_depth")) { + return OpenNiDepth_1mm_Registered; + }else if( !str.compare("ir8") ) { + return OpenNiIr8bit; + }else if( !str.compare("ir24") ) { + return OpenNiIr24bit; + }else if( !str.compare("ir+") ) { + return OpenNiIrProj; + }else if( !str.compare("ir8+") ) { + return OpenNiIr8bitProj; + }else if( str.empty() ) { + return OpenNiUnassigned; + }else{ + throw pangolin::VideoException("Unknown OpenNi sensor", str ); + } +} + +// Find prefix character key +// Given arguments "depth!5:320x240@15", "!:@", would return map +// \0->"depth", !->"5", :->"320x240", @->"15" +inline std::map GetTokenSplits(const std::string& str, const std::string& tokens) +{ + std::map splits; + + char last_token = 0; + size_t last_start = 0; + for(unsigned int i=0; i> (std::istream &is, OpenNiStreamMode& fmt) +{ + std::string str; + is >> str; + + std::map splits = GetTokenSplits(str, "!:@#"); + + if(splits.count(0)) { + fmt.sensor_type = openni_sensor(splits[0]); + } + + if(splits.count('@')) { + fmt.fps = pangolin::Convert::Do(splits['@']); + } + + if(splits.count(':')) { + fmt.dim = pangolin::Convert::Do(splits[':']); + } + + if(splits.count('!')) { + fmt.device = pangolin::Convert::Do(splits['!']); + } + + if(splits.count('#')) { + fmt.roi = pangolin::Convert::Do(splits['#']); + } + + return is; +} + +} diff --git a/include/pangolin/video/drivers/pango.h b/include/pangolin/video/drivers/pango.h new file mode 100644 index 0000000..f0d0752 --- /dev/null +++ b/include/pangolin/video/drivers/pango.h @@ -0,0 +1,101 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT PangoVideo + : public VideoInterface, public VideoPropertiesInterface, public VideoPlaybackInterface +{ +public: + PangoVideo(const std::string& filename, std::shared_ptr playback_session); + ~PangoVideo(); + + // Implement VideoInterface + + size_t SizeBytes() const override; + + const std::vector& Streams() const override; + + void Start() override; + + void Stop() override; + + bool GrabNext( unsigned char* image, bool wait = true ) override; + + bool GrabNewest( unsigned char* image, bool wait = true ) override; + + // Implement VideoPropertiesInterface + const picojson::value& DeviceProperties() const override { + if (-1 == _src_id) throw std::runtime_error("Not initialised"); + return _device_properties; + } + + const picojson::value& FrameProperties() const override { + return _frame_properties; + } + + // Implement VideoPlaybackInterface + + size_t GetCurrentFrameId() const override; + + size_t GetTotalFrames() const override; + + size_t Seek(size_t frameid) override; + +private: + void HandlePipeClosed(); + +protected: + int FindPacketStreamSource(); + void SetupStreams(const PacketStreamSource& src); + + const std::string _filename; + std::shared_ptr _playback_session; + std::shared_ptr _reader; + SyncTimeEventPromise _event_promise; + int _src_id; + const PacketStreamSource* _source; + + size_t _size_bytes; + bool _fixed_size; + std::vector _streams; + std::vector stream_decoder; + picojson::value _device_properties; + picojson::value _frame_properties; + + Registration session_seek; +}; + +} diff --git a/include/pangolin/video/drivers/pango_video_output.h b/include/pangolin/video/drivers/pango_video_output.h new file mode 100644 index 0000000..a986a70 --- /dev/null +++ b/include/pangolin/video/drivers/pango_video_output.h @@ -0,0 +1,70 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT PangoVideoOutput : public VideoOutputInterface +{ +public: + PangoVideoOutput(const std::string& filename, size_t buffer_size_bytes, const std::map &stream_encoder_uris); + ~PangoVideoOutput(); + + const std::vector& Streams() const override; + void SetStreams(const std::vector& streams, const std::string& uri, const picojson::value& device_properties) override; + int WriteStreams(const unsigned char* data, const picojson::value& frame_properties) override; + bool IsPipe() const override; + +protected: +// void WriteHeader(); + + std::vector streams; + std::string input_uri; + const std::string filename; + picojson::value device_properties; + + PacketStreamWriter packetstream; + size_t packetstream_buffer_size_bytes; + int packetstreamsrcid; + size_t total_frame_size; + bool is_pipe; + + bool fixed_size; + std::map stream_encoder_uris; + std::vector stream_encoders; +}; + +} diff --git a/include/pangolin/video/drivers/pleora.h b/include/pangolin/video/drivers/pleora.h new file mode 100644 index 0000000..4b45744 --- /dev/null +++ b/include/pangolin/video/drivers/pleora.h @@ -0,0 +1,196 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2015 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace pangolin +{ + +struct GrabbedBuffer { + + inline GrabbedBuffer(PvBuffer* b,PvResult r,bool v) + : buff(b), res(r), valid(v) + { + } + + PvBuffer* buff; + PvResult res; + bool valid; + +}; + +typedef std::list GrabbedBufferList; + +typedef std::list BufferList; + +class PANGOLIN_EXPORT PleoraVideo : public VideoInterface, public VideoPropertiesInterface, + public BufferAwareVideoInterface, public GenicamVideoInterface +{ +public: + + static const size_t DEFAULT_BUFFER_COUNT = 30; + + PleoraVideo(const Params& p); + + ~PleoraVideo(); + + void Start(); + + void Stop(); + + size_t SizeBytes() const; + + const std::vector& Streams() const; + + bool GrabNext( unsigned char* image, bool wait = true ); + + bool GrabNewest( unsigned char* image, bool wait = true ); + + std::string GetParameter(const std::string& name); + + void SetParameter(const std::string& name, const std::string& value); + + void SetGain(int64_t val); + + int64_t GetGain(); + + void SetAnalogBlackLevel(int64_t val); + + int64_t GetAnalogBlackLevel(); + + void SetExposure(double val); + + double GetExposure(); + + void SetGamma(double val); + + double GetGamma(); + + + + void SetupTrigger(bool triggerActive, int64_t triggerSource, int64_t acquisitionMode); + + const picojson::value& DeviceProperties() const { + return device_properties; + } + + const picojson::value& FrameProperties() const { + return frame_properties; + } + + uint32_t AvailableFrames() const; + + bool DropNFrames(uint32_t n); + +protected: + + void InitDevice(const char *model_name, const char *serial_num, size_t index); + + void DeinitDevice(); + + void SetDeviceParams(Params& p); + + void InitStream(); + + void DeinitStream(); + + void InitPangoStreams(); + + void InitPangoDeviceProperties(); + + void InitBuffers(size_t buffer_count); + + void DeinitBuffers(); + + template + T DeviceParam(const char* name); + + template + bool SetDeviceParam(const char* name, T val); + + template + T StreamParam(const char* name); + + template + bool SetStreamParam(const char* name, T val); + + bool ParseBuffer(PvBuffer* lBuffer, unsigned char* image); + + void RetriveAllAvailableBuffers(uint32_t timeout); + + std::vector streams; + picojson::value device_properties; + picojson::value frame_properties; + + size_t size_bytes; + + // Pleora handles + PvSystem* lPvSystem; + const PvDeviceInfo* lDeviceInfo; + PvDevice* lDevice; + PvStream* lStream; + + // Genicam device parameters + PvGenParameterArray* lDeviceParams; + PvGenCommand* lStart; + PvGenCommand* lStop; + + PvGenInteger* lAnalogGain; + PvGenInteger* lAnalogBlackLevel; + PvGenFloat* lExposure; + PvGenFloat* lGamma; + PvGenEnum* lAquisitionMode; + PvGenEnum* lTriggerSource; + PvGenEnum* lTriggerMode; + PvGenFloat* lTemperatureCelcius; + bool getTemp; + + // Genicam stream parameters + PvGenParameterArray* lStreamParams; + + BufferList lBufferList; + GrabbedBufferList lGrabbedBuffList; + uint32_t validGrabbedBuffers; +}; + +} diff --git a/include/pangolin/video/drivers/pvn.h b/include/pangolin/video/drivers/pvn.h new file mode 100644 index 0000000..8e47e54 --- /dev/null +++ b/include/pangolin/video/drivers/pvn.h @@ -0,0 +1,77 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT PvnVideo : public VideoInterface +{ +public: + PvnVideo(const std::string& filename, bool realtime = false); + ~PvnVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + +protected: + int frames; + std::ifstream file; + + std::vector streams; + size_t frame_size_bytes; + + bool realtime; + pangolin::basetime frame_interval; + pangolin::basetime last_frame; + + void ReadFileHeader(); +}; + +} diff --git a/include/pangolin/video/drivers/realsense.h b/include/pangolin/video/drivers/realsense.h new file mode 100644 index 0000000..064bc7a --- /dev/null +++ b/include/pangolin/video/drivers/realsense.h @@ -0,0 +1,87 @@ +#pragma once + +#include + +#include + +#include + +namespace rs { +class context; +class device; +} + +namespace pangolin +{ + +//! Interface to video capture sources +struct RealSenseVideo : public VideoInterface, public VideoPropertiesInterface, public VideoPlaybackInterface +{ +public: + + // Open all RGB and Depth streams from all devices + RealSenseVideo(ImageDim dim=ImageDim(640,480), int fps=30); + + // Open streams specified + // TODO + //RealSenseVideo(std::vector& stream_modes); + + ~RealSenseVideo(); + + //! Implement VideoInput::Start() + void Start() override; + + //! Implement VideoInput::Stop() + void Stop() override; + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const override; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const override; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ) override; + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ) override; + + //! Implement VideoPropertiesInterface::Properties() + const picojson::value& DeviceProperties() const override { + return device_properties; + } + + //! Implement VideoPropertiesInterface::Properties() + const picojson::value& FrameProperties() const override { + return frame_properties; + } + + //! Implement VideoPlaybackInterface::GetCurrentFrameId + size_t GetCurrentFrameId() const override; + + //! Implement VideoPlaybackInterface::GetTotalFrames + size_t GetTotalFrames() const override; + + //! Implement VideoPlaybackInterface::Seek + size_t Seek(size_t frameid) override; + +protected: + size_t sizeBytes; + + std::vector streams; + + picojson::value device_properties; + picojson::value frame_properties; + picojson::value* streams_properties; + + size_t current_frame_index; + size_t total_frames; + + rs::context* ctx_; + std::vector devs_; + + ImageDim dim_; + size_t fps_; +}; + +} diff --git a/include/pangolin/video/drivers/shared_memory.h b/include/pangolin/video/drivers/shared_memory.h new file mode 100644 index 0000000..66f27dc --- /dev/null +++ b/include/pangolin/video/drivers/shared_memory.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace pangolin +{ + +class SharedMemoryVideo : public VideoInterface +{ +public: + SharedMemoryVideo(size_t w, size_t h, std::string pix_fmt, + const std::shared_ptr& shared_memory, + const std::shared_ptr& buffer_full); + ~SharedMemoryVideo(); + + size_t SizeBytes() const; + const std::vector& Streams() const; + void Start(); + void Stop(); + bool GrabNext(unsigned char *image, bool wait); + bool GrabNewest(unsigned char *image, bool wait); + +private: + PixelFormat _fmt; + size_t _frame_size; + std::vector _streams; + std::shared_ptr _shared_memory; + std::shared_ptr _buffer_full; +}; + +} diff --git a/include/pangolin/video/drivers/shift.h b/include/pangolin/video/drivers/shift.h new file mode 100644 index 0000000..ed7f510 --- /dev/null +++ b/include/pangolin/video/drivers/shift.h @@ -0,0 +1,73 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +// Video class that debayers its video input using the given method. +class PANGOLIN_EXPORT ShiftVideo : public VideoInterface, public VideoFilterInterface +{ +public: + ShiftVideo(std::unique_ptr& videoin, PixelFormat new_fmt, int shift_right_bits = 0, unsigned int mask = 0xFFFF); + ~ShiftVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + std::vector& InputStreams(); + +protected: + std::unique_ptr src; + std::vector videoin; + std::vector streams; + size_t size_bytes; + unsigned char* buffer; + int shift_right_bits; + unsigned int mask; +}; + +} diff --git a/include/pangolin/video/drivers/split.h b/include/pangolin/video/drivers/split.h new file mode 100644 index 0000000..a567458 --- /dev/null +++ b/include/pangolin/video/drivers/split.h @@ -0,0 +1,65 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT SplitVideo + : public VideoInterface, public VideoFilterInterface +{ +public: + SplitVideo(std::unique_ptr& videoin, const std::vector& streams); + + ~SplitVideo(); + + size_t SizeBytes() const; + + const std::vector& Streams() const; + + void Start(); + + void Stop(); + + bool GrabNext( unsigned char* image, bool wait = true ); + + bool GrabNewest( unsigned char* image, bool wait = true ); + + std::vector& InputStreams(); + +protected: + std::unique_ptr src; + std::vector videoin; + std::vector streams; +}; + + +} diff --git a/include/pangolin/video/drivers/teli.h b/include/pangolin/video/drivers/teli.h new file mode 100644 index 0000000..0cf2292 --- /dev/null +++ b/include/pangolin/video/drivers/teli.h @@ -0,0 +1,116 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2015 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace pangolin +{ + +// Video class that outputs test video signal. +class PANGOLIN_EXPORT TeliVideo : public VideoInterface, public VideoPropertiesInterface, + public BufferAwareVideoInterface, public GenicamVideoInterface +{ +public: + TeliVideo(const Params &p); + ~TeliVideo(); + + Params OpenCameraAndGetRemainingParameters(Params ¶ms); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + inline Teli::CAM_HANDLE GetCameraHandle() { + return cam; + } + + inline Teli::CAM_STRM_HANDLE GetCameraStreamHandle() { + return strm; + } + + std::string GetParameter(const std::string& name); + + void SetParameter(const std::string& name, const std::string& value); + + //! Returns number of available frames + uint32_t AvailableFrames() const; + + //! Drops N frames in the queue starting from the oldest + //! returns false if less than n frames arae available + bool DropNFrames(uint32_t n); + + //! Access JSON properties of device + const picojson::value& DeviceProperties() const; + + //! Access JSON properties of most recently captured frame + const picojson::value& FrameProperties() const; + + void PopulateEstimatedCenterCaptureTime(pangolin::basetime host_reception_time); + +protected: + void Initialise(); + void InitPangoDeviceProperties(); + void SetDeviceParams(const Params &p); + + std::vector streams; + size_t size_bytes; + + Teli::CAM_HANDLE cam; + Teli::CAM_STRM_HANDLE strm; +#ifdef _WIN_ + HANDLE hStrmCmpEvt; +#endif +#ifdef _LINUX_ + Teli::SIGNAL_HANDLE hStrmCmpEvt; +#endif + double transfer_bandwidth_gbps; + int exposure_us; + picojson::value device_properties; + picojson::value frame_properties; +}; + +} diff --git a/include/pangolin/video/drivers/test.h b/include/pangolin/video/drivers/test.h new file mode 100644 index 0000000..02646f6 --- /dev/null +++ b/include/pangolin/video/drivers/test.h @@ -0,0 +1,66 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +// Video class that outputs test video signal. +class PANGOLIN_EXPORT TestVideo : public VideoInterface +{ +public: + TestVideo(size_t w, size_t h, size_t n, std::string pix_fmt); + ~TestVideo(); + + //! Implement VideoInput::Start() + void Start() override; + + //! Implement VideoInput::Stop() + void Stop() override; + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const override; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const override; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ) override; + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ) override; + +protected: + std::vector streams; + size_t size_bytes; +}; + +} diff --git a/include/pangolin/video/drivers/thread.h b/include/pangolin/video/drivers/thread.h new file mode 100644 index 0000000..ddd0242 --- /dev/null +++ b/include/pangolin/video/drivers/thread.h @@ -0,0 +1,112 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin +{ + + +// Video class that creates a thread that keeps pulling frames and processing from its children. +class PANGOLIN_EXPORT ThreadVideo : public VideoInterface, public VideoPropertiesInterface, + public BufferAwareVideoInterface, public VideoFilterInterface +{ +public: + ThreadVideo(std::unique_ptr& videoin, size_t num_buffers); + ~ThreadVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + const picojson::value& DeviceProperties() const; + + const picojson::value& FrameProperties() const; + + uint32_t AvailableFrames() const; + + bool DropNFrames(uint32_t n); + + void operator()(); + + std::vector& InputStreams(); + +protected: + struct GrabResult + { + GrabResult(const size_t buffer_size) + : return_status(false), + buffer(new unsigned char[buffer_size]) + { + } + + // No copy constructor. + GrabResult(const GrabResult& o) = delete; + + // Default move constructor + GrabResult(GrabResult&& o) = default; + + bool return_status; + std::unique_ptr buffer; + picojson::value frame_properties; + }; + + std::unique_ptr src; + std::vector videoin; + + bool quit_grab_thread; + FixSizeBuffersQueue queue; + + std::condition_variable cv; + std::mutex cvMtx; + std::thread grab_thread; + + mutable picojson::value device_properties; + picojson::value frame_properties; +}; + +} diff --git a/include/pangolin/video/drivers/unpack.h b/include/pangolin/video/drivers/unpack.h new file mode 100644 index 0000000..4f0826c --- /dev/null +++ b/include/pangolin/video/drivers/unpack.h @@ -0,0 +1,84 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2014 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +// Video class that debayers its video input using the given method. +class PANGOLIN_EXPORT UnpackVideo : + public VideoInterface, + public VideoFilterInterface, + public BufferAwareVideoInterface +{ +public: + UnpackVideo(std::unique_ptr& videoin, PixelFormat new_fmt); + ~UnpackVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + //! Implement VideoFilterInterface method + std::vector& InputStreams(); + + uint32_t AvailableFrames() const; + + bool DropNFrames(uint32_t n); + +protected: + void Process(unsigned char* image, const unsigned char* buffer); + + std::unique_ptr src; + std::vector videoin; + std::vector streams; + size_t size_bytes; + unsigned char* buffer; + + picojson::value device_properties; + picojson::value frame_properties; +}; + +} diff --git a/include/pangolin/video/drivers/uvc.h b/include/pangolin/video/drivers/uvc.h new file mode 100644 index 0000000..5427c13 --- /dev/null +++ b/include/pangolin/video/drivers/uvc.h @@ -0,0 +1,103 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +#ifdef _MSC_VER +// Define missing timeval struct +typedef struct timeval { + long tv_sec; + long tv_usec; +} timeval; +#endif // _MSC_VER + +#include + +namespace pangolin +{ + +class PANGOLIN_EXPORT UvcVideo : public VideoInterface, public VideoUvcInterface, public VideoPropertiesInterface +{ +public: + UvcVideo(int vendor_id, int product_id, const char* sn, int deviceid, int width, int height, int fps); + ~UvcVideo(); + + void InitDevice(int vid, int pid, const char* sn, int deviceid, int width, int height, int fps); + void DeinitDevice(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + //! Implement VideoUvcInterface::GetCtrl() + int IoCtrl(uint8_t unit, uint8_t ctrl, unsigned char* data, int len, UvcRequestCode req_code); + + //! Access JSON properties of device + const picojson::value& DeviceProperties() const; + + //! Access JSON properties of most recently captured frame + const picojson::value& FrameProperties() const; + +protected: + void InitPangoDeviceProperties(); + static uvc_error_t FindDevice( + uvc_context_t *ctx, uvc_device_t **dev, + int vid, int pid, const char *sn, int device_id); + + std::vector streams; + size_t size_bytes; + + uvc_context* ctx_; + uvc_device* dev_; + uvc_device_handle* devh_; + uvc_stream_handle* strm_; + uvc_stream_ctrl_t ctrl_; + uvc_frame_t* frame_; + picojson::value device_properties; + picojson::value frame_properties; + bool is_streaming; +}; + +} diff --git a/include/pangolin/video/drivers/uvc_mediafoundation.h b/include/pangolin/video/drivers/uvc_mediafoundation.h new file mode 100644 index 0000000..4468180 --- /dev/null +++ b/include/pangolin/video/drivers/uvc_mediafoundation.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +struct IMFActivate; +struct IMFMediaSource; +struct IMFSourceReader; +struct IBaseFilter; +struct IKsControl; + +namespace pangolin +{ + +class PANGOLIN_EXPORT UvcMediaFoundationVideo + : public pangolin::VideoInterface, public pangolin::VideoUvcInterface, public pangolin::VideoPropertiesInterface +{ + public: + UvcMediaFoundationVideo(int vendorId, int productId, int deviceId, size_t width, size_t height, int fps); + ~UvcMediaFoundationVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext(unsigned char* image, bool wait = true); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest(unsigned char* image, bool wait = true); + + //! Implement VideoUvcInterface::GetCtrl() + int IoCtrl(uint8_t unit, uint8_t ctrl, unsigned char* data, int len, pangolin::UvcRequestCode req_code); + + //! Access JSON properties of device + const picojson::value& DeviceProperties() const; + + //! Access JSON properties of most recently captured frame + const picojson::value& FrameProperties() const; + + protected: + bool FindDevice(int vendorId, int productId, int deviceId); + void InitDevice(size_t width, size_t height, int fps); + void DeinitDevice(); + + static bool DeviceMatches(const std::wstring& symLink, int vendorId, int productId); + static bool SymLinkIDMatches(const std::wstring& symLink, const wchar_t* idStr, int id); + + std::vector streams; + size_t size_bytes; + + IMFMediaSource* mediaSource; + IMFSourceReader* sourceReader; + IBaseFilter* baseFilter; + IKsControl* ksControl; + DWORD ksControlNodeId; + + picojson::value device_properties; + picojson::value frame_properties; +}; +} \ No newline at end of file diff --git a/include/pangolin/video/drivers/v4l.h b/include/pangolin/video/drivers/v4l.h new file mode 100644 index 0000000..13f70b5 --- /dev/null +++ b/include/pangolin/video/drivers/v4l.h @@ -0,0 +1,124 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace pangolin +{ + +typedef enum { + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR, +} io_method; + +struct buffer { + void* start; + size_t length; +}; + +class PANGOLIN_EXPORT V4lVideo : public VideoInterface, public VideoUvcInterface, public VideoPropertiesInterface +{ +public: + V4lVideo(const char* dev_name, io_method io = IO_METHOD_MMAP, unsigned iwidth=0, unsigned iheight=0); + ~V4lVideo(); + + //! Implement VideoInput::Start() + void Start(); + + //! Implement VideoInput::Stop() + void Stop(); + + //! Implement VideoInput::SizeBytes() + size_t SizeBytes() const; + + //! Implement VideoInput::Streams() + const std::vector& Streams() const; + + //! Implement VideoInput::GrabNext() + bool GrabNext( unsigned char* image, bool wait = true ); + + //! Implement VideoInput::GrabNewest() + bool GrabNewest( unsigned char* image, bool wait = true ); + + //! Implement VideoUvcInterface::IoCtrl() + int IoCtrl(uint8_t unit, uint8_t ctrl, unsigned char* data, int len, UvcRequestCode req_code); + + void SetExposureUs(int exposure_us); + + void SetGain(double gain); + + int GetFileDescriptor() const{ + return fd; + } + + //! Access JSON properties of device + const picojson::value& DeviceProperties() const; + + //! Access JSON properties of most recently captured frame + const picojson::value& FrameProperties() const; + +protected: + void InitPangoDeviceProperties(); + + + int ReadFrame(unsigned char* image); + void Mainloop(); + + void init_read(unsigned int buffer_size); + void init_mmap(const char* dev_name); + void init_userp(const char* dev_name, unsigned int buffer_size); + + void init_device(const char* dev_name, unsigned iwidth, unsigned iheight, unsigned ifps, unsigned v4l_format = V4L2_PIX_FMT_YUYV, v4l2_field field = V4L2_FIELD_INTERLACED); + void uninit_device(); + + void open_device(const char* dev_name); + void close_device(); + + std::vector streams; + + io_method io; + int fd; + buffer* buffers; + unsigned int n_buffers; + bool running; + unsigned width; + unsigned height; + float fps; + size_t image_size; + + picojson::value device_properties; + picojson::value frame_properties; +}; + +} diff --git a/include/pangolin/video/iostream_operators.h b/include/pangolin/video/iostream_operators.h new file mode 100644 index 0000000..37be326 --- /dev/null +++ b/include/pangolin/video/iostream_operators.h @@ -0,0 +1,132 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2015 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace pangolin +{ + +struct PANGOLIN_EXPORT Point +{ + inline Point() : x(0), y(0) {} + inline Point(size_t x, size_t y) : x(x), y(y) {} + size_t x; + size_t y; +}; + +typedef Point ImageDim; + +struct PANGOLIN_EXPORT ImageRoi +{ + inline ImageRoi() : x(0), y(0), w(0), h(0) {} + inline ImageRoi(size_t x, size_t y, size_t w, size_t h) : x(x), y(y), w(w), h(h) {} + size_t x; size_t y; + size_t w; size_t h; +}; + +inline std::istream& operator>> (std::istream &is, ImageDim &dim) +{ + if(std::isdigit(is.peek()) ) { + // Expect 640x480, 640*480, ... + is >> dim.x; is.get(); is >> dim.y; + }else{ + // Expect 'VGA', 'QVGA', etc + std::string sdim; + is >> sdim; + ToUpper(sdim); + + if( !sdim.compare("QQVGA") ) { + dim = ImageDim(160,120); + }else if( !sdim.compare("HQVGA") ) { + dim = ImageDim(240,160); + }else if( !sdim.compare("QVGA") ) { + dim = ImageDim(320,240); + }else if( !sdim.compare("WQVGA") ) { + dim = ImageDim(360,240); + }else if( !sdim.compare("HVGA") ) { + dim = ImageDim(480,320); + }else if( !sdim.compare("VGA") ) { + dim = ImageDim(640,480); + }else if( !sdim.compare("WVGA") ) { + dim = ImageDim(720,480); + }else if( !sdim.compare("SVGA") ) { + dim = ImageDim(800,600); + }else if( !sdim.compare("DVGA") ) { + dim = ImageDim(960,640); + }else if( !sdim.compare("WSVGA") ) { + dim = ImageDim(1024,600); + }else{ + throw VideoException("Unrecognised image-size string."); + } + } + return is; +} + +inline std::istream& operator>> (std::istream &is, ImageRoi &roi) +{ + is >> roi.x; is.get(); is >> roi.y; is.get(); + is >> roi.w; is.get(); is >> roi.h; + return is; +} + +inline std::istream& operator>> (std::istream &is, PixelFormat& fmt) +{ + std::string sfmt; + is >> sfmt; + fmt = PixelFormatFromString(sfmt); + return is; +} + +inline std::istream& operator>> (std::istream &is, Image& img) +{ + size_t offset; + is >> offset; is.get(); + img.ptr = (unsigned char*)0 + offset; + is >> img.w; is.get(); + is >> img.h; is.get(); + is >> img.pitch; + return is; +} + +inline std::istream& operator>> (std::istream &is, StreamInfo &stream) +{ + PixelFormat fmt; + Image img_offset; + is >> img_offset; is.get(); + is >> fmt; + stream = StreamInfo(fmt, img_offset); + return is; +} + +} diff --git a/include/pangolin/video/stream_encoder_factory.h b/include/pangolin/video/stream_encoder_factory.h new file mode 100644 index 0000000..b6f7efe --- /dev/null +++ b/include/pangolin/video/stream_encoder_factory.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +namespace pangolin { + +using ImageEncoderFunc = std::function&)>; +using ImageDecoderFunc = std::function; + +class StreamEncoderFactory +{ +public: + static StreamEncoderFactory& I(); + + ImageEncoderFunc GetEncoder(const std::string& encoder_spec, const PixelFormat& fmt); + + ImageDecoderFunc GetDecoder(const std::string& encoder_spec, const PixelFormat& fmt); +}; + +} diff --git a/include/pangolin/video/stream_info.h b/include/pangolin/video/stream_info.h new file mode 100644 index 0000000..fcd6e10 --- /dev/null +++ b/include/pangolin/video/stream_info.h @@ -0,0 +1,100 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin { + +class PANGOLIN_EXPORT StreamInfo +{ +public: + inline StreamInfo() + : fmt(PixelFormatFromString("GRAY8")) {} + + inline StreamInfo(PixelFormat fmt, const Image img_offset ) + : fmt(fmt), img_offset(img_offset) {} + + inline StreamInfo(PixelFormat fmt, size_t w, size_t h, size_t pitch, unsigned char* offset = 0) + : fmt(fmt), img_offset(offset,w,h,pitch) {} + + //! Format representing how image is layed out in memory + inline PixelFormat PixFormat() const { return fmt; } + + //! Image width in pixels + inline size_t Width() const { return img_offset.w; } + + //! Image height in pixels + inline size_t Height() const { return img_offset.h; } + + inline double Aspect() const { return (double)Width() / (double)Height(); } + + //! Pitch: Number of bytes between one image row and the next + inline size_t Pitch() const { return img_offset.pitch; } + + //! Number of contiguous bytes in memory that the image occupies + inline size_t RowBytes() const { + // Row size without padding + return (fmt.bpp*img_offset.w)/8; + } + + //! Returns true iff image contains padding or stridded access + //! This implies that the image data is not contiguous in memory. + inline bool IsPitched() const { + return Pitch() != RowBytes(); + } + + //! Number of contiguous bytes in memory that the image occupies + inline size_t SizeBytes() const { + return (img_offset.h-1) * img_offset.pitch + RowBytes(); + } + + //! Offset in bytes relative to start of frame buffer + inline unsigned char* Offset() const { return img_offset.ptr; } + + //! Return Image wrapper around raw base pointer + inline Image StreamImage(unsigned char* base_ptr) const { + Image img = img_offset; + img.ptr += (size_t)base_ptr; + return img; + } + + //! Return Image wrapper around raw base pointer + inline const Image StreamImage(const unsigned char* base_ptr) const { + Image img = img_offset; + img.ptr += (size_t)base_ptr; + return img; + } + +protected: + PixelFormat fmt; + Image img_offset; +}; + +} diff --git a/include/pangolin/video/video.h b/include/pangolin/video/video.h new file mode 100644 index 0000000..bf66f75 --- /dev/null +++ b/include/pangolin/video/video.h @@ -0,0 +1,257 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +// Pangolin video supports various cameras and file formats through +// different 3rd party libraries. +// +// Video URI's take the following form: +// scheme:[param1=value1,param2=value2,...]//device +// +// scheme = file | files | pango | shmem | dc1394 | uvc | v4l | openni2 | +// openni | depthsense | pleora | teli | mjpeg | test | +// thread | convert | debayer | split | join | shift | mirror | unpack +// +// file/files - read one or more streams from image file(s) / video +// e.g. "files://~/data/dataset/img_*.jpg" +// e.g. "files://~/data/dataset/img_[left,right]_*.pgm" +// e.g. "files:///home/user/sequence/foo%03d.jpeg" +// +// e.g. "file:[fmt=GRAY8,size=640x480]///home/user/raw_image.bin" +// e.g. "file:[realtime=1]///home/user/video/movie.pango" +// e.g. "file:[stream=1]///home/user/video/movie.avi" +// +// dc1394 - capture video through a firewire camera +// e.g. "dc1394:[fmt=RGB24,size=640x480,fps=30,iso=400,dma=10]//0" +// e.g. "dc1394:[fmt=FORMAT7_1,size=640x480,pos=2+2,iso=400,dma=10]//0" +// e.g. "dc1394:[fmt=FORMAT7_3,deinterlace=1]//0" +// +// v4l - capture video from a Video4Linux (USB) camera (normally YUVY422 format) +// method=mmap|read|userptr +// e.g. "v4l:///dev/video0" +// e.g. "v4l[method=mmap]:///dev/video0" +// +// openni2 - capture video / depth from OpenNI2 SDK (Kinect / Xtrion etc) +// imgN=grey|rgb|ir|ir8|ir24|depth|reg_depth +// e.g. "openni2://' +// e.g. "openni2:[img1=rgb,img2=depth,coloursync=true]//" +// e.g. "openni2:[img1=depth,close=closerange,holefilter=true]//" +// e.g. "openni2:[size=320x240,fps=60,img1=ir]//" +// +// openni - capture video / depth from OpenNI 1.0 SDK (Kinect / Xtrion etc) +// Sensor modes containing '8' will truncate to 8-bits. +// Sensor modes containing '+' explicitly enable IR illuminator +// imgN=rgb|ir|ir8|ir+|ir8+|depth|reg_depth +// autoexposure=true|false +// e.g. "openni://' +// e.g. "openni:[img1=rgb,img2=depth]//" +// e.g. "openni:[size=320x240,fps=60,img1=ir]//" +// +// depthsense - capture video / depth from DepthSense SDK. +// DepthSenseViewer can be used to alter capture settings. +// imgN=depth|rgb +// sizeN=QVGA|320x240|... +// fpsN=25|30|60|... +// e.g. "depthsense://" +// e.g. "depthsense:[img1=depth,img2=rgb]//" +// +// pleora - USB 3 vision cameras accepts any option in the same format reported by eBUSPlayer +// e.g. for lightwise cameras: "pleora:[size=512x256,pos=712x512,sn=00000274,ExposureTime=10000,PixelFormat=Mono12p,AcquisitionMode=SingleFrame,TriggerSource=Line0,TriggerMode=On]//" +// e.g. for toshiba cameras: "pleora:[size=512x256,pos=712x512,sn=0300056,PixelSize=Bpp12,ExposureTime=10000,ImageFormatSelector=Format1,BinningHorizontal=2,BinningVertical=2]//" +// e.g. toshiba alternated "pleora:[UserSetSelector=UserSet1,ExposureTime=10000,PixelSize=Bpp12,Width=1400,OffsetX=0,Height=1800,OffsetY=124,LineSelector=Line1,LineSource=ExposureActive,LineSelector=Line2,LineSource=Off,LineModeAll=6,LineInverterAll=6,UserSetSave=Execute, +// UserSetSelector=UserSet2,PixelSize=Bpp12,Width=1400,OffsetX=1048,Height=1800,OffsetY=124,ExposureTime=10000,LineSelector=Line1,LineSource=Off,LineSelector=Line2,LineSource=ExposureActive,LineModeAll=6,LineInverterAll=6,UserSetSave=Execute, +// SequentialShutterIndex=1,SequentialShutterEntry=1,SequentialShutterIndex=2,SequentialShutterEntry=2,SequentialShutterTerminateAt=2,SequentialShutterEnable=On,,AcquisitionFrameRateControl=Manual,AcquisitionFrameRate=70]//" +// +// thread - thread that continuously pulls from the child streams so that data in, unpacking, debayering etc can be decoupled from the main application thread +// e.g. thread://pleora:// +// e.g. thread://unpack://pleora:[PixelFormat=Mono12p]// +// +// convert - use FFMPEG to convert between video pixel formats +// e.g. "convert:[fmt=RGB24]//v4l:///dev/video0" +// e.g. "convert:[fmt=GRAY8]//v4l:///dev/video0" +// +// mjpeg - capture from (possibly networked) motion jpeg stream using FFMPEG +// e.g. "mjpeg://http://127.0.0.1/?action=stream" +// +// debayer - debayer an input video stream +// e.g. "debayer:[tile="BGGR",method="downsample"]//v4l:///dev/video0 +// +// split - split an input video into a one or more streams based on Region of Interest / memory specification +// roiN=X+Y+WxH +// memN=Offset:WxH:PitchBytes:Format +// e.g. "split:[roi1=0+0+640x480,roi2=640+0+640x480]//files:///home/user/sequence/foo%03d.jpeg" +// e.g. "split:[mem1=307200:640x480:1280:GRAY8,roi2=640+0+640x480]//files:///home/user/sequence/foo%03d.jpeg" +// e.g. "split:[stream1=2,stream2=1]//pango://video.pango" +// +// join - join streams +// e.g. "join:[sync_tolerance_us=100, sync_continuously=true]//{pleora:[sn=00000274]//}{pleora:[sn=00000275]//}" +// +// test - output test video sequence +// e.g. "test://" +// e.g. "test:[size=640x480,fmt=RGB24]//" + +#include +#include +#include +#include + +namespace pangolin +{ + +//! Open Video Interface from string specification (as described in this files header) +PANGOLIN_EXPORT +std::unique_ptr OpenVideo(const std::string& uri); + +//! Open Video Interface from Uri specification +PANGOLIN_EXPORT +std::unique_ptr OpenVideo(const Uri& uri); + +//! Open VideoOutput Interface from string specification (as described in this files header) +PANGOLIN_EXPORT +std::unique_ptr OpenVideoOutput(const std::string& str_uri); + +//! Open VideoOutput Interface from Uri specification +PANGOLIN_EXPORT +std::unique_ptr OpenVideoOutput(const Uri& uri); + +//! Create vector of matching interfaces either through direct cast or filter interface. +template +std::vector FindMatchingVideoInterfaces( VideoInterface& video ) +{ + std::vector matches; + + T* vid = dynamic_cast(&video); + if(vid) { + matches.push_back(vid); + } + + VideoFilterInterface* vidf = dynamic_cast(&video); + if(vidf) { + std::vector fmatches = vidf->FindMatchingStreams(); + matches.insert(matches.begin(), fmatches.begin(), fmatches.end()); + } + + return matches; +} + +template +T* FindFirstMatchingVideoInterface( VideoInterface& video ) +{ + T* vid = dynamic_cast(&video); + if(vid) { + return vid; + } + + VideoFilterInterface* vidf = dynamic_cast(&video); + if(vidf) { + std::vector fmatches = vidf->FindMatchingStreams(); + if(fmatches.size()) { + return fmatches[0]; + } + } + + return 0; +} + +inline +picojson::value GetVideoFrameProperties(VideoInterface* video) +{ + VideoPropertiesInterface* pi = dynamic_cast(video); + VideoFilterInterface* fi = dynamic_cast(video); + + if(pi) { + return pi->FrameProperties(); + }else if(fi){ + if(fi->InputStreams().size() == 1) { + return GetVideoFrameProperties(fi->InputStreams()[0]); + }else if(fi->InputStreams().size() > 0){ + picojson::value streams; + + for(size_t i=0; i< fi->InputStreams().size(); ++i) { + const picojson::value dev_props = GetVideoFrameProperties(fi->InputStreams()[i]); + if(dev_props.contains("streams")) { + const picojson::value& dev_streams = dev_props["streams"]; + for(size_t i=0; i < dev_streams.size(); ++i) { + streams.push_back(dev_streams[i]); + } + }else{ + streams.push_back(dev_props); + } + } + + if(streams.size() > 1) { + picojson::value json = streams[0]; + json["streams"] = streams; + return json; + }else{ + return streams[0]; + } + } + } + return picojson::value(); +} + +inline +picojson::value GetVideoDeviceProperties(VideoInterface* video) +{ + VideoPropertiesInterface* pi = dynamic_cast(video); + VideoFilterInterface* fi = dynamic_cast(video); + + if(pi) { + return pi->DeviceProperties(); + }else if(fi){ + if(fi->InputStreams().size() == 1) { + return GetVideoDeviceProperties(fi->InputStreams()[0]); + }else if(fi->InputStreams().size() > 0){ + picojson::value streams; + + for(size_t i=0; i< fi->InputStreams().size(); ++i) { + const picojson::value dev_props = GetVideoDeviceProperties(fi->InputStreams()[i]); + if(dev_props.contains("streams")) { + const picojson::value& dev_streams = dev_props["streams"]; + for(size_t i=0; i < dev_streams.size(); ++i) { + streams.push_back(dev_streams[i]); + } + }else{ + streams.push_back(dev_props); + } + } + + if(streams.size() > 1) { + picojson::value json = streams[0]; + json["streams"] = streams; + return json; + }else{ + return streams[0]; + } + } + } + return picojson::value(); +} + +} diff --git a/include/pangolin/video/video_exception.h b/include/pangolin/video/video_exception.h new file mode 100644 index 0000000..60208c2 --- /dev/null +++ b/include/pangolin/video/video_exception.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace pangolin { + +struct PANGOLIN_EXPORT VideoException : std::exception +{ + VideoException(std::string str) : desc(str) {} + VideoException(std::string str, std::string detail) { + desc = str + "\n\t" + detail; + } + ~VideoException() throw() {} + const char* what() const throw() { return desc.c_str(); } + std::string desc; +}; + +struct PANGOLIN_EXPORT VideoExceptionNoKnownHandler : public VideoException +{ + VideoExceptionNoKnownHandler(const std::string& scheme) + : VideoException("No known video handler for URI '" + scheme + "'") + { + } +}; + +} + diff --git a/include/pangolin/video/video_input.h b/include/pangolin/video/video_input.h new file mode 100644 index 0000000..b496241 --- /dev/null +++ b/include/pangolin/video/video_input.h @@ -0,0 +1,139 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace pangolin +{ + +struct PANGOLIN_EXPORT VideoInput + : public VideoInterface, + public VideoFilterInterface +{ + ///////////////////////////////////////////////////////////// + // VideoInterface Methods + ///////////////////////////////////////////////////////////// + + size_t SizeBytes() const override; + const std::vector& Streams() const override; + void Start() override; + void Stop() override; + bool GrabNext( unsigned char* image, bool wait = true ) override; + bool GrabNewest( unsigned char* image, bool wait = true ) override; + + ///////////////////////////////////////////////////////////// + // VideoFilterInterface Methods + ///////////////////////////////////////////////////////////// + + std::vector& InputStreams() override + { + return videos; + } + + ///////////////////////////////////////////////////////////// + // VideoInput Methods + ///////////////////////////////////////////////////////////// + + VideoInput(); + VideoInput(const std::string &input_uri, const std::string &output_uri = "pango:[buffer_size_mb=100]//video_log.pango"); + ~VideoInput(); + + void Open(const std::string &input_uri, const std::string &output_uri = "pango:[buffer_size_mb=100]//video_log.pango"); + void Close(); + + // experimental - not stable + bool Grab( unsigned char* buffer, std::vector >& images, bool wait = true, bool newest = false); + + // Return details of first stream + unsigned int Width() const { + return (unsigned int)Streams()[0].Width(); + } + unsigned int Height() const { + return (unsigned int)Streams()[0].Height(); + } + PixelFormat PixFormat() const { + return Streams()[0].PixFormat(); + } + const Uri& VideoUri() const { + return uri_input; + } + + void Reset() { + Close(); + Open(uri_input.full_uri, uri_output.full_uri); + } + + // Return pointer to inner video class as VideoType + template + VideoType* Cast() { + return dynamic_cast(video_src.get()); + } + + const std::string& LogFilename() const; + std::string& LogFilename(); + + // Switch to live video and record output to file + void Record(); + + // Switch to live video and record a single frame + void RecordOneFrame(); + + // Specify that one in n frames are logged to file. Default is 1. + void SetTimelapse(size_t one_in_n_frames); + + // True iff grabbed live frames are being logged to file + bool IsRecording() const; + +protected: + void InitialiseRecorder(); + + Uri uri_input; + Uri uri_output; + + std::unique_ptr video_src; + std::unique_ptr video_recorder; + + // Use to store either video_src or video_file for VideoFilterInterface, + // depending on which is active + std::vector videos; + + int buffer_size_bytes; + + int frame_num; + size_t record_frame_skip; + + bool record_once; + bool record_continuous; +}; + +// VideoInput subsumes the previous VideoRecordRepeat class. +typedef VideoInput VideoRecordRepeat; + +} diff --git a/include/pangolin/video/video_interface.h b/include/pangolin/video/video_interface.h new file mode 100644 index 0000000..40d8abb --- /dev/null +++ b/include/pangolin/video/video_interface.h @@ -0,0 +1,172 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include +#include + +#define PANGO_HAS_TIMING_DATA "has_timing_data" +#define PANGO_HOST_RECEPTION_TIME_US "host_reception_time_us" +#define PANGO_CAPTURE_TIME_US "capture_time_us" +#define PANGO_EXPOSURE_US "exposure_us" +#define PANGO_GAMMA "gamma" +#define PANGO_ANALOG_GAIN "analog_gain" +#define PANGO_ANALOG_BLACK_LEVEL "analog_black_level" +#define PANGO_SENSOR_TEMPERATURE_C "sensor_temperature_C" +#define PANGO_ESTIMATED_CENTER_CAPTURE_TIME_US "estimated_center_capture_time_us" +#define PANGO_FRAME_COUNTER "frame_counter" + +namespace pangolin { + +//! Interface to video capture sources +struct PANGOLIN_EXPORT VideoInterface +{ + virtual ~VideoInterface() {} + + //! Required buffer size to store all frames + virtual size_t SizeBytes() const = 0; + + //! Get format and dimensions of all video streams + virtual const std::vector& Streams() const = 0; + + //! Start Video device + virtual void Start() = 0; + + //! Stop Video device + virtual void Stop() = 0; + + //! Copy the next frame from the camera to image. + //! Optionally wait for a frame if one isn't ready + //! Returns true iff image was copied + virtual bool GrabNext( unsigned char* image, bool wait = true ) = 0; + + //! Copy the newest frame from the camera to image + //! discarding all older frames. + //! Optionally wait for a frame if one isn't ready + //! Returns true iff image was copied + virtual bool GrabNewest( unsigned char* image, bool wait = true ) = 0; +}; + +//! Interface to GENICAM video capture sources +struct PANGOLIN_EXPORT GenicamVideoInterface +{ + virtual ~GenicamVideoInterface() {} + + virtual std::string GetParameter(const std::string& name) = 0; + + virtual void SetParameter(const std::string& name, const std::string& value) = 0; + +}; + +struct PANGOLIN_EXPORT BufferAwareVideoInterface +{ + virtual ~BufferAwareVideoInterface() {} + + //! Returns number of available frames + virtual uint32_t AvailableFrames() const = 0; + + //! Drops N frames in the queue starting from the oldest + //! returns false if less than n frames arae available + virtual bool DropNFrames(uint32_t n) = 0; +}; + +struct PANGOLIN_EXPORT VideoPropertiesInterface +{ + virtual ~VideoPropertiesInterface() {} + + //! Access JSON properties of device + virtual const picojson::value& DeviceProperties() const = 0; + + //! Access JSON properties of most recently captured frame + virtual const picojson::value& FrameProperties() const = 0; +}; + +enum UvcRequestCode { + UVC_RC_UNDEFINED = 0x00, + UVC_SET_CUR = 0x01, + UVC_GET_CUR = 0x81, + UVC_GET_MIN = 0x82, + UVC_GET_MAX = 0x83, + UVC_GET_RES = 0x84, + UVC_GET_LEN = 0x85, + UVC_GET_INFO = 0x86, + UVC_GET_DEF = 0x87 +}; + +struct PANGOLIN_EXPORT VideoFilterInterface +{ + virtual ~VideoFilterInterface() {} + + template + std::vector FindMatchingStreams() + { + std::vector matches; + std::vector children = InputStreams(); + for(size_t c=0; c < children.size(); ++c) { + T* concrete_video = dynamic_cast(children[c]); + if(concrete_video) { + matches.push_back(concrete_video); + }else{ + VideoFilterInterface* filter_video = dynamic_cast(children[c]); + if(filter_video) { + std::vector child_matches = filter_video->FindMatchingStreams(); + matches.insert(matches.end(), child_matches.begin(), child_matches.end()); + } + } + } + return matches; + } + + virtual std::vector& InputStreams() = 0; +}; + +struct PANGOLIN_EXPORT VideoUvcInterface +{ + virtual ~VideoUvcInterface() {} + virtual int IoCtrl(uint8_t unit, uint8_t ctrl, unsigned char* data, int len, UvcRequestCode req_code) = 0; +}; + +struct PANGOLIN_EXPORT VideoPlaybackInterface +{ + virtual ~VideoPlaybackInterface() {} + + /// Return monotonic id of current frame + virtual size_t GetCurrentFrameId() const = 0; + + /// Return total number of frames to be captured from device, + /// or 0 if unknown. + virtual size_t GetTotalFrames() const = 0; + + /// Return frameid on success, or next frame on failure + virtual size_t Seek(size_t frameid) = 0; +}; + +} diff --git a/include/pangolin/video/video_output.h b/include/pangolin/video/video_output.h new file mode 100644 index 0000000..6c9c07f --- /dev/null +++ b/include/pangolin/video/video_output.h @@ -0,0 +1,78 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011-2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +// Pangolin video output supports various formats using +// different 3rd party libraries. (Only one right now) +// +// VideoOutput URI's take the following form: +// scheme:[param1=value1,param2=value2,...]//device +// +// scheme = ffmpeg +// +// ffmpeg - encode to compressed file using ffmpeg +// fps : fps to embed in encoded file. +// bps : bits per second +// unique_filename : append unique suffix if file already exists +// +// e.g. ffmpeg://output_file.avi +// e.g. ffmpeg:[fps=30,bps=1000000,unique_filename]//output_file.avi + +#include +#include +#include + +namespace pangolin +{ + +//! VideoOutput wrap to generically construct instances of VideoOutputInterface. +class PANGOLIN_EXPORT VideoOutput : public VideoOutputInterface +{ +public: + VideoOutput(); + VideoOutput(const std::string& uri); + ~VideoOutput(); + + bool IsOpen() const; + void Open(const std::string& uri); + void Close(); + + const std::vector& Streams() const override; + + void SetStreams(const std::vector& streams, const std::string& uri = "", const picojson::value& properties = picojson::value() ) override; + + int WriteStreams(const unsigned char* data, const picojson::value& frame_properties = picojson::value() ) override; + + bool IsPipe() const override; + +protected: + Uri uri; + std::unique_ptr recorder; +}; + +} diff --git a/include/pangolin/video/video_output_interface.h b/include/pangolin/video/video_output_interface.h new file mode 100644 index 0000000..efbec4d --- /dev/null +++ b/include/pangolin/video/video_output_interface.h @@ -0,0 +1,52 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011-2013 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace pangolin { + +//! Interface to video recording destinations +struct PANGOLIN_EXPORT VideoOutputInterface +{ + virtual ~VideoOutputInterface() {} + + //! Get format and dimensions of all video streams + virtual const std::vector& Streams() const = 0; + + virtual void SetStreams(const std::vector& streams, const std::string& uri ="", const picojson::value& properties = picojson::value() ) = 0; + + virtual int WriteStreams(const unsigned char* data, const picojson::value& frame_properties = picojson::value() ) = 0; + + virtual bool IsPipe() const = 0; +}; + +} diff --git a/include/pangolin/video/video_record_repeat.h b/include/pangolin/video/video_record_repeat.h new file mode 100644 index 0000000..79e8571 --- /dev/null +++ b/include/pangolin/video/video_record_repeat.h @@ -0,0 +1,31 @@ +/* This file is part of the Pangolin Project. + * http://github.com/stevenlovegrove/Pangolin + * + * Copyright (c) 2011 Steven Lovegrove + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +// VideoInput subsumes the previous VideoRecordRepeat class. +#include diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000..d88d40c --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories( ${PROJECT_SOURCE_DIR}/include) +include_directories( "/usr/include/eigen3" ) +find_package(pybind11) + + +# pybind11 (version 2.2.1) +LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/external/pybind11/tools) +include_directories(${PROJECT_SOURCE_DIR}/external/pybind11/include) +include(pybind11Tools) + + +pybind11_add_module( pangolin pangolin.cpp ) +SET_TARGET_PROPERTIES(pangolin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}) +target_link_libraries( pangolin PRIVATE ${LIBRARY_NAME}) \ No newline at end of file diff --git a/python/blank.hpp b/python/blank.hpp new file mode 100644 index 0000000..514f681 --- /dev/null +++ b/python/blank.hpp @@ -0,0 +1,21 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareParams(py::module & m) { + + py::class_> cls(m, "Params"); + + cls.def(py::init<>()); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/contrib.hpp b/python/contrib.hpp new file mode 100644 index 0000000..873ea3f --- /dev/null +++ b/python/contrib.hpp @@ -0,0 +1,258 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + + +namespace pangolin { + +namespace { + +void DrawPoints(py::array_t points) { + auto r = points.unchecked<2>(); + glBegin(GL_POINTS); + for (ssize_t i = 0; i < r.shape(0); ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + } + glEnd(); +} + +void DrawPoints(py::array_t points, py::array_t colors) { + auto r = points.unchecked<2>(); + auto rc = colors.unchecked<2>(); + + glBegin(GL_POINTS); + for (ssize_t i = 0; i < r.shape(0); ++i) { + glColor3f(rc(i, 0), rc(i, 1), rc(i, 2)); + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + } + glEnd(); +} + + +void DrawCameras(py::array_t cameras, float w=1.0, float h_ratio=0.75, float z_ratio=0.6) { + auto r = cameras.unchecked<3>(); + + float h = w * h_ratio; + float z = w * z_ratio; + + for (ssize_t i = 0; i < r.shape(0); ++i) { + glPushMatrix(); + // glMultMatrixd(r.data(i, 0, 0)); + glMultTransposeMatrixd(r.data(i, 0, 0)); + + glBegin(GL_LINES); + glVertex3f(0,0,0); + glVertex3f(w,h,z); + glVertex3f(0,0,0); + glVertex3f(w,-h,z); + glVertex3f(0,0,0); + glVertex3f(-w,-h,z); + glVertex3f(0,0,0); + glVertex3f(-w,h,z); + + glVertex3f(w,h,z); + glVertex3f(w,-h,z); + + glVertex3f(-w,h,z); + glVertex3f(-w,-h,z); + + glVertex3f(-w,h,z); + glVertex3f(w,h,z); + + glVertex3f(-w,-h,z); + glVertex3f(w,-h,z); + glEnd(); + + glPopMatrix(); + } +} + +void DrawCamera(py::array_t camera, float w=1.0, float h_ratio=0.75, float z_ratio=0.6) { + auto r = camera.unchecked<2>(); + + float h = w * h_ratio; + float z = w * z_ratio; + + glPushMatrix(); + // glMultMatrixd(r.data(0, 0)); + glMultTransposeMatrixd(r.data(0, 0)); + + glBegin(GL_LINES); + glVertex3f(0,0,0); + glVertex3f(w,h,z); + glVertex3f(0,0,0); + glVertex3f(w,-h,z); + glVertex3f(0,0,0); + glVertex3f(-w,-h,z); + glVertex3f(0,0,0); + glVertex3f(-w,h,z); + + glVertex3f(w,h,z); + glVertex3f(w,-h,z); + + glVertex3f(-w,h,z); + glVertex3f(-w,-h,z); + + glVertex3f(-w,h,z); + glVertex3f(w,h,z); + + glVertex3f(-w,-h,z); + glVertex3f(w,-h,z); + glEnd(); + + glPopMatrix(); +} + + +void DrawLine(py::array_t points, float point_size=0) { + auto r = points.unchecked<2>(); + glBegin(GL_LINES); + for (ssize_t i = 0; i < r.shape(0)-1; ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + glVertex3d(r(i+1, 0), r(i+1, 1), r(i+1, 2)); + } + glEnd(); + + if(point_size > 0) { + glPointSize(point_size); + glBegin(GL_POINTS); + for (ssize_t i = 0; i < r.shape(0); ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + } + glEnd(); + } +} + + + +void DrawLines(py::array_t points, float point_size=0) { + auto r = points.unchecked<2>(); + + glBegin(GL_LINES); + for (ssize_t i = 0; i < r.shape(0); ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + glVertex3d(r(i, 3), r(i, 4), r(i, 5)); + } + glEnd(); + + if(point_size > 0) { + glPointSize(point_size); + glBegin(GL_POINTS); + for (ssize_t i = 0; i < r.shape(0); ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + glVertex3d(r(i, 3), r(i, 4), r(i, 5)); + } + glEnd(); + } +} + + +void DrawLines(py::array_t points, py::array_t points2, float point_size=0) { + auto r = points.unchecked<2>(); + auto r2 = points2.unchecked<2>(); + glBegin(GL_LINES); + for (ssize_t i = 0; i < std::min(r.shape(0), r2.shape(0)); ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + glVertex3d(r2(i, 0), r2(i, 1), r2(i, 2)); + } + glEnd(); + + if(point_size > 0) { + glPointSize(point_size); + glBegin(GL_POINTS); + for (ssize_t i = 0; i < std::min(r.shape(0), r2.shape(0)); ++i) { + glVertex3d(r(i, 0), r(i, 1), r(i, 2)); + glVertex3d(r2(i, 0), r2(i, 1), r2(i, 2)); + } + glEnd(); + } +} + + +void DrawBoxes(py::array_t cameras, py::array_t sizes) { + auto r = cameras.unchecked<3>(); + auto rs = sizes.unchecked<2>(); + + for (ssize_t i = 0; i < r.shape(0); ++i) { + glPushMatrix(); + // glMultMatrixd(r.data(i, 0, 0)); + glMultTransposeMatrixd(r.data(i, 0, 0)); + + float w = *rs.data(i, 0) / 2.0; // w/2 + float h = *rs.data(i, 1) / 2.0; + float z = *rs.data(i, 2) / 2.0; + + glBegin(GL_LINES); + glVertex3f(-w, -h, -z); + glVertex3f(w, -h, -z); + glVertex3f(-w, -h, -z); + glVertex3f(-w, h, -z); + glVertex3f(-w, -h, -z); + glVertex3f(-w, -h, z); + + glVertex3f(w, h, -z); + glVertex3f(-w, h, -z); + glVertex3f(w, h, -z); + glVertex3f(w, -h, -z); + glVertex3f(w, h, -z); + glVertex3f(w, h, z); + + glVertex3f(-w, h, z); + glVertex3f(w, h, z); + glVertex3f(-w, h, z); + glVertex3f(-w, -h, z); + glVertex3f(-w, h, z); + glVertex3f(-w, h, -z); + + glVertex3f(w, -h, z); + glVertex3f(-w, -h, z); + glVertex3f(w, -h, z); + glVertex3f(w, h, z); + glVertex3f(w, -h, z); + glVertex3f(w, -h, -z); + glEnd(); + + glPopMatrix(); + } +} + + +// TODO: +// draw surface + +} + + + + +void declareContrib(py::module & m) { + + m.def("DrawPoints", (void (*) (py::array_t)) &DrawPoints, + "points"_a); + m.def("DrawPoints", (void (*) (py::array_t, py::array_t)) &DrawPoints, + "points"_a, "colors"_a); + + m.def("DrawLine", (void (*) (py::array_t, float)) &DrawLine, + "points"_a, "point_size"_a=0); + m.def("DrawLines", (void (*) (py::array_t, float)) &DrawLines, + "points"_a, "point_size"_a=0); + m.def("DrawLines", (void (*) (py::array_t, py::array_t, float)) &DrawLines, + "points"_a, "points2"_a, "point_size"_a=0); + + m.def("DrawCameras", &DrawCameras, + "poses"_a, "w"_a=1.0, "h_ratio"_a=0.75, "z_ratio"_a=0.6); + m.def("DrawCamera", &DrawCamera, + "poses"_a, "w"_a=1.0, "h_ratio"_a=0.75, "z_ratio"_a=0.6); + + m.def("DrawBoxes", &DrawBoxes, + "poses"_a, "sizes"_a); + +} + +} \ No newline at end of file diff --git a/python/display/attach.hpp b/python/display/attach.hpp new file mode 100644 index 0000000..0a3bc08 --- /dev/null +++ b/python/display/attach.hpp @@ -0,0 +1,42 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareAttach(py::module & m) { + + py::enum_(m, "Unit") + .value("Fraction", Unit::Fraction) + .value("Pixel", Unit::Pixel) + .value("ReversePixel", Unit::ReversePixel) + .export_values(); + + + py::class_>(m, "Attach") + .def(py::init<>(), "Attach to Left/Bottom edge") + .def(py::init(), "General constructor") + .def(py::init(), "Specify relative position in range [0,1].") + + .def_static("Pix", &Attach::Pix, "p"_a, + "Specify absolute position from leftmost / bottom-most edge.") + + .def_static("ReversePix", &Attach::ReversePix, "p"_a, + "Specify absolute position from rightmost / topmost edge.") + + .def_static("Frac", &Attach::Frac, "frac"_a, + "Specify relative position in range [0,1].") + + .def_readwrite("unit", &Attach::unit) + .def_readwrite("p", &Attach::p) + ; + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/display.hpp b/python/display/display.hpp new file mode 100644 index 0000000..eb5ce89 --- /dev/null +++ b/python/display/display.hpp @@ -0,0 +1,118 @@ +#include +#include + +#include +#include +//#include +#include +#include +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareDisplay(py::module & m) { + + //m.def_readonly("PARAM_DISPLAYNAME", &PARAM_DISPLAYNAME) + //m.def_readonly("PARAM_DOUBLEBUFFER", &PARAM_DOUBLEBUFFER) + //m.def_readonly("PARAM_SAMPLE_BUFFERS", &PARAM_SAMPLE_BUFFERS) + //m.def_readonly("PARAM_SAMPLES", &PARAM_SAMPLES) + //m.def_readonly("PARAM_HIGHRES", &PARAM_HIGHRES) + + m.def("BindToContext", &BindToContext, "name"_a, + py::return_value_policy::reference, + "Initialise OpenGL window (determined by platform) and bind context."); + + m.def("CreateWindowAndBind", &CreateWindowAndBind, "window_title"_a, "w"_a = 640, "h"_a = 480, "params"_a = Params(), + py::return_value_policy::reference, + "Initialise OpenGL window (determined by platform) and bind context."); + + m.def("GetBoundWindow", &GetBoundWindow, "Return pointer to current Pangolin Window context, or nullptr if none bound."); + + m.def("DestroyWindow", &DestroyWindow, "window_title"_a); + + m.def("FinishFrame", &FinishFrame, "Perform any post rendering, event processing and frame swapping."); + + m.def("Quit", &Quit, "Request that the window close."); + + m.def("QuitAll", &QuitAll, "Request that all windows close."); + + m.def("ShouldQuit", &ShouldQuit, "Returns true if user has requested to close OpenGL window."); + + m.def("HadInput", &HadInput, "Returns true if user has interacted with the window since this was last called."); + + m.def("HasResized", &HasResized, "Returns true if user has resized the window."); + + m.def("RenderViews", &RenderViews, "Renders any views with default draw methods."); + + m.def("PostRender", &PostRender, "Perform any post render events, such as screen recording."); + + m.def("RegisterKeyPressCallback", &RegisterKeyPressCallback, "key"_a, "func"_a, + "Request to be notified via functor when key is pressed."); // py::keep_alive<1, 2>() + + m.def("SaveWindowOnRender", &SaveWindowOnRender, "filename_prefix"_a, "Save window contents to image."); + + m.def("SaveFramebuffer", &SaveFramebuffer, "prefix"_a, "v"_a); //v : Viewport + + + // namespace process + m.def_submodule("process") + + .def("Keyboard", &process::Keyboard, "key"_a, "x"_a, "y"_a) + + .def("KeyboardUp", &process::KeyboardUp, "key"_a, "x"_a, "y"_a) + + .def("SpecialFunc", &process::SpecialFunc, "key"_a, "x"_a, "y"_a) + + .def("SpecialFuncUp", &process::SpecialFuncUp, "key"_a, "x"_a, "y"_a) + + .def("Resize", &process::Resize, "width"_a, "height"_a, "Tell pangolin base window size has changed") + + .def("Display", &process::Display) + + .def("Mouse", &process::Mouse, "button"_a, "state"_a, "x"_a, "y"_a) + + .def("MouseMotion", &process::MouseMotion, "x"_a, "y"_a) + + .def("PassiveMouseMotion", &process::PassiveMouseMotion, "x"_a, "y"_a) + + .def("Scroll", &process::Scroll, "x"_a, "y"_a) + + .def("Zoom", &process::Zoom, "m"_a) + + .def("Rotate", &process::Rotate, "r"_a) + + .def("SubpixMotion", &process::SubpixMotion, "x"_a, "y"_a, "pressure"_a, "rotation"_a, "tiltx"_a, "tilty"_a) + + .def("SpecialInput", &process::SpecialInput, "inType"_a, "x"_a, "y"_a, "p1"_a, "p2"_a, "p3"_a, "p4"_a) + ; + + + m.def("DisplayBase", &DisplayBase, py::return_value_policy::reference, + "Retrieve 'base' display, corresponding to entire window."); + + m.def("Display", &Display, "name"_a, py::return_value_policy::reference, + "Create or retrieve named display managed by pangolin (automatically deleted)."); + + m.def("CreateDisplay", &CreateDisplay, py::return_value_policy::reference, + "Create unnamed display managed by pangolin (automatically deleted)."); + + + m.def("ToggleFullscreen", &ToggleFullscreen, "Switch between windowed and fullscreen mode."); + + m.def("SetFullscreen", &SetFullscreen, "fullscreen"_a = true, "Switch windows/fullscreenmode = fullscreen."); + + m.def("ToggleConsole", &ToggleConsole, "Toggle display of Pangolin console"); + + // not implemented + //m.def("LaunchUserApp", &LaunchUserApp, "app"_a, "Launch users derived UserApp, controlling OpenGL event loop."); + //py::class_> cls(m, "ToggleViewFunctor"); + + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/image_view.hpp b/python/display/image_view.hpp new file mode 100644 index 0000000..06ec1fb --- /dev/null +++ b/python/display/image_view.hpp @@ -0,0 +1,45 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareImageView(py::module & m) { + + py::class_, View, ImageViewHandler>(m, "ImageView") + .def(py::init<>()) + + .def("Render", &ImageView::Render) + .def("Mouse", &ImageView::Mouse, "view"_a, "button"_a, "x"_a, "y"_a, "pressed"_a, "button_state"_a) + .def("Keyboard", &ImageView::Keyboard, "view"_a, "key"_a, "x"_a, "y"_a, "pressed"_a) + .def("Tex", &ImageView::Tex) + + .def("SetImage", (ImageView& (ImageView::*) (void*, size_t, size_t, size_t, GlPixFormat, bool)) + &ImageView::SetImage, "ptr"_a, "w"_a, "h"_a, "pitch"_a, "img_fmt"_a, "delayed_upload"_a = false) + .def("SetImage", (ImageView& (ImageView::*) (const Image&, const GlPixFormat&, bool)) + &ImageView::SetImage, "img"_a, "glfmt"_a, "delayed_upload"_a = false) + .def("SetImage", (ImageView& (ImageView::*) (const TypedImage&, bool)) &ImageView::SetImage, + "img"_a, "delayed_upload"_a = false) + .def("SetImage", (ImageView& (ImageView::*) (const GlTexture&)) &ImageView::SetImage, "texture"_a) + + .def("LoadPending", &ImageView::LoadPending) + .def("Clear", &ImageView::Clear) + .def("GetOffsetScale", &ImageView::GetOffsetScale) + .def("MouseReleased", &ImageView::MouseReleased) + .def("MousePressed", &ImageView::MousePressed) + .def("SetRenderOverlay", &ImageView::SetRenderOverlay, "val"_a) + + ; + //py::class_> cls(m, "Params"); + + //cls.def(py::init<>()); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/opengl_render_state.hpp b/python/display/opengl_render_state.hpp new file mode 100644 index 0000000..eb8e06c --- /dev/null +++ b/python/display/opengl_render_state.hpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareOpenGlRenderState(py::module & m) { + + py::enum_(m, "OpenGlStack") + .value("GlModelViewStack", OpenGlStack::GlModelViewStack) + .value("GlProjectionStack", OpenGlStack::GlProjectionStack) + .value("GlTextureStack", OpenGlStack::GlTextureStack) + .export_values(); + + py::enum_(m, "AxisDirection") + .value("AxisNone", AxisDirection::AxisNone) + .value("AxisNegX", AxisDirection::AxisNegX) + .value("AxisX", AxisDirection::AxisX) + .value("AxisNegY", AxisDirection::AxisNegY) + .value("AxisY", AxisDirection::AxisY) + .value("AxisNegZ", AxisDirection::AxisNegZ) + .value("AxisZ", AxisDirection::AxisZ) + .export_values(); + + py::class_>(m, "CameraSpec"); + //.def_readwrite("forward", &CameraSpec::forward) // "error: invalid array assignment" + //.def_readwrite("up", &CameraSpec::up) + //.def_readwrite("right", &CameraSpec::right) + //.def_readwrite("img_up", &CameraSpec::img_up) + //.def_readwrite("img_right", &CameraSpec::img_right); + + + py::class_>(m, "OpenGlMatrix", py::buffer_protocol()) + .def_static("Translate", &OpenGlMatrix::Translate, "x"_a, "y"_a, "z"_a) + .def_static("Scale", &OpenGlMatrix::Scale, "x"_a, "y"_a, "z"_a) + .def_static("RotateX", &OpenGlMatrix::RotateX, "theta_rad"_a) + .def_static("RotateY", &OpenGlMatrix::RotateY, "theta_rad"_a) + .def_static("RotateZ", &OpenGlMatrix::RotateZ, "theta_rad"_a) + + .def_static("ColMajor4x4", &OpenGlMatrix::ColMajor4x4, "col_major_4x4"_a) + .def_static("ColMajor4x4", &OpenGlMatrix::ColMajor4x4, "col_major_4x4"_a) + + //.def(py::init<>()) + .def(py::init([](){ + OpenGlMatrix glmat; + glmat.SetIdentity(); + return glmat; + })) + // EIGEN, TOON, OCULUS +#ifdef USE_EIGEN + .def(py::init&>(), "mat"_a) + .def(py::init&>(), "mat"_a) +#endif + + .def("Load", &OpenGlMatrix::Load) + .def("Multiply", &OpenGlMatrix::Multiply) + .def("SetIdentity", &OpenGlMatrix::SetIdentity) + .def("Transpose", &OpenGlMatrix::Transpose) + .def("Inverse", &OpenGlMatrix::Inverse) + + .def("__getitem__", [](OpenGlMatrix &glmat, std::pair i) { + if (i.first >= 4 || i.second >= 4) + throw py::index_error(); + return glmat(i.first, i.second); + }) + .def("__setitem__", [](OpenGlMatrix &glmat, std::pair i, GLprecision v) { + if (i.first >= 4 || i.second >= 4) + throw py::index_error(); + glmat(i.first, i.second) = v; + }) + + .def_buffer([](OpenGlMatrix &glmat) -> py::buffer_info { + return py::buffer_info( + glmat.m, + {4, 4}, + {sizeof(GLprecision), sizeof(GLprecision) * 4} + ); + }) + + .def("numpy_view", [](py::object &obj) { // doesn't copy data + OpenGlMatrix &glmat = obj.cast(); + return py::array_t({4, 4}, {sizeof(GLprecision), sizeof(GLprecision) * 4}, glmat.m, obj); + }) + + .def_property("m", + [](OpenGlMatrix &glmat) -> const py::array { // getter + return py::array_t({4, 4}, {sizeof(GLprecision), sizeof(GLprecision) * 4}, glmat.m); + }, + [](OpenGlMatrix &glmat, Eigen::Matrix& v) { // setter + for(int r=0; r<4; ++r ) { + for(int c=0; c<4; ++c ) { + glmat.m[c*4+r] = v(r,c); + } + } + } + ) + + .def("__mul__", [](const OpenGlMatrix& lhs, const OpenGlMatrix& rhs) { + return lhs * rhs;}) + ; + + + // something wrong with inhertance + py::class_, OpenGlMatrix>(m, "OpenGlMatrixSpec") + .def_readwrite("type", &OpenGlMatrixSpec::type); + + + py::class_>(m, "OpenGlRenderState") + .def(py::init<>()) + .def(py::init(), + "projection_matrix"_a) + .def(py::init(), + "projection_matrix"_a, "modelview_matrx"_a) + + .def("Apply", &OpenGlRenderState::Apply) + .def("ApplyNView", &OpenGlRenderState::ApplyNView, "view"_a) + .def("ApplyIdentity", &OpenGlRenderState::ApplyIdentity) + .def("SetProjectionMatrix", &OpenGlRenderState::SetProjectionMatrix, "m"_a, + py::return_value_policy::reference_internal) + .def("SetModelViewMatrix", &OpenGlRenderState::SetModelViewMatrix, "m"_a, + py::return_value_policy::reference_internal) + //.def("Set", &OpenGlRenderState::Set, "m"_a, // deprecated + // py::return_value_policy::reference_internal) + + .def("GetViewOffset", (OpenGlMatrix& (OpenGlRenderState::*) (unsigned int)) + &OpenGlRenderState::GetViewOffset, "view"_a, py::return_value_policy::reference_internal) + .def("GetViewOffset", (OpenGlMatrix (OpenGlRenderState::*) (unsigned int) const) + &OpenGlRenderState::GetViewOffset, "view"_a) + + .def("GetProjectionMatrix", (OpenGlMatrix& (OpenGlRenderState::*) ()) + &OpenGlRenderState::GetProjectionMatrix, py::return_value_policy::reference_internal) + .def("GetProjectionMatrix", (OpenGlMatrix (OpenGlRenderState::*) () const) + &OpenGlRenderState::GetProjectionMatrix) + .def("GetProjectionMatrix", (OpenGlMatrix& (OpenGlRenderState::*) (unsigned int)) + &OpenGlRenderState::GetProjectionMatrix, "view"_a, py::return_value_policy::reference_internal) + .def("GetProjectionMatrix", (OpenGlMatrix (OpenGlRenderState::*) (unsigned int) const) + &OpenGlRenderState::GetProjectionMatrix, "view"_a) + + .def("GetModelViewMatrix", (OpenGlMatrix& (OpenGlRenderState::*) ()) + &OpenGlRenderState::GetModelViewMatrix, py::return_value_policy::reference_internal) + .def("GetModelViewMatrix", (OpenGlMatrix (OpenGlRenderState::*) () const) + &OpenGlRenderState::GetModelViewMatrix) + .def("GetModelViewMatrix", (OpenGlMatrix (OpenGlRenderState::*) (int) const) + &OpenGlRenderState::GetModelViewMatrix, "i"_a) + + .def("GetProjectionModelViewMatrix", &OpenGlRenderState::GetProjectionModelViewMatrix) + .def("GetProjectiveTextureMatrix", &OpenGlRenderState::GetProjectiveTextureMatrix) + .def("EnableProjectiveTexturing", &OpenGlRenderState::EnableProjectiveTexturing) + .def("DisableProjectiveTexturing", &OpenGlRenderState::DisableProjectiveTexturing) + .def("Follow", &OpenGlRenderState::Follow, "T_wc"_a, "follow"_a) + .def("Unfollow", &OpenGlRenderState::Unfollow) + ; + + + m.def("ProjectionMatrix", &ProjectionMatrix, + "w"_a, "h"_a, "fu"_a, "fv"_a, "u0"_a, "v0"_a, "zNear"_a, "zFar"_a); + + m.def("ProjectionMatrixOrthographic", &ProjectionMatrixOrthographic, + "l"_a, "r"_a, "b"_a, "t"_a, "n"_a, "f"_a); + + m.def("ProjectionMatrixRUB_BottomLeft", &ProjectionMatrixRUB_BottomLeft, + "w"_a, "h"_a, "fu"_a, "fv"_a, "u0"_a, "v0"_a, "zNear"_a, "zFar"_a); + + m.def("ProjectionMatrixRDF_TopLeft", &ProjectionMatrixRDF_TopLeft, + "w"_a, "h"_a, "fu"_a, "fv"_a, "u0"_a, "v0"_a, "zNear"_a, "zFar"_a); + + m.def("ModelViewLookAtRUB", &ModelViewLookAtRUB, + "ex"_a, "ey"_a, "ez"_a, "lx"_a, "ly"_a, "lz"_a, "ux"_a, "uy"_a, "uz"_a); + + m.def("ModelViewLookAtRDF", &ModelViewLookAtRDF, + "ex"_a, "ey"_a, "ez"_a, "lx"_a, "ly"_a, "lz"_a, "ux"_a, "uy"_a, "uz"_a); + + m.def("ModelViewLookAt", (OpenGlMatrix (*) (GLprecision, GLprecision, GLprecision, + GLprecision, GLprecision, GLprecision, GLprecision, GLprecision, GLprecision)) + &ModelViewLookAt, + "ex"_a, "ey"_a, "ez"_a, "lx"_a, "ly"_a, "lz"_a, "ux"_a, "uy"_a, "uz"_a); + + m.def("ModelViewLookAt", (OpenGlMatrix (*) (GLprecision, GLprecision, + GLprecision, GLprecision, GLprecision, GLprecision, AxisDirection)) + &ModelViewLookAt, + "ex"_a, "ey"_a, "ez"_a, "lx"_a, "ly"_a, "lz"_a, "up"_a); + + m.def("IdentityMatrix", (OpenGlMatrix (*) ()) &IdentityMatrix); + m.def("IdentityMatrix", (OpenGlMatrixSpec (*) (OpenGlStack)) &IdentityMatrix); + m.def("IdentityMatrix", &negIdentityMatrix); + + // operator<< +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/user_app.hpp b/python/display/user_app.hpp new file mode 100644 index 0000000..0d60329 --- /dev/null +++ b/python/display/user_app.hpp @@ -0,0 +1,42 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +namespace { + class PyUserApp : public UserApp { + public: + /* Inherit the constructors */ + using UserApp::UserApp; + + /* Trampoline (need one for each virtual function) */ + void Init() override { + PYBIND11_OVERLOAD_PURE( + void, /* Return type */ + UserApp, /* Parent class */ + Init, /* Name of function in C++ (must match Python name) */ + /* Argument(s) */ + ); + } + void Render() override { PYBIND11_OVERLOAD_PURE(void, UserApp, Render,) } + }; +} + + +void declareUserApp(py::module & m) { + + py::class_(m, "UserApp") + .def(py::init<>()) + .def("Init", &UserApp::Init) + .def("Render", &UserApp::Render); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/view.hpp b/python/display/view.hpp new file mode 100644 index 0000000..1c2060d --- /dev/null +++ b/python/display/view.hpp @@ -0,0 +1,216 @@ +#include +#include +#include + +#include +#include +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareView(py::module & m) { + + py::enum_(m, "Layout") + .value("LayoutOverlay", Layout::LayoutOverlay) + .value("LayoutVertical", Layout::LayoutVertical) + .value("LayoutHorizontal", Layout::LayoutHorizontal) + .value("LayoutEqual", Layout::LayoutEqual) + .value("LayoutEqualVertical", Layout::LayoutEqualVertical) + .value("LayoutEqualHorizontal", Layout::LayoutEqualHorizontal) + .export_values(); + + py::enum_(m, "Lock") + .value("LockLeft", Lock::LockLeft) + .value("LockBottom", Lock::LockBottom) + .value("LockCenter", Lock::LockCenter) + .value("LockRight", Lock::LockRight) + .value("LockTop", Lock::LockTop) + .export_values(); + + + py::class_>(m, "View") + + .def(py::init(), "aspect"_a) + + .def("Activate", (void (View::*)() const) &View::Activate, + "Activate Displays viewport for drawing within this area") + .def("Activate", (void (View::*)(const OpenGlRenderState&) const) &View::Activate, "state"_a, + "Activate Displays and set State Matrices") + + .def("ActivateAndScissor", (void (View::*) () const) &View::ActivateAndScissor, + "Activate Displays viewport and Scissor for drawing within this area") + .def("ActivateAndScissor", (void (View::*) (const OpenGlRenderState&) const) &View::ActivateAndScissor, + "Activate Display and set State Matrices") + + .def("ActivateScissorAndClear", (void (View::*) () const) &View::ActivateScissorAndClear, + "Activate Displays viewport and Scissor for drawing within this area") + .def("ActivateScissorAndClear", (void (View::*) (const OpenGlRenderState&) const) + &View::ActivateScissorAndClear, + " Activate Display and set State Matrices") + + .def("ActivatePixelOrthographic", &View::ActivatePixelOrthographic, + "Activate Display and setup coordinate system for 2d pixel View coordinates") + + .def("ActivateIdentity", &View::ActivateIdentity, + "Activate Display and reset coordinate system to OpenGL default") + + .def("GetClosestDepth", &View::GetClosestDepth, "winx"_a, "winy"_a, "radius"_a, + "Return closest depth buffer value within radius of window (winx,winy)") + + .def("GetCamCoordinates", &View::GetCamCoordinates, + "cam_state"_a, "winx"_a, "winy"_a, "winzdepth"_a, "x"_a, "y"_a, "z"_a, + "Obtain camera space coordinates of scene at pixel (winx, winy, winzdepth)") + + .def("GetObjectCoordinates", &View::GetObjectCoordinates, + "cam_state"_a, "winx"_a, "winy"_a, "winzdepth"_a, "x"_a, "y"_a, "z"_a, + "Obtain object space coordinates of scene at pixel (winx, winy, winzdepth)") + + .def("Resize", &View::Resize, "parent"_a, + "Given the specification of Display, compute viewport") + + .def("ResizeChildren", &View::ResizeChildren, + "Instruct all children to resize") + + .def("Render", &View::Render, + "Perform any automatic rendering for this View.") + + .def("RenderChildren", &View::RenderChildren, + "Instruct all children to render themselves if appropriate") + + .def("SetFocus", &View::SetFocus, + py::return_value_policy::reference_internal, + "Set this view as the active View to receive input") + + .def("HasFocus", &View::HasFocus, + "Returns true iff this view currently has focus and will receive user input") + + .def("SetBounds", (View& (View::*)(Attach, Attach, Attach, Attach)) + &View::SetBounds, "bottom"_a, "top"_a, "left"_a, "right"_a, + py::return_value_policy::reference_internal, + "Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates)") + .def("SetBounds", (View& (View::*)(Attach, Attach, Attach, Attach, bool)) + &View::SetBounds, "bottom"_a, "top"_a, "left"_a, "right"_a, "keep_aspect"_a, + py::return_value_policy::reference_internal, + "Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates)") + .def("SetBounds", (View& (View::*)(Attach, Attach, Attach, Attach, double)) + &View::SetBounds, "bottom"_a, "top"_a, "left"_a, "right"_a, "aspect"_a, + py::return_value_policy::reference_internal, + "Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates)") + + .def("SetBounds", + [](View &view, float bottom, float top, float left, float right) { + return std::ref(view.SetBounds(bottom, top, left, right));}, + "bottom"_a, "top"_a, "left"_a, "right"_a, + py::return_value_policy::reference_internal, + "Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates)") + .def("SetBounds", + [](View &view, float bottom, float top, float left, float right, bool keep_aspect) { + return std::ref(view.SetBounds(bottom, top, left, right, keep_aspect));}, + "bottom"_a, "top"_a, "left"_a, "right"_a, "keep_aspect"_a, + py::return_value_policy::reference_internal, + "Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates)") + .def("SetBounds", + [](View &view, float bottom, float top, float left, float right, double aspect) { + return std::ref(view.SetBounds(bottom, top, left, right, aspect));}, + "bottom"_a, "top"_a, "left"_a, "right"_a, "aspect"_a, + py::return_value_policy::reference_internal, + "Set bounds for the View using mixed fractional / pixel coordinates (OpenGl view coordinates)") + + .def("SetHandler", &View::SetHandler, + py::return_value_policy::reference_internal, + "Designate handler for accepting mouse / keyboard input.", + py::keep_alive<1, 2>()) + + .def("SetDrawFunction", &View::SetDrawFunction, "drawFunc"_a, + py::return_value_policy::reference_internal, + "Set drawFunc as the drawing function for this view") + + .def("SetAspect", &View::SetAspect, "aspect"_a, + py::return_value_policy::reference_internal, + "Force this view to have the given aspect, whilst fitting snuggly") + + .def("SetLock", &View::SetLock, "horizontal"_a, "vertical"_a, + py::return_value_policy::reference_internal, + "Set how this view should be positioned relative to its parent") + + .def("SetLayout", &View::SetLayout, "layout"_a, + py::return_value_policy::reference_internal, + "Set layout policy for this view") + + .def("AddDisplay", &View::AddDisplay, "view"_a, + py::return_value_policy::reference_internal, + "Add view as child", py::keep_alive<1, 2>()) + + .def("Show", &View::Show, "show"_a = true, + py::return_value_policy::reference_internal, + "Show / hide this view") + + .def("ToggleShow", &View::ToggleShow, + "Toggle this views visibility") + + .def("IsShown", &View::IsShown, + "Return whether this view should be shown.") + + .def("GetBounds", &View::GetBounds, + "Returns viewport reflecting space that will actually get drawn") + + .def("SaveOnRender", &View::SaveOnRender, "filename_prefix"_a, + "Specify that this views region in the framebuffer should be saved to\n" + "a file just before the buffer is flipped.") + + .def("RecordOnRender", &View::RecordOnRender, "record_uri"_a, + "Specify that this views region in the framebuffer should be saved to\n" + "a file just before the buffer is flipped.") + + .def("SaveRenderNow", &View::SaveRenderNow, "filename_prefix"_a, "scale"_a = 1, + "Uses the views default render method to draw into an FBO 'scale' times\n" + "the size of the view and save to a file.") + + .def("NumChildren", &View::NumChildren, + "Return number of child views attached to this view") + + .def("__getitem__", &View::operator[], + py::return_value_policy::reference_internal, py::is_operator(), + "Return (i)th child of this view") + + .def("NumVisibleChildren", &View::NumVisibleChildren, + "Return number of visible child views attached to this view.") + + .def("VisibleChild", &View::VisibleChild, + py::return_value_policy::reference_internal, + "Return visible child by index.") + + .def("FindChild", &View::FindChild, "x"_a, "y"_a, + py::return_value_policy::reference_internal, + "Return visible child at window coords x,y") + + .def_readwrite("aspect", &View::aspect, "Desired width / height aspect (0 if dynamic)") // double + .def_readwrite("top", &View::top) // Attach + .def_readwrite("left", &View::left) // Attach + .def_readwrite("right", &View::right) // Attach + .def_readwrite("bottom", &View::bottom) // Attach + .def_readwrite("hlock", &View::hlock) // Lock + .def_readwrite("vlock", &View::vlock) // Lock + .def_readwrite("layout", &View::layout) // Layout + .def_readwrite("scroll_offset", &View::scroll_offset) // int + .def_readwrite("vp", &View::vp, "Cached client area (space allocated from parent)") // Viewport + .def_readwrite("v", &View::v, "Cached absolute viewport (recomputed on resize - respects aspect)") // Viewport + .def_readwrite("show", &View::show, "Should this view be displayed?") // bool + .def_readwrite("zorder", &View::zorder, "Child views are rendered in order of low to high z-order") // int + .def_readwrite("handler", &View::handler, "Input event handler (if any)") // Handler* + .def_readwrite("views", &View::views, "Map for sub-displays (if any)") // std::vector + + // std::function + .def_readwrite("extern_draw_function", &View::extern_draw_function, "External draw function") + + // Private copy constructor + ; + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/viewport.hpp b/python/display/viewport.hpp new file mode 100644 index 0000000..61294b7 --- /dev/null +++ b/python/display/viewport.hpp @@ -0,0 +1,46 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareViewport(py::module & m) { + + py::class_> cls(m, "Viewport"); + + cls.def(py::init<>()); + cls.def(py::init()); + + cls.def("Activate", &Viewport::Activate); + cls.def("ActivateIdentity", &Viewport::ActivateIdentity); + cls.def("ActivatePixelOrthographic", &Viewport::ActivatePixelOrthographic); + + cls.def("Scissor", &Viewport::Scissor); + cls.def("ActivateAndScissor", &Viewport::ActivateAndScissor); + + cls.def("Contains", &Viewport::Contains); + + cls.def("Inset", (Viewport (Viewport::*) (int) const) &Viewport::Inset); + cls.def("Inset", (Viewport (Viewport::*) (int, int) const) &Viewport::Inset); + cls.def("Intersect", &Viewport::Intersect); + + cls.def_static("DisableScissor", &Viewport::DisableScissor); + + cls.def("r", &Viewport::r); + cls.def("t", &Viewport::t); + cls.def("aspect", &Viewport::aspect); + + cls.def_readwrite("l", &Viewport::l); + cls.def_readwrite("b", &Viewport::b); + cls.def_readwrite("w", &Viewport::w); + cls.def_readwrite("h", &Viewport::h); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/widgets/widgets.hpp b/python/display/widgets/widgets.hpp new file mode 100644 index 0000000..82fba86 --- /dev/null +++ b/python/display/widgets/widgets.hpp @@ -0,0 +1,105 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + + +namespace { + template + void templatedWidget(py::module & m, const std::string & suffix) { + py::class_, std::shared_ptr>, View, Handler, Var>(m, ("Widget" + suffix).c_str()) + .def(py::init(), "title"_a, "tv"_a) + .def_readwrite("title", &Widget::title); + } +} + + + +void declareWidgets(py::module & m) { + + m.def("CreatePanel", &CreatePanel, py::return_value_policy::reference); + + + py::class_, View> (m, "Panel") + .def(py::init<>()) + .def(py::init(), "auto_register_var_prefix"_a) + .def("Render", &Panel::Render) + .def("ResizeChildren", &Panel::ResizeChildren) + .def_static("AddVariable", &Panel::AddVariable); + + + templatedWidget(m, "Bool"); + templatedWidget(m, "Int"); + templatedWidget(m, "Float"); + templatedWidget(m, "String"); + templatedWidget>(m, "Func"); + + + py::class_, Widget> (m, "Button") + .def(py::init(), "title"_a, "tv"_a) + .def("Mouse", &Button::Mouse) + .def("Render", &Button::Render) + .def("ResizeChildren", &Button::ResizeChildren) + // GLfloat raster[2] + .def_readwrite("gltext", &Button::gltext) + .def_readwrite("down", &Button::down); + + + py::class_, Widget>> (m, "FunctionButton") + .def(py::init(), "title"_a, "tv"_a) + .def("Mouse", &FunctionButton::Mouse) + .def("Render", &FunctionButton::Render) + .def("ResizeChildren", &FunctionButton::ResizeChildren) + // GLfloat raster[2] + .def_readwrite("gltext", &FunctionButton::gltext) + .def_readwrite("down", &FunctionButton::down); + + + py::class_, Widget> (m, "Checkbox") + .def(py::init(), "title"_a, "tv"_a) + .def("Mouse", &Checkbox::Mouse) + .def("Render", &Checkbox::Render) + .def("ResizeChildren", &Checkbox::ResizeChildren) + // GLfloat raster[2] + .def_readwrite("gltext", &Checkbox::gltext) + .def_readwrite("vcb", &Checkbox::vcb); + + + py::class_, Widget> (m, "Slider") + .def(py::init()) + .def("Mouse", &Slider::Mouse, "view"_a, "button"_a, "x"_a, "y"_a, "pressed"_a, "mouse_state"_a) + .def("MouseMotion", &Slider::MouseMotion, "view"_a, "x"_a, "y"_a, "mouse_state"_a) + .def("Keyboard", &Slider::Keyboard, "view"_a, "key"_a, "x"_a, "y"_a, "pressed"_a) + .def("Render", &Slider::Render) + .def("ResizeChildren", &Slider::ResizeChildren) + // GLfloat raster[2] + .def_readwrite("gltext", &Slider::gltext) + .def_readwrite("lock_bounds", &Slider::lock_bounds) + .def_readwrite("logscale", &Slider::logscale) + .def_readwrite("is_integral_type", &Slider::is_integral_type); + + + py::class_, Widget> (m, "TextInput") + .def(py::init()) + .def("Mouse", &TextInput::Mouse, "view"_a, "button"_a, "x"_a, "y"_a, "pressed"_a, "mouse_state"_a) + .def("MouseMotion", &TextInput::MouseMotion, "view"_a, "x"_a, "y"_a, "mouse_state"_a) + .def("Keyboard", &TextInput::Keyboard, "view"_a, "key"_a, "x"_a, "y"_a, "pressed"_a) + .def("Render", &TextInput::Render) + .def("ResizeChildren", &TextInput::ResizeChildren) + .def_readwrite("edit", &TextInput::edit) + .def_readwrite("gledit", &TextInput::gledit) + // GLfloat raster[2] + // int sel[2] + .def_readwrite("gltext", &TextInput::gltext) + .def_readwrite("do_edit", &TextInput::do_edit); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/display/window.hpp b/python/display/window.hpp new file mode 100644 index 0000000..44c8769 --- /dev/null +++ b/python/display/window.hpp @@ -0,0 +1,20 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareWindow(py::module & m) { + + py::class_>(m, "GlContextInterface"); + py::class_>(m, "WindowInterface"); + +} + +} \ No newline at end of file diff --git a/python/examples/HelloPangolin.py b/python/examples/HelloPangolin.py new file mode 100644 index 0000000..c49b551 --- /dev/null +++ b/python/examples/HelloPangolin.py @@ -0,0 +1,48 @@ +# https://github.com/stevenlovegrove/Pangolin/tree/master/examples/HelloPangolin + +import OpenGL.GL as gl +import pangolin + +import numpy as np + +def main(): + pangolin.CreateWindowAndBind('Main', 640, 480) + gl.glEnable(gl.GL_DEPTH_TEST) + + # Define Projection and initial ModelView matrix + scam = pangolin.OpenGlRenderState( + pangolin.ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.2, 100), + pangolin.ModelViewLookAt(-2, 2, -2, 0, 0, 0, pangolin.AxisDirection.AxisY)) + handler = pangolin.Handler3D(scam) + + # Create Interactive View in window + dcam = pangolin.CreateDisplay() + dcam.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0/480.0) + dcam.SetHandler(handler) + + while not pangolin.ShouldQuit(): + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + gl.glClearColor(1.0, 1.0, 1.0, 1.0) + dcam.Activate(scam) + + # Render OpenGL Cube + pangolin.glDrawColouredCube() + + # Draw Point Cloud + points = np.random.random((100000, 3)) * 10 + colors = np.zeros((len(points), 3)) + colors[:, 1] = 1 -points[:, 0] / 10. + colors[:, 2] = 1 - points[:, 1] / 10. + colors[:, 0] = 1 - points[:, 2] / 10. + + gl.glPointSize(2) + gl.glColor3f(1.0, 0.0, 0.0) + # access numpy array directly(without copying data), array should be contiguous. + pangolin.DrawPoints(points, colors) + + pangolin.FinishFrame() + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/SimpleDisplayImage.py b/python/examples/SimpleDisplayImage.py new file mode 100644 index 0000000..406fd2f --- /dev/null +++ b/python/examples/SimpleDisplayImage.py @@ -0,0 +1,67 @@ +# https://github.com/stevenlovegrove/Pangolin/tree/master/examples/SimpleDisplayImage + +import numpy as np + +import OpenGL.GL as gl +import pangolin + + +def random_image(w, h): # HWC + return (np.ones((h, w, 3), 'uint8') * + np.random.randint(256, size=3, dtype='uint8')) + + +def main(): + # Create OpenGL window in single line + pangolin.CreateWindowAndBind('Main', 640, 480) + + # 3D Mouse handler requires depth testing to be enabled + gl.glEnable(gl.GL_DEPTH_TEST) + + + scam = pangolin.OpenGlRenderState( + pangolin.ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.1, 1000), + pangolin.ModelViewLookAt(-1, 1, -1, 0, 0, 0, pangolin.AxisDirection.AxisY)) + + # Aspect ratio allows us to constrain width and height whilst fitting within specified + # bounds. A positive aspect ratio makes a view 'shrink to fit' (introducing empty bars), + # whilst a negative ratio makes the view 'grow to fit' (cropping the view). + dcam = pangolin.CreateDisplay() + dcam.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0/480.0) + dcam.SetHandler(pangolin.Handler3D(scam)) + + # This view will take up no more than a third of the windows width or height, and it + # will have a fixed aspect ratio to match the image that it will display. When fitting + # within the specified bounds, push to the top-left (as specified by SetLock). + dimg = pangolin.Display('image') + dimg.SetBounds(2./3, 1.0, 0.0, 1./3, 640./480) + dimg.SetLock(pangolin.Lock.LockLeft, pangolin.Lock.LockTop) + + + w, h = 64, 48 + texture = pangolin.GlTexture(w, h, gl.GL_RGB, False, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE) + + # Default hooks for exiting (Esc) and fullscreen (tab). + while not pangolin.ShouldQuit(): + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + gl.glClearColor(0.95, 0.95, 0.95, 1.0) + + dcam.Activate(scam) + gl.glColor3f(1.0, 1.0, 1.0) + pangolin.glDrawColouredCube() + + # Set some random image data and upload to GPU + image = random_image(w, h) + texture.Upload(image, gl.GL_RGB, gl.GL_UNSIGNED_BYTE) + + # display the image + dimg.Activate() + gl.glColor3f(1.0, 1.0, 1.0) + texture.RenderToViewport() + + pangolin.FinishFrame() + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/SimpleDisplayMenu.py b/python/examples/SimpleDisplayMenu.py new file mode 100644 index 0000000..cadc763 --- /dev/null +++ b/python/examples/SimpleDisplayMenu.py @@ -0,0 +1,92 @@ +# https://github.com/stevenlovegrove/Pangolin/tree/master/examples/SimpleDisplay + +import OpenGL.GL as gl +import pangolin + + +class SetVarFunctor(object): + def __init__(self, var=None, value=None): + super().__init__() + self.var = var + self.value = value + + def __call__(self): + self.var.SetVal(self.value) + +def reset(): + #float_slider.SetVal(0.5) + print('You typed ctrl-r or pushed reset') + + + +def main(): + pangolin.ParseVarsFile('app.cfg') + + pangolin.CreateWindowAndBind('Main', 640, 480) + gl.glEnable(gl.GL_DEPTH_TEST) + + scam = pangolin.OpenGlRenderState( + pangolin.ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.1, 1000), + pangolin.ModelViewLookAt(0, 0.5, -3, 0, 0, 0, pangolin.AxisDirection.AxisY)) + handler3d = pangolin.Handler3D(scam) + + dcam = pangolin.CreateDisplay() + dcam.SetBounds(0.0, 1.0, 180/640., 1.0, -640.0/480.0) + # dcam.SetBounds(pangolin.Attach(0.0), pangolin.Attach(1.0), + # pangolin.Attach.Pix(180), pangolin.Attach(1.0), -640.0/480.0) + + dcam.SetHandler(pangolin.Handler3D(scam)) + + panel = pangolin.CreatePanel('ui') + panel.SetBounds(0.0, 1.0, 0.0, 180/640.) + + + button = pangolin.VarBool('ui.Button', value=False, toggle=False) + checkbox = pangolin.VarBool('ui.Checkbox', value=False, toggle=True) + float_slider = pangolin.VarFloat('ui.Float', value=3, min=0, max=5) + float_log_slider = pangolin.VarFloat('ui.Log_scale var', value=3, min=1, max=1e4, logscale=True) + int_slider = pangolin.VarInt('ui.Int', value=2, min=0, max=5) + int_slave_slider = pangolin.VarInt('ui.Int_slave', value=2, toggle=False) + + save_window = pangolin.VarBool('ui.Save_Window', value=False, toggle=False) + save_cube = pangolin.VarBool('ui.Save_Cube', value=False, toggle=False) + record_cube = pangolin.VarBool('ui.Record_Cube', value=False, toggle=False) + + def reset(): + #float_slider.SetVal(0.5) + print('You typed ctrl-r or pushed reset') + + # Reset = SetVarFunctor(float_slider, 0.5) + # reset = pangolin.VarFunc('ui.Reset', reset) + # pangolin.RegisterKeyPressCallback(int(pangolin.PANGO_CTRL) + ord('r'), reset) # segfault + # pangolin.RegisterKeyPressCallback(int(pangolin.PANGO_CTRL) + ord('b'), pangolin.SetVarFunctorFloat('ui.Float', 4.5)) # segfault + # pangolin.RegisterKeyPressCallback(int(pangolin.PANGO_CTRL) + ord('b'), SetVarFunctor(float_slider, 4.5)) # segfault + + while not pangolin.ShouldQuit(): + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + + if pangolin.Pushed(button): + print('You Pushed a button!') + + if checkbox.Get(): + int_slider.SetVal(int(float_slider)) + int_slave_slider.SetVal(int_slider) + + if pangolin.Pushed(save_window): + pangolin.SaveWindowOnRender("window") + + if pangolin.Pushed(save_cube): + pangolin.SaveWindowOnRender("cube") + + if pangolin.Pushed(record_cube): + pangolin.DisplayBase().RecordOnRender("ffmpeg:[fps=50,bps=8388608,unique_filename]//screencap.avi") + + + dcam.Activate(scam) + gl.glColor3f(1.0, 1.0, 1.0) + pangolin.glDrawColouredCube() + pangolin.FinishFrame() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/SimpleMultiDisplay.py b/python/examples/SimpleMultiDisplay.py new file mode 100644 index 0000000..5ab266f --- /dev/null +++ b/python/examples/SimpleMultiDisplay.py @@ -0,0 +1,113 @@ +# https://github.com/stevenlovegrove/Pangolin/blob/master/examples/SimpleMultiDisplay + +import numpy as np + +import OpenGL.GL as gl +import pangolin + + +def random_image(w, h): + return (np.ones((h, w, 3), 'uint8') * + np.random.randint(256, size=3, dtype='uint8')) + + +def main(): + # Create OpenGL window in single line + pangolin.CreateWindowAndBind('Main', 640, 480) + + # 3D Mouse handler requires depth testing to be enabled + gl.glEnable(gl.GL_DEPTH_TEST) + + # Issue specific OpenGl we might need + gl.glEnable(gl.GL_BLEND) + gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) + + + # Define Camera Render Object (for view / scene browsing) + proj = pangolin.ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.1, 1000) + scam = pangolin.OpenGlRenderState( + proj, pangolin.ModelViewLookAt(1, 0.5, -2, 0, 0, 0, pangolin.AxisY)) + scam2 = pangolin.OpenGlRenderState( + proj, pangolin.ModelViewLookAt(0, 0, -2, 0, 0, 0, pangolin.AxisY)) + + + # Add named OpenGL viewport to window and provide 3D Handler + dcam1 = pangolin.Display('cam1') + dcam1.SetAspect(640 / 480.) + dcam1.SetHandler(pangolin.Handler3D(scam)) + + dcam2 = pangolin.Display('cam2') + dcam2.SetAspect(640 / 480.) + dcam2.SetHandler(pangolin.Handler3D(scam2)) + + dcam3 = pangolin.Display('cam3') + dcam3.SetAspect(640 / 480.) + dcam3.SetHandler(pangolin.Handler3D(scam)) + + dcam4 = pangolin.Display('cam4') + dcam4.SetAspect(640 / 480.) + dcam4.SetHandler(pangolin.Handler3D(scam2)) + + dimg1 = pangolin.Display('img1') + dimg1.SetAspect(640 / 480.) + + dimg2 = pangolin.Display('img2') + dimg2.SetAspect(640 / 480.) + + + # LayoutEqual is an EXPERIMENTAL feature - it requires that all sub-displays + # share the same aspect ratio, placing them in a raster fasion in the + # viewport so as to maximise display size. + view = pangolin.Display('multi') + view.SetBounds(0.0, 1.0, 0.0, 1.0) + view.SetLayout(pangolin.LayoutEqual) + view.AddDisplay(dcam1) + view.AddDisplay(dimg1) + view.AddDisplay(dcam2) + + view.AddDisplay(dimg2) + view.AddDisplay(dcam3) + view.AddDisplay(dcam4) + + + w, h = 64, 48 + image_texture = pangolin.GlTexture( + w, h, gl.GL_RGB, False, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE) + + # Default hooks for exiting (Esc) and fullscreen (tab) + while not pangolin.ShouldQuit(): + + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + + + gl.glColor3f(1.0, 1.0, 1.0) + + dcam1.Activate(scam) + pangolin.glDrawColouredCube() + + dcam2.Activate(scam2) + pangolin.glDrawColouredCube() + + dcam3.Activate(scam) + pangolin.glDrawColouredCube() + + dcam4.Activate(scam2) + pangolin.glDrawColouredCube() + + + dimg1.Activate() + gl.glColor4f(1.0, 1.0, 1.0, 1.0) + image_texture.Upload(random_image(w, h), gl.GL_RGB, gl.GL_UNSIGNED_BYTE) + image_texture.RenderToViewport() + + dimg2.Activate() + gl.glColor4f(1.0, 1.0, 1.0, 1.0) + # image_texture.Upload(random_image(w, h), gl.GL_RGB, gl.GL_UNSIGNED_BYTE) + image_texture.RenderToViewport() + + pangolin.FinishFrame() + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/SimplePlot.py b/python/examples/SimplePlot.py new file mode 100644 index 0000000..d5400b0 --- /dev/null +++ b/python/examples/SimplePlot.py @@ -0,0 +1,51 @@ +# https://github.com/stevenlovegrove/Pangolin/blob/master/examples/SimplePlot + +import OpenGL.GL as gl +import pangolin + +import numpy as np + + + +def main(): + # Create OpenGL window in single line + pangolin.CreateWindowAndBind('Main', 640, 480) + + # Data logger object + log = pangolin.DataLog() + + # Optionally add named labels + labels = ['sin(t)', 'cos(t)', 'sin(t)+cos(t)'] + log.SetLabels(labels) + + # OpenGL 'view' of data. We might have many views of the same data. + tinc = 0.03 + plotter = pangolin.Plotter(log, 0.0, 4.0*np.pi/tinc, -2.0, 2.0, np.pi/(4*tinc), 0.5) + plotter.SetBounds(0.0, 1.0, 0.0, 1.0) + plotter.Track('$i') + + # Add some sample annotations to the plot + plotter.AddMarker(pangolin.Marker.Vertical, -1000, pangolin.Marker.LessThan, + pangolin.Colour.Blue().WithAlpha(0.2)) + plotter.AddMarker(pangolin.Marker.Horizontal, 100, pangolin.Marker.GreaterThan, + pangolin.Colour.Red().WithAlpha(0.2)) + plotter.AddMarker(pangolin.Marker.Horizontal, 10, pangolin.Marker.Equal, + pangolin.Colour.Green().WithAlpha(0.2)) + + pangolin.DisplayBase().AddDisplay(plotter) + + + t = 0 + while not pangolin.ShouldQuit(): + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + + log.Log(np.sin(t), np.cos(t), np.sin(t)+np.cos(t)) + t += tinc + + pangolin.FinishFrame() + + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/SimplePlotDisplay.py b/python/examples/SimplePlotDisplay.py new file mode 100644 index 0000000..73936e2 --- /dev/null +++ b/python/examples/SimplePlotDisplay.py @@ -0,0 +1,76 @@ +# https://github.com/stevenlovegrove/Pangolin/tree/master/examples/HelloPangolin +# https://github.com/stevenlovegrove/Pangolin/blob/master/examples/SimplePlot + +import OpenGL.GL as gl +import pangolin + +import numpy as np + + + +def main(): + # Create OpenGL window in single line + pangolin.CreateWindowAndBind('Main', 640, 480) + gl.glEnable(gl.GL_DEPTH_TEST) + + + # Define Projection and initial ModelView matrix + scam = pangolin.OpenGlRenderState( + pangolin.ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.2, 100), + pangolin.ModelViewLookAt(-2, 2, -2, 0, 0, 0, pangolin.AxisDirection.AxisY)) + handler = pangolin.Handler3D(scam) + + # Create Interactive View in window + dcam = pangolin.CreateDisplay() + dcam.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0/480.0) + dcam.SetHandler(handler) + + + + # Data logger object + log = pangolin.DataLog() + + # Optionally add named labels + labels = ['sin(t)', 'cos(t)', 'sin(t)+cos(t)'] + log.SetLabels(labels) + + # OpenGL 'view' of data. We might have many views of the same data. + tinc = 0.03 + plotter = pangolin.Plotter(log, 0.0, 6.0*np.pi/tinc, -2.0, 2.0, np.pi/(6*tinc), 0.5) + plotter.SetBounds(0.05, 0.3, 0.0, 0.4) + plotter.Track('$i') + + # Add some sample annotations to the plot + plotter.AddMarker(pangolin.Marker.Vertical, -1000, pangolin.Marker.LessThan, + pangolin.Colour.Blue().WithAlpha(0.2)) + plotter.AddMarker(pangolin.Marker.Horizontal, 100, pangolin.Marker.GreaterThan, + pangolin.Colour.Red().WithAlpha(0.2)) + plotter.AddMarker(pangolin.Marker.Horizontal, 10, pangolin.Marker.Equal, + pangolin.Colour.Green().WithAlpha(0.2)) + + pangolin.DisplayBase().AddDisplay(plotter) + + + t = 0 + while not pangolin.ShouldQuit(): + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + + # Plot line + log.Log(np.sin(t), np.cos(t), np.sin(t)+np.cos(t)) + t += tinc + + + gl.glClearColor(1.0, 1.0, 1.0, 1.0) + dcam.Activate(scam) + + # Render OpenGL 3D Cube + pangolin.glDrawColouredCube() + + + pangolin.FinishFrame() + + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/SimpleScene.py b/python/examples/SimpleScene.py new file mode 100644 index 0000000..3323a69 --- /dev/null +++ b/python/examples/SimpleScene.py @@ -0,0 +1,44 @@ +# https://github.com/stevenlovegrove/Pangolin/tree/master/examples/SimpleScene + +import OpenGL.GL as gl +import pangolin + + + +def main(): + pangolin.CreateWindowAndBind('Main', 640, 480) + gl.glEnable(gl.GL_DEPTH_TEST) + + # Define Projection and initial ModelView matrix + scam = pangolin.OpenGlRenderState( + pangolin.ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.2, 100), + pangolin.ModelViewLookAt(-2, 2, -2, 0, 0, 0, pangolin.AxisY)) + + tree = pangolin.Renderable() + tree.Add(pangolin.Axis()) + + # Create Interactive View in window + handler = pangolin.SceneHandler(tree, scam) + dcam = pangolin.CreateDisplay() + dcam.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0 /480.0) + dcam.SetHandler(handler) + + def draw(view): + view.Activate(scam) + tree.Render() + dcam.SetDrawFunction(draw) + + while not pangolin.ShouldQuit(): + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + + # or + # dcam.Activate(scam) + # tree.Render() + + pangolin.FinishFrame() + + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/examples/app.cfg b/python/examples/app.cfg new file mode 100644 index 0000000..e69de29 diff --git a/python/examples/imgs/HelloPangolin.png b/python/examples/imgs/HelloPangolin.png new file mode 100644 index 0000000000000000000000000000000000000000..7f87f11d28c598c8079f2d19c8cba41dbb8ede3a GIT binary patch literal 32968 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%0A>Y|Jc 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 Ag8%>k literal 0 HcmV?d00001 diff --git a/python/examples/imgs/SPTAM_KITTI00.png b/python/examples/imgs/SPTAM_KITTI00.png new file mode 100644 index 0000000000000000000000000000000000000000..128c5f07a278c19e330b53fea8ae3e9dacc22596 GIT binary patch literal 202383 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�fXOP`6dbqkh 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 Cw-e|9 literal 0 HcmV?d00001 diff --git a/python/examples/imgs/SPTAM_PointCloud.png b/python/examples/imgs/SPTAM_PointCloud.png new file mode 100644 index 0000000000000000000000000000000000000000..051272d97e524c7dac543fa607872588b3059455 GIT binary patch literal 194169 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

    uw}*_+__^xb6Hk( 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

      F$tIsZ68(JRfqnWHST6#1I`ufJ+LOf{2wDa0V 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 WcYrh12>{Ok0000S^SE3hC5CeVOT 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 JSk&;#{{h1*IgbDU literal 0 HcmV?d00001 diff --git a/python/examples/imgs/SimplePlotDisplay.png b/python/examples/imgs/SimplePlotDisplay.png new file mode 100644 index 0000000000000000000000000000000000000000..f92e6e4650dabe48cbe3d50f532056c1310d1649 GIT binary patch literal 27974 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 literal 0 HcmV?d00001 diff --git a/python/examples/imgs/SimpleScene.png b/python/examples/imgs/SimpleScene.png new file mode 100644 index 0000000000000000000000000000000000000000..517675e536d410a65ce8c8d27f3987e026f13f12 GIT binary patch literal 2982 zcmeHJ{ZCV86h8O%7H;|KMaJ48wv}xz6Osacl~CJ@=!C4O*$hD?T3}HT<4_W*fc8oe zJ4LH=VyA*^F8BkA75u^>T&1o?hF}=eX<@Ah1*T90M#Um~%bzj1$;~Yc*$Ib#B=4K_W#zdaWoQ0VoGwKc;8E7!yeHj#^Qv8O;(7*DA$P#-Lf6$mAGx!jqV;Td&$}~ z)ZBI%dbi-Di976FUk1dJyN*XAYntFzXK?)%CSAi9PjaezL^Si0^^JF`CzK4aemskTCWM@kf^q!P z#(YaMa5A!_LmF8O#LctFmU=8AdBw^h7yAF|-8RY>wi1*r`Z?dIgHKvqXR_8!;yD!a z%ig6M!<@Sd&b6>H<%Uy@J1e!U20wOHYH4m|2bJ6)-P!Jaj&m+fuLSJM^kX@7BYfjQ zDB48nyZwrDF7pbC|xX4=}$XFs(!a04_Z1(>HEc|Qcp=}I&ZHoHw;Df2{zXbTW6b4 z9#WX#;g|ApuZt_-Zo1i~LR=Q{=oF(cw7+2@Sj*5E7>{*{sZQh)n{Fbe9V2JuV+3>(?#tn&XClyD8F{_N7R)r+x0Rw*$F9 zDudZG2ta&&+#Xo9Nv*^wjq!G}8DTXLlI%lc8ErY=PP@0p(%Z_kY*#3b6@QecdC@Vg z-#ZoTPwSP-)5m;Yt_HlRr8eyP1UH0`M4sFk)mOKq0@Zi>ErnO-8}EfAt>#_6XUmYe zu&V_jlNf0KDbH-RjFSaZ(s3l%DB{taXFc>}K(IBCVL$8==#m@h6NfAIU+NUpTZY?D zyLN|K+N*BFakjS*)RU(}d;`i`(maMfdQZCMGtaenk(s$0XU2n>3F&`6B#uZM;)b^= Tw5i+Cpbd#DRxPhyrY`&sp=b1q literal 0 HcmV?d00001 diff --git a/python/examples/imgs/simple_draw.png b/python/examples/imgs/simple_draw.png new file mode 100644 index 0000000000000000000000000000000000000000..3a35de32edc7c2eafb3f1f975ce2ec7b7b2f0922 GIT binary patch literal 46182 zcmeEug;N~e7v%uKHNic&1b2tv1P>4(Sa5fD2<{Nv-Q8UWcXxNUK?dLXepUNVZ0%Ha zO;7jhuIkhGoqMjl2~$##LPa7#0ssK0GScEd0RU(}003$m0SGR`+#6SNjuA8)ScPOS@0yw?ef zxM5hAk^b~x$T35~pdofXDjXAk*%dkFyffkBZ919Y2=Htknlt4fxObRz|Cs(X+{u0A zan(uL@gR`h*dUo-cX)ix`CC|UYEvuRdW}mIfh-WiU|7lb5(eXc`yaYr073ul2IC?6 z{qKlaw2y3jZPE#wRWYrixA>%YNx29yA#{|2M5{{P$j zzk$uJDf<#jSGNrwT(CX{-5g>t5uR|HBE$+5Wj|WcMG#mU@Pof|5WsGODIP@~>3DRg zXUwn+FNK49j^~f3>_bHnYFCu4)|z}T10ZHW41)XSsRAdq)$#*gwhcMbTv>)X)5za6 z%l(2hyP)U2Sl)1Q!}d!iIT^&l-kS?x83Ms0owj%#ABw+Qe9?V;xnD}TQ%&)BGPlux z3xMt{#fv3n7leXlVN)C*)3=0&QcQE?Z~u%lzoG64n&Tpq_4|f2_yu8neCZB-UrKCq z{~*JA7sPnq$#Si+;(0j67kKWE*lG@n5x7uyV*K$w*KOEg>Gu=Ke`e$-F_viP2kuWO zEYVj=p=afq7t}kFvODMp852R&zY%wyi@vwi$Ex1kdbGidry))H z!FITkKRy{y*hM<7A}5)0n-hT;yC13J|J|(FTL|yLB~AH>AZHq}5$=BBw=HpG? z#DQb9Ksxj++ZzgCT+?X@WBPh2a@lc21yB(Dz<5=&M=Fvul?tV~^m-2QEAIQueI9e# zNDG~sF`0p^2L~?37BT#O!jrcq8w3GQM2H=k>>WNUTj=8Dj2{)QODAu}7r_@$23>D@ z5mMH3(arCNeu%$*+lRbv-`mB*p|tQ5%>T4B-C-P-GRLa30Nk)OXJ>-S#QOxT2fQ1GeAzP^Da_KU?1$?19KbiC&bB)2V#ZpTWhAy zdnzWL9lWa63yD{~qWBU-j0HGTa-tAGYcsA%>vtIg%G_5@v9K z#32(4ix9{4d)sRWjB6h&=!p+7AM}Jq2TIqb zFKJU2Xi!*SkjIQ~26wZc=MK?#`wD!vFgXy7@#13@Ly}YwLkY%$I6ail!~wZghf>I#i-d6UGqmOK&8AQ_eu^=v(Pd&)B`o!f6RyF% zLo|v~u3G1I>U6Y|f}X-I-Fuu!I32HRVyz}>q>u-i=5RY}>Rf*5Xr^1f?q3K|p*GwC#4%J>!ob4SD5~bdqxpZV}Ll9~ge&V4=%xkcZdyh#)5EZq3hp zxrVslLjY23>M19R#|O|$h|r9*!+PTn7zU3dij9H5c;@B>P_wsJooAl2TqtycGeg_H z38FvzBi8^}D80Q5dda1;+XqfL%v~B|F}j@{nx~kYxT9zJQrg(U4J=;6_Utv=@nE^E zbB-3163#Cbx13EHw!B>8^T3t+KFRr&Pr0Yb4a(;T0I#n26ELdzhii=G*H7H*?;p-QycPW+_R}L34?%oQk+%PIFq~Alqg8vrw6Z z+$HZlD-j8?-t^(WWOV4S7<9466<={cyT}^iWw#|=q`Jwq%iF`v=zHqbr2e(wkR_kH zJn#2v_S`51n0!#k5aAn~WcMd=vi_p~rjS+Nc<mnNeq)N6V~6j}70JpYR# zxWtSg=MTM+vVBP1xyuTX&DI&mc`unfYcHvbtupzduVj&37qR zMg`lng@^#~Sp*hi&}|V9Y4^hTua}B~3UVp5N(4KvqWBImE6y0d%Vm zHkC9uTwey#j2CXc@TKcLhxC4ziG)i+6yKRs+Ce`kob#1#?q9MudfX+V2sgH0u|>$Z zl#BHLTy~PK{#i!ud?&d4z<4(Z_D5Ql$jr(|2jnmj6h1x8a@$wDD^uBBAi~$*fyFd< zKrDZwU_}Yc!u#5#h+@jzZ@}R^9H@lY5-P)#IvNr*H1sIc#-3S<=?1)e+b) z)^T~yZajwzL}{B8oW;;zvagYZ6#s2H>q^=r=M+%`Yr-;Bxd&F9Ji4u~U3#BBBi7rbjiwE(c4EpYiBleD?va_tB9!C1$Z=l9ay%xsjB``^P zu^=Q#xYN8YcDoq`VeIND(nDrPpBxY@1bFWx(D_867Fx?#ZX-^@+-*Oa;4nO7r#Nrz%d3oVPn22&j8)2no2MRO{kui09NY?e9d&au}lxdD1&)=~{Lw#ZXBMr$}R1)o&j%RP$b54|J zG(HqX_pIOrL^wEZ3p53~psr2F=$zY!VvYARL;GLpE#5MxRo@~sVvqlaIIE-a~z~E20t9XKfP+SdgC!}^Nn;ZbN2oYm{ z!e$>dcGx7?`Y!&zAue9wni1#+T}h_Burq_S^2pu-s*gzf-x2$4zszaqXo25f2}*l^ zOq&R;Qy01~vR0_SVj$ekXIZ$>@i6wY4$a8v+he#!s zdT;81O_DDp2=e?W#8K(%jbw$2~ z{>Twqhko5^hnE1{8MZ3IVwC}gmg6%A_LtdoL{-X@t1|NB$JkJJa~(|?Yj6Ju8sR8& z3#>ZbI!*ZeL3E5;p3<^06*cajsLYo?=n5O2ewWSl@^kt+5_%~pbfLcf z#vNetGv_y7#sj~1NO&vP^h%G4Vn7I39unfFLyfTvC#nN-6)^FIqQYm3v~7ri%Xk4`BBix|1rKee=4k|lqdFESciumd`1Ev~p^3Bcj*%LC zh`%pk33a(pw^qQYIscPSI9!l8eYx@I<-n#U{Xl6az9H|bBeH1}S^CYq=W=>jyK?mT zs=V)~Dy>;ypJvxn4&nr=tnh4DWBYJ%Z{71W(e}5o&!x0q5K4JqYHZ8}ldsqbLx>BC z8RaV0rkvr0`iQeh-<{sx!4kIryE3B|_JZN}pG2QCeGICGoK5)LOhlhe^0c@hSc1wq%tN&fnqgJCd7 z$@qti%UL+>sO@$Ax-lwx|e+@j3N`A|uiWLT%=wEaC9 zhc<|4tU6=hx||e{!;G%uN-A-jP^{r_0I~YJ?WlrM;aA2ip-WYlk3B0TktHjkj+0N+ zS#K=N89q$>Bprc_Xl{wgHiBW&hRm%U0d9TrB~()nDp}j_T1@20OiKJ@k~0m6ocm!M zcXbXYi(YU3l5BehFK@s8lyY5E0?k+S5fkO#3fL7Et1P}I)|+kS!D|I9-b1M-$Je{G z#dvZPea~T&v=#8Xq^|>~d%V{yWw&gD=31Q}g&ppn#ZHbNW?8z@wFiTCKW!w_WG&u& zLVgu`XZSO--Ft$Rh|1Gm->kqu)RuM7 znDq`iR;g`6jRTT1qrJ~l@wXO>{d%lA8>Zo|k%Q4Oaf^7fHqnxC$}O#d%jo0BwXPJ_ z;bK9o8Mm@i`$f2a0^2DtX5z%kk$Ju4Hi*8EC@2|{XX0|6p%u^HM}Zkwv}?h}Qp@%I zh_^4r__&a}#hLeR`Fybg{SY(-RkN7_FY-PG|FhdTGC1r_wUx1`)BYRaE#_+KlzGga zkE5)--JVBvR#Q0bSAmTi>9Wk4_bEQeaGbwy z(Z*@Yt6lp1P2)u#GwEP^Wig=Dcczxo08LJdN^qOZ8ZA~HHdXC|zP4rZZS=c#%R&Bl z=k_Bl+BK|IgGGd81+bPeQ;AZFHbcyLWt?z*du6mwrZwNb-73q{Ey3(-_zXX;pTVRG z-+@O$3Db@uLhZ>v0BygCD7>mu13Za@W9g?;*>ZKkM}gZn!wPhizrMVFe))IpVlWUd zH`)dT$F|V0;f4a(#6=fb;-n^SNh{JnGZ%QEB0__mILJ525S_UG1f+JstZ=B?TS2dR zNsUn!`jPKmPU%S69+;E{9lZ`fqQ8$KKJWM-@K~eQyuHUel`=WLtSE3S4I0a&Qq9Uq zajx}ICdQ7U6VesT)UHC(gZ)JBu>NscC`Wpa6y|nPE~qLm;riF0y+$tRkk#AmRf66E z8HF&@;H}SOoPqL1uwc(7+I!b(@?+b`;Twmf!tq|!9a#$uY!6iU;L=r8VeuMJ^KHn^ z=Z035yCstg{g>e6__AE&^0q*?53H;*>o}=5yYytw@Q8p2gukW!i+b?s2pnr;gg*~G zs{tsZ+_C)ZoR9z)g97v4Vqity-YEK+Ur-PB49wCdM%7To%i7G=H2j+<$+cI)x`jsC zPz@17)aT}i^>=9G&t2wmk4fV5kP=lk_aj1LC^WEX;vT1QtErUjlvO%$Y}KGK6OH-? zS_HFp))djGOwBdYk&Efg(oNm>;#pYt>6M`$mm)2+rTCZ{kbVE@?@R`>7;oEup7Z=W$JqqN>c{!?+a2-{~#bksleT)n3P z19|Zo2Jz=~dwtVZME5vLG%*GFOY-EG2k-X2(0_i=jw?wKnzAEh@i; z(LjY-6s|Siu>j<*(tOwOAUQF`@Bc|grpVS~zxQjs8u(^^86t1$Xi~ufE(z1)3qSovjL%+-W&kl6x2fYgGbGo_R8$&I`S|k0?V4BX6 zS5unY6D8p?!sMyL%cdvsQ%B5Z^zS4e&LH(o;m;Dvcw8PZH@e(R)lt^GrKFwy#5AskD>!qO zdWXm5R|RE4y&|qu<=vTnd1eifp(-0G2W7__Jegd%p)x`jG#Xhs{0Fv1@sMK@tuW_G@m0cUyD*=(dlA zNb6!UD5cPeNb~ZPrXMbs+I@ zSzwQPh`vdt-1kaXy#;=)Yozxj0yp#T<+nU=IM$RmM)p{r*EOv}QhZhx!27H-0hRQtQao9vbKGcY zL7hzvgH^V?yKLkiZzM&DaPdCawu!&h&G`(|k_D`jgH1RNNh9G?ohVzp!KXDR@+}Aw z21XGIL|M?$qvSyu5oI&hE#uA082q6JV^XyP1rq(cBxO?l$7bmI&?|Yk6PEE4o~bXl zwDbg4f8{feFB#a=K6=ugz%V+$5x9Z=vq3Tia0JhKw{S zM-G5)qS>DeUzhp~NSLnI(Zl-&!s5f`soXZ&<62=Hd27$)yy7@6MY6;PzYAP^f5dVh z{xBW>KF^O0$Y)7D868O}>BxNzE8)5~#rK6^mK}@qpUgC7IfufEK+^_*4h^QVWq0P0 zXVk8@9Jrs=9{-&q#5H06YyN<{2&7St_6@u}rQA7$jtfR%vh_FkUi_TkSwb9_^VA^> z5rorAy3w|`$afV{e{j~9!zULpaapapU%-cRmB%X>yXm~nRxjmPd#=A_HKQcPT2QnU ztp`X)=2n!4nn7tjrLi(kPrD#?gkM-I&R5m;FAT4r3H3DN(gr`3<2Fur359Z}e8q>K z(6^rzI-1=$6?Zigc;1lk0X0HA#-I7wQiVVR1C+Cc%;m7|10e+6<|46@Y0P>cT1t_U1(TfAWyur zlY5nmu2mcwPJ(uxXHAUkLK&NV(ubFX$Gz)fsp4&qbFO#Y#i>jWCi-6| zU4nT$s&=sQqfQaKe%X^10cyp%QI?Bh^Lk$V@3$!7Dg%3*h#^*|(Vf|L(?xk)LnZM) zE{uE&L|WJahHU?h&R>sGxw4-0k4fM0rFa=|^OmPQEZSFq>mvUYgiJQxgT-JatsA2U zXYd!jF!lFtf6x;=-+YqeTWX$es`8KeMqeWbKjM$1z_TkE{jrcqnk{{ya!L6b%$RiE#`IaQ6&Y3{xfsh(UddIoK8q-z27~YQ_!_^G{ z-k|`BV@=)ZYf{Mr5D!wIDH8cFn8D}dgXQE4swn))z+*qr6%?uVtRAE1MUomAaaqqi z=(s{Eo#9I*@7odXq%a^z_rv0}9*fP!N&*JCCg)-#UgO=IqB161)-oAJk!MJxgGf)) zU;DT5t?|+A2uyP=kU4pG`W8&|76=rK#M>GQhsCT_O}q_eXk!;G;2KAOUVeOOX4K#GCbwLgsE=vD5fjB~k&fo$ zzK@G_i$;CE{=;1@4xX7ean;_^o(Idr(`h}6ysSDIGrJ4682O)${9RLsG}SA(JYH4( zz@C>Z?%MJ6)^dgjsh*Fyu$?CKv{4yOeF|^HeHoS;pzKO_Oq?S4afJ69G7(3Hi6EMj zy@)k?y;gnKaMhK`V0Fe%vYjJxsV%@%=)UrSN}0PdM^EJL=m>)X_pD(ma|zv z!$3Jg1J+HOr=7=71~*TvHIl_a5uwPrxsbK^BFBIbkqto5-yb=wVKo1!)Tf8n_`Y>l zQn7jYxpn;2W&+~0C()Y;w&L@2P>a9S<$4ItWgK;}aZsel=<8%}i~E#<4n3!gY3(dc}4x%8@Y zJTla^4a&zq&G2R< zI`SifZ{EPJo$;sP&XFlT@czSqxHihxNc51cLBh#;n;SeNcW#pI5hnPpur(c{F`0-I z4sZM7nS!do$jL8utW-k zV@^5C1YLj9-lKoN#`zFs5p}OT@G-rdp;kOsg^9h@+qWZI65LVU;fEy&)_UCZwXp!rEo*3O8{ zYsD%e`QBo@rFoN99|yGGBl7vH_HNo_c`HD09#S1a%CwnUNEr8*H@5hSka?#b_n!m& zjEsFI^|2TiWt+R@Mch`Ex8a4oI-y?clr3zyS~w(bu?x7{wC6R_*{?I!pw%dfA7C%8 zJq1z2Ibb_gR8uV+74J@K*?ZiUq)D>wQP)n&>nU>?oSLFw1hqA zUtLLC^vLFJ7ubqnm!0OgpGzkB+#+)f974(qxf;_A69z>qF%BP-j>|Q9arMGaFov$n zJ?5Ovu>{wjZL2A|2Ll#PBvpvDI}RTiUcGo$8gN~|PIUsAcfNcC$)R3;e>rvyeSX|d zt&_c}%y@s}O}wTFTqcr+g$T}A^A0JW!^##?NceZD=^883Vs`06a2D_cuxh#H!ji~6 z9jw=MyR|wUa#i-3RI6xx`X#Cc^U2~xqtS!1lW=|BcFlh-b8ifNNV#5(H15`AmJU^= z-h3+3U{?1kZW3Hdu%N(qB6)L&xPUs{)`qXnqfk&AV>CNZ9%D08hm;I#2(LmiJ;uml`KWS@%b3}^zGKjI(;$7y>W>@U43swQzu2{+{*pV>y!7@-?k z4A$wnm@2&A0{%F3Z;ytnGGSjA{Mj&>&Vp#GUw zF^>;qRPW=_-j}fOkl$`y3nt1zSmYco}`a!2&J2 z4NbgSDen&SZ}oX)&Fma~H&SorEeUt)=s)hkhm|k1X*P0e6288SJ8+5UCR;ITPO+zz z+pStYGId*01=NG1w$0m}N3KPd#wm@mXD8Yhxz-B^9P-BZOZiX7_myTVY?8D;jZRGS zHJ&b5ppEf+`sl&6*gz#lb z<48+<(#G%a+?uss9BNU7J?5WFWMdUPPDl~*f+9Zky){%t#>(?cA7rY=5a+mzIrD5# z%O*z~k2ghVH0S;67HsF_Cv8X&5DK<#{OiUrJR2mx-+-`_cA&~Z&7yn~)Bg1OhPYoL z;7?DwDUGY=tsDkaF#;!f&qtckB;Ea!Z>KfGP_xedaA#3t)k*9c0YwepR?3 zqHOO0a)YK0(?C8;vJT@<7~hK$xOWt$xpF;#nQPEaU{#==tr$2#L0b*Je3j$&>xeq{x0?$3UK2`K7>jbdaSt-hb45vZ5p< za6p;uVy6SvHsPP7iF0zqgot3P$$cE3;tj+iq$bh%WxUVrKH& zbI@$*H&`lCUt8e5pfga|UC{x|_R2>(YGOB!a17-uC?7XP9g;$Z@lEraIz#5-Dbk() zO|{rPGiLY@7Bj0q`#{uGgKz74=dbHi!3$I5+sC z0K&MKHzI%x?bnE2RYXUP1NA&#>nVtg;If}_UviE+q+U4&0aiu zzHO*o;R0=h)K<;GD8N#yQ4EHuFGzzUhoU?i(<}9`ZA*DRMubpPjFDTna4zY!DSs$s z$*zpwZuL20GtZ@Uk{k!lxFx#Kded{5lgJ{L#oG<&(8W*9Pg?fVRd#{2CIr&!4|5; z@)eC)bL}LDJEvO)#yqHpQC&sdmTzq)SB$!i`G40){#NH2Q5eG%`YU~^fB!u`FZ zI=zJ7Kl4Y0+X`k%m(+O7@He7rKYr`N7bn}ehsodcXc{cE1H=r8q0u6x{^(SmB^6I7Qzkn0`9{?eK~H4vG3#;x%q;9bO$tfEsQo% z6V%BM^-aK`W#T16Z0A{gW_58BR?8=q7H&L9S`8Rr!PqiG29=&z8r%!6erPkmE zCq)AOsiki%=Jkubx&~+iCy;(_{#P}*c6@%7v7a`1;Fx-jw=>0xQ>Tgbm@ZJ$Mszdev|;#Qz2dhvx9RRq_=$cpk8WU z6V_(d-8a6WkJNmRY!PJ+Ni-rUN3TWU3`2^0^AppZc;ClsB#0yu{V&No7(PgIT!yMb ztv~ePM(nAj&`N_)67<`sUU!nf z1rk#V|F|gOigwNz!j{c0YFru^u3W`CF3YPn+eJIX!@K@B%wIw}Bda5 zJO!arP{?MjL_i`73*56^7{R)c?pwBWqTh>(E;+`vj^dLT2xcy$0FFO0Cy> zl}=U9i{mK+xtcw~+*C! zbLZ;@&r~egV&F$yo)J?9*hYYlwa@^Fvgxh-6$cbx0ZzUN&?S zd|2fY`3{s+=^@j_=G!<;NP+yqd3U4l+T*Fs8UJema0qX z%Y=FS4^Pea34}Ds3hLCz*l#6Za+wT&^0?qn3W{;hpDR0@wp4Kh79xEO80CSu>@3^) z#(e~$3-15y(bH7E+ta1M!lQWuLWV?VdM-H4Ei1pSRI_h-QeP@xeW^O3PPV^U=YfzGlLp7cD5DJx$zn>fUt=?FUn0PIR_YHDJ zjbG{Y3+g#JUeyFFmm5I8z%?0FHO6kWD{eRL|GY#R-yfJ~J3K~SnI$%g>WnKfrJ+V! zpLAr{C12V^Q72eVby!>`#INFbT^C~v#Q0!*p7I7jNGaObD~w>{;bn_`Fj<49Gt|7C z@?`^36S|hk{NwcHLcLo~UJTb7mbA1y3y6CY$M|ff^J~BbU16s^;%K{yK9+tlmYGNT zP}ItKlOvq8GdL)usMfl{wj{4xJ)azl27{ny*6J1(9@sRluO;Zj?7z;WJNg&#R#UiF zI{0+Q-T^k#$u)Gc>MO5GzGG$Zw*;Vi9C^BvVVceR9T>VPH7Vav2fJ&O!<2|S3$`?Q z1eaL6OoiduiZUzbF99B)XVKA0g9AxQf*F;f{p1p)vN#)W3zOP))x{$C;@Pyd`5CXs&1HWl7KTb=dw$n&KV0G0iz~I7JE7zyI|_(B zZ#7zcA=4+x9_PQX%^V83HnldA`D4d*E1Xa{oX@|w7}Lwa_c_`F6A)vQZ-&Of|>bcUcup z@QIO-yQkEyH&oLa=o?ZgLikBvDR5}RHEzmHhDH=D=KczWY0DzPirr7#a1AdfKt{NM zwD+(~RFxGxX6p#Fd9fcT30P^V3I~Od{$R;~mG31U%~3l5xN$$yg)bd4qlU0FT1iEM zp`)WaBlAkuC+ydYNn!os^Z?ymfk|?Ceymryh?q?j+yb<_0FLlZRe-VSxGG%-=J9++U z{;DRFtpE-T6DMQXMEe7&XStPRFG>;tUe||7=hx-N%Qg|GMQB}` z)8Z{c5-n9R(GLh&;h%0&va2T9y1Z;TvdU08Ykx>uHAar=3p%-|2F#IbY{)8BlMYya zsgMIby%?4f&B*l5YIYZ;RX%D57Ot3GlE1-xhcxE)#FNF{)*C2Og!4~Twx8?r(Lf=$ z=Qu3pxoUKhjbGdAxRt{lFKdhl`onz9I7rxKcl!%#BTFpSa2`@t1_1{xqNHxG+g<#> zI^6&ICvXS33z3yrx_a?9lKMW|<|}THyj9^Z<939C&T;L_alCQ6B?eGmre7BhDA}M? zIryqQh!0vYr70<#bl>1=we*UfZebAAj88o7V4wLZ?i$rpNX`wGTR5Gn&Ho(GP{=&M z+rc9>$Sosi5`McH+#~$6cMAvS(bv+!C2#oKTu(iuNH?S#rK1a)Fo~x1As@@%K>1CJdPqoHAfnu!_7h5|LopQk0QJqHV(41#Z6%ZahYNdW{gz%%x+ zX6~DTEtj2gSC_)-Z=y^`1$DXEd(br3?LEqYWtc5q5(*wi$kZXS)@xcn74?7fEubRq z(lIxkn0R~QVJU6=IrLi3CD!C?ys4HIo`B6E`>!=%B>umY8uA`ts10labWXVZ&XVn( zO}Ae|Eymg1(zF<_YO+M$v8eL*)?2`+fcc`HQ1ZQ=`rO?(6K%@f)*Ze7Eu{`|VuE$5 zet(OtOregG4FgT0+DRk94WD5k;ojXtn|WGG*68O>r>11(U)(*8%~vDQA-=u0*!(pU z^N%GKr~1I)^rzP--awk?89Cz zyp|b^Or-3Et)+4@Qw>gq*M=-CIr=YGduYJ&+Pn2pi%k@B1uJ5|$bZ}vT(q)(Yr|09 zKs7!{Aq!egkVPBZQMsm-uy~VE?T%3rOH<~rq^SRu3wdp8?4%89B>WA7#7@9S=(lvEL5=W0ZXsxSs~jj`OlhQD}L*fZlwB zw+JV1^O85G?ytHMt*d=emtIfDLY+-h@h-RLF*{MvZ6uWC2Ni7#cpcK@>M)x>-n>AL zQmmYbDvGeJhfnDLCe95!8p2dUxLH_dqZQeCeM*wEh)wYbQS6_kX=t)n{L0!|j`o{r zv4H~jdxQvFiHISxultums{H6)A77<>J%~xF{k7PKi_@QeJe;+3 zQWIyo_d!L8GD$1CCK(8Yh6U&KZjp501MNVy(aD%LblbD4n?4UrU-lGl(w|+GOnrZL zk)?~wsJ;)}hmV1yAj1BD22ynAEM5Ijj3E7*$P;PaHY4=%<)7*ARG!u7-yDcO*VqkS zZ`89Ar1BP7j2|EO`$D%mgCE^jyL4Dea@_ACVb)8iaYSbYv7o&O9A)%(IDl05JIwnY zaR@|?T4K#X-F3u>@(p9U-hEz+rNEk)p5Y&u3<$}&)wl7uzS27` zzIArIPQoln8y$DT?NY+`(^<;wS5=i%)Q&PYvuZoVn$X!xF(%C~ZcE8BJ4m$k{)7p; zap!R5s}nJ%o(EYdG-58Cnvk7(gQld`7stkwE%Q}7uhkGPW~%>y)xG<%5o=cdc*KIU zp_OyUHnSrH{aTm>JBXS40|k(+*zGUJ^obO1RrDa|L~EHxB?>i zGl@92&S1eNtLMHEa({U?$~bvkkMaL;lD{=@v}oRKO&a77II?%qvQ8a8q@}F;j)O$S zTxK$R*T%i;{7vAk8T}{S6Wxh&_;0MMcfGynZeK*hVgqr$ALkwVDHAV_R?F=2TDHNd zaVBzVJFqt&iOJ zN4yEkpLCwX#op&FH_CDw13OaOH-*B!#99w1buaEyHHPVZ6s|fGcN&pVp73->eQsjZ zHu48Y+O~yMzrG4$N`OR7@=|kq(cT#z1r0Ls*L5U>w84rFtr#W)0daSr)Wzf*p(Wqk zKzhG)UrYDTwyZt?-T1(#1nbuQT^3&yFJMcuQp_kk()6huYjIs zP(LYHe{{Q(GED?%vF;S3&$Qn&_?sar;z7$BllW|ry;l2B{7^ms>upU951Tybyr8U< zvJ8l_$BAUwc0%Y-?~Ox8OT|r0qRmCVX74ngqqzt{0wjm#YH@SI?SQ=v3SQ}-^|MZ5P) z__rUlX$7V)cw)s|d95nz4}&2r+DxNCc*3AyOz>Z97ahl7W}kDl^`EE4Qxn2@rLDu` zMhRUoVy{Q)wmW*&pqr8G&8cWYMHUAme#x&BD~9RjtO^FZhcU%hy*9+IN^migZ*ttd9k z5W3e_F5K0KmshkBYzg8FKJnC?sDK0eX8krm%?7zH5<3c9DRTZ&Ut z*y2R7%)#3+eSxgadN13+-*i`F-H>t`V7C1F)Wzl+IDb^ft6~rK^>SJ9$?`&?f6dh) z`3bK(u06&`Xn5%__x0*|fLHm!Z@_N&e>u-nDoD5MA}v)H@)q~k$HkCRJf?NMP8|6e z{f7^)LQ#xnWatUwzg0*UD#81>aJ0?}tf}Xe>=*f0Z7Tanx~dX|Kd*Cw!{tz#mD6rw zQCxEjSXfslEq&O(mu?RcmTX&>#qzF=CZ(gZaxV4-XMG8W&NgmupQPz@U&^8oxLXNf!bmQEiL7hwN$n;#^_q z!{JCR-JzP#5QR2KhG^AW>aRJPpU_z1z5Z2m>r9=^qTg0-NA)LWe!FMGuINbB}fIu6bYFkUNb2bHKE@gfn3bOE+UZ*as3kfsS5qJWYyWL6=#8BF7 z59br&39RVJKQViT?qFu(6~X(qj59affzrm)X_LKPH@jdW(H>^o zUl@u`dJj0wFNqBtNy$)lER-oR_$%Qu33u%=;H%jdg$3SDepZoM>Z^Xzd4}bIm1BG1#{U5i6 zPaZRRI6D8dO#8m8$tiNjOyy8Cb znS6%c3;0ZZXX9li>7E4H3I($g9|jDf-?(E* zO0Ci+b-Y>}eipj&CM(i>`-~rR`ITfvL7>jNuqz$1YPtfll)e`3BrJXbeV2bG)@s7& z|MmiOc>x}4x%23ePAl7$Z8uOwXiaixk&1Q(30cFMOaRipC-&rV&{Pzks=K4YZkPnV zXGWzD@_@s|I!>?pGM>*3Ik@HbZcZc7o!K4glH)^~>fK*tbZ;Nv%{D+kC9~Edg*8lu zHj5Ep9R%z@I3bX$#zRahx{LBf&n{AaEe3R)>6w~pR;(Qvn-(^`l5RBBtKW3tn^Xy} zc$2pz1l%Sg)q04Wos9$Tao>QE!I27sA!#cu-f3P!Jz_8~h>b=!;&ly=3bD-Kr}2K6 zoE9bQL|02@vh+t#TxPrS2He$y+9Mp35wIk@Z|m`V4>|4(T=ytBz5T5U*RO2a>9+gV zc01&c2a=;*UM`xqx@K|70yT6G!zc&CuuhX`?;2x&ps2X5DGR`(%y*gmv~A1Y);kAQ z;w1>(eC?G+o4d;qaEu(+(hYw+F>H99gu=q|*!|A)M5K~GaU2}7?pQ318gkQwMF;7d zw+$D2bQwRvFdcO+QC1bEiVYXVQ2Nq zMG_7p+H3kl)6L?Q@xc(~5MmkVe~l;rmDw2t&=RO^UE)#Z?L{;UX0)*Kzk;j?)lN0S zuE97HA+ry^JA*fu-TdqR=$Lc7|9*}!3Xi7rIu;^xpmjZHg3wejEEjz83bF>sy}wa> z@-zjw?GJgjUaFzTr-j=sb?e4NMdP=7Jkg$JZOR9;{X#l{dFl}XDeZZoKv-aKMrfTizj}QuwSh3Er&Z~_MEb9V(l>(Xr?P`3=^Fc~Woz%`@Dp9e#9#k)NIUr8RnGC^ zz;bBJRnnTIf5*kE-MWnSGc?DLYB-EQuQBcy4hn&Xy;0tI8DfLP-y%%>JEuH?hnvdT zi`PH8EWey}ezcAG$UV5B4ks6Bj~gx({Pf;OS_eabjKwnOiv_UxNL}z}M#Rt2u$Im1 zum%577LZX$_Nny`D!UaItk`PR{bJ3!LtSY0(dHt&`hCKWi-I=)m=jeZN{>(At%Gq_ zSM|S*Qujjq0v|0{RXLX<^4f&`t=NW3G0eZOs>TW=OrS5+m=nsQ%6sHlVa4qc8s$V8 zce=AlwQQpkr(^&OvZwL{T^5n3n`BzH=!r)!X|jDT@74Ys5=e6WatM)qKQ=j4Sy+c$ zJZQj@DB)?Q?=p#APoXj4e;f)eI8p88tPh_3)&gTZX^K5y1KB+vMZF$c~?4>7T zRyp)r2*8%G*NHCLpYD4rBaZZDG1Fzcq%v&&)3&z`+h*)Z$s}bzw;^%Wsrl{b`vcB_ z34S8G(n)a`KatcpZ-UL&2LMrEBi+d1b|MyaPpx=&BR^+KkW@0g$Qx6evWVnp?H`$E zTRR)G;uqij?AF9o|4Z-R6~uTPjb_?1 z!}BDp&GX5Yx}<}tU+?J%H+ZT2VT1DZVtYVvV>BN*iBKy_Y*v1>M5_%16F>m<2^j{{RmKtmKOYF&#LrQIuQn|cAXCZ61y9JVU@9~Bn7VkDS)|*aa$T$W&|j3lppdzxH63BoGD zcccx-kG_$N+xdQk>uVZ7CYjQc;a_+av4@B)u>x$wyGuIsF3oNP{UyLuuF+g7t9> z;nd!tX_ZNF2ugsr6rRRYDl)vl#zYd=XEfz1O zlI4q#s2|Y{Jl^@y_Vd~OKY}p^B5|H~c!--KHC?u0sXy?pCn5H)s&yH1eoXnyGIP8` zqic_en6_7qhSY$c-=>R3;!rQC2@5JP19L&n^ex0K9@^z>Qew{a?*=Y9EX2Un>N57_?#+F8+Q)BMRPbe`h7Cz}*qUTo&`Jt8tA z`Q(oB#w^;Uf7zL233>9IO6Dq1S0j!3r0`Ts(o-kx(SBjU}e6q?~vT0 zD%d(y*y{@=Gy3uxLaDFES}zJKJ$*uxza(sv>*>f}6*{b>Vx-|Jtk2QtvW8Fk$`|ZI!ViD_|J8-IR{ksV$b2r;ly|E-Gsizq zN|S%$e#!FnHBvUwgp;C*Snvk!y;5h9J-QsSz5-J)Amqh0!Xq!n*7o>bmivnp-9hlV zgi^;juapDl*)(x4+PvXehOB!6H~wk&m}m0R#}#GVI{Q(DL==zkLe0s-jL*K3iQ5;; zG$1`<1>ODmgkkf7JaW_YL{VL1Zb@VHE1e&a>kY2+sS4`k4%3Oq@Wml>Hy#(~I`OIf z+jeb6XiP)BwL!eH;D`OaH@qzbOzJ%|pHu0Jd=A&_wlFEE#2PWjEjJuke~#ReJ4q-t z^Tj&Y{IW>Za3$8!wp!)x-nejBk|iQ94mTJp?adPJb1uY{ zynnqz#kZ*mj=PFu6S_?MDCDS?&8Yvcz-naW_)e`TR(O>+lr9(m*%?eBIQ}NK7W#Bo z+1xp!va_CXQTo-_=B-*t-1@}kW%4g#Kh*%cOc3jbzZ2zGE1!itQAq@!CIs`Vm&dj} zPWE>U-vuwbBx3Un@U=PFZcJMqoKYfw_I;CO(jZ;f@w>KnVo>8VuvP1%1%{k%VCi|= zHou8|^g)7)NrdF9O*_e|)6|;SSi#NN_L*Z>Dqs*NjAo(Nu~WV%6Q5gR33|=(TAIb> zXy|=*`?u|$Md*cpTcu0*SP$aAIGhz z2g^X@QB?jz0Ay~zRaNsJ{tDj-eZlBAPsHU~T+hg>fM4jSviAR%ed922JnihI)+$BS zdz~bAyF#M6Di5O^bbj8TtqRypuxs@Xinzo*XiZs#Q(KIl^?HI3P|})`cUd&(y=n!A zdg;M-`u@7Q2qh!mq4~dC1?7IE*T&vBO)99l{P!viXhuT5mjv1oG^x=1H<6@oC0lXjwI^|f0!|j8uUo}5b&^UKMFK99NjulputF(&zOI$# z(2`C?45t6q_Tw-v$uTT-EI?>p$`{Rx+9I?+IqJU>h}HiA3u$_W`C*fsq*v{N%iGEJ z{rg$d&02htj~%-T?T+gMw;E&X;(a~`3$Qg|Y)t%jfImc)g>3KOkSR@cHDVOC z^^B44U2cj2%a{CHS<%6j8)qkNd@(${TGQjV#fqYDMW@CuBBCF*wY4ACuF|EQ!RsE5 z|B3w>IOT$E;T|*_{v1ZklB!B!)*z%dEvqq*+ME&f$`48XR@FQJogngoY$C%@Q=tG}= z!0u>=*=~DEuy!BgZT(HPTY*ufH>no3gy*+jDk3u0i~bt~T%LZfOLA5fSZMxRDAXQ< zvm|zah@?gzZ=$(<^P`-6kP|Rg#r~*zo$a=)6#T&VaH+Xw9Z>YXy?Cddr|0`ci`Ml& z;@L~I(O+eu)~#OFCNw_y*;@ulmY4llU*b*Irq)KQ`Rz-7;ydkK;H`BHPV74fYO2!Y z6%z&+ijP01r*6QK1&MQpX%ZC|R)o+vL)w1uY}GQ88yN|v?c?q$UFacwB>uy8yNY1D zDoUu_2nO_yeQBSV>ZT&>y5q4=5dv$Css<}L=X2{QQIp}S{di_q?ZRd9`Cvq zW2XS=j?@G5!8`Qw;c}kw_?9qwK~6U*^b@9A{7>!kvR{P ziJw+0KysUuj9H@}uYXP>&l85%w>PGO<3Csc}H6RHsFE1x6z|ZwIQWEO%OTS(h!X?keBIo?60)o^e*+S$>o{2NplK zq0QP7*bT?$SXp>Cq}A3^Wr?+C3b*4MYF}o}Tq^wek#%YThlF|EM|2b>ycMSvT0~SX zs4>P!)noT24RS|{d;Mcjq;5~2>*V>Ng?)n)Q~!0H+1#@CM~=hS9alDcvEqwYZm#mQ zgi{D``CesNWPrbTbFYtR`9g5_ma5=YB2`XvWyF>$(NB7jIVGO;UsO{+3nB6c`qlXiC6s-Q~v$E z&0%ppd7J09PySRa-jbPuTW!}*6+j^OeR!C8F#DwF_{bV(JQsZYAJMiwPEL#_d&_Xr z$_G*HlcoB=9C67{{jOIJ$MmYvjamnfoMk1dnjQ(`cb*qd9XnB+qD|_Ab{TmZe|bg! zC^uHr-5qH^>|~?1U)e~l58-Js`*_*fGI>u;ypAcpwGxlYWA)_`fliiEKYjV0BPE%! zxeaIYwc-ug3++|(u|5_X-Lb-%uGX&dyl9#*VZQL{NDxZVZtnT|H2iLFe1zXvFFm|G zkL2kSqVB5yB@{ST1&kN8FQ^}K5=rt$umRpLmFBgIdpckiJd-EpocG`QqRVSy++y(u zSm5tzTWW|q@FvLI)A8ALNGdw@^6zS;D)%!iBr{&kXtr}ui?8uY%)@vj5)`L%A?BGb zQxmsolI~O(;FAlMbYMJraR$wM){~f=ylPvfu&|u&CV}3AdNE9ooNglaTrXl#9~b5) zlvm2CnTyi7;2>{I`pUT9aR}(ztW?rVY8W6f5m}`EP{z@F3V?mUZFLdHH5M^ZxCWn16OqdhXtP%9Jv!g9sGve>uFkL8cBj@4` z{_FjYY{G|Ng7P#RM6t^!;x;u`L*i@3Wd=Fn0ZX+><}w<3!QwI`-m8NLmURP<8+_|w&y zj4zyFu1trr;j%5N6WN-?If417O#0%o@Z{+oF!F$9PlGp1i>EvqlbVuR_WC%H)d4Fs zHaXh-AaeNj1J1PZ`=#VBo(&}5GC&5G5FLYM-^-ufT#bSSo^D7u#7}v`{nZNj58rwF zs+&F_ajS($iSF&e02nKxO$zNR=7rp|N3n8z$NjF|_C_=jvdBMwQmV$?PN`Mh_A-+I-&>ab$G6#1mGn&`$gEq6++?9W^J z{@;0Q!t)u!ibp7PT-y@`i-RlTO=oG;`0LPSSW_fJfY$0e`jnWzV-oWkoL-0kc&ffS zX|9t$v^0+yJ@k%&sKkuQ$Cs-`^=Idtz1{MR@-LQ&v>qOJq4O;SB82A-Bd}qO&pcw7 z{G40Fro;@$2mK|AApKZkp4v*O{%h>U#k~FMKTmV7Cx~!N7e~le&}AHXPWG5K3!Ej> zzQn093m^AhLjcj?P}bY*QBv0Nyp~fXJ=GiaeZS1dIVaXrdUM=6E!h9;I(`EYq>LkK z41#lA9Me_?qk^9ZMv61G_OU$IcNy=Bc&m&tH8snUxaL>C-AGs`?k&8%s z9SIHgnPv}T=vYcp!(n``r{oM`Yr#i)2g|1=!fy)Ar-?zTIBg^Sot?s%mUMlf3qgt-O_dTv{KmFhteBM_^6a(9A zn!dilpG7|)b4j#);uW8fna6%8jKSCb<%t1vBRU+?fKqKOGBpt%Wp~p+`LcfGqhmGn zdwaP%rC%O0i?uG3v*W$7&S5Zi-^NTHrew7YYi)Lm9q}{gyOl-bm8V&Z4>L$NV}9cG zDAr2EjW%a$o%AK~-6Zsqmzi~aDz1-)^PxROFgW7hajS+*5RW`040KZ4 zwzi-eNZ;Ci9BG_K>0tWFhC`J6TQO;!R`c`LEzQi3a-W-Vq!|EN^eK+x+g@tqGgwQr zJSaA9o(;DL({!Hfe6>zw=`=*2bZ`X5CN3} z+pnj$e?t}Kd}FjsDJ^)yk5KT@m(5l`6DMsinu=<{(=ekizF4G_H8x#5YNK#G1f?-2 zYZda%ZNGASWX8a~U5K0r=?l9k@;CpSh^MziH*ISF0+=`NwcI+XVhg7%Q=+V2_astR z!+OE5BUJ;8nP9xpr8!?PAVtg>6qaF27!70>O`qbcn_0*n$!Xus8qy|Q{%LSd|2RlY zBEMVu`ljacHxG_HeG(q8;Nph{MzzkZ_j>McY*gx|AaR3Qd{zz#pO*%yE3v<6OyJ-X zmn^D_d05o1ST&$toGOzVGYn3&(rwJ%S@1+{PJ5xgaV-F@`gQ^<|E(INK3?vJ8t}gy z$h91zDjNf-A%ht4VRfv|?daw6@TsL5@ab*Yj+7eO#!68l>0p)&*?DB-FE`;;)wBsUu23PpYlAThTML%DcnSA|eTmb<79fSXXrwsg|~+yGbn)7`U${O>@-zJy&La3Z_Bw6FVSFW&H?ZMaoj1{ifUVRtA@}fZ=*}0Ob zvc|fv7Tn6cb)~d|)}GwupZdSzvi6n!6e8X7c$=R!9In=eU2JH(ChP zcf9l?ulxY(2eHC1v2USe4+t_YZ}C2yO=BbNfw=8~BQe^>bNMaKWI2GY)^acG3D?`?3tD-=v$Dde> zQQ85bZF^!pF1}T64Q*some*v-%2c>azO;9cz5HB|QY|t#V?`LiAon&kJBHBK)}mE> z?B54$ddsB)5htqIB0j#^p2ko3>aipd9@j`to?82we(~SCk;J}V-E2Pkn1`2sPK_`9 zJMyb%*5;?;kWMvN5Xq=|2>MPI!}Nt2+bZ?$GsG~m^w%$+a%|SIZT%mM)LxO;Guas0 z9(Z}p#A8rA##QXti#Q;89M@l3n=s_ln>NXx3H%S6s5$d~z zwY^QNvLT{HaUTiO8I8c#V8*H_-LO*ZG^DeS$-b=NdpY(q72U|Qerwmy#Lb)S28c44 z$G9x%V1g(!R@QzW)kyWsHnmxS5qYUy^S2gfsl`8IHWrEH6p?#-llPBZ9kX(O;uG4j zZuEpy`3xUhrj3kW++OnB3tIgPEpR0_ER5HTVHicARp`@b)H!7vF2}cYUhbaX6R&M< zeE?ie9_TQFt)=iD5vIEwA4Lqq!>Uj4$Vn)`3j_gmVO_;?0C9fy&H)~q~*S{za=zQtmLl4W~p-Xdj% zWL2rzSj@hADKh#f$p|CvxSna0`_qFYf10s?(l|{>wVKWSM~a9e^_bGIwjy?hlKftY%V^-b z7Kv6Eu-g9k%=*O*ORBIy(hS+L+ zME#jQh3O&Pveo^$T*_OyRd#{MM4|dj%$L{E%w}@e-MqCd))ehGQN2{vJ+b~j#1m9r zXm;3SdSK4J{Z(fAheQaj=Xd8i`=oYXN`CX#C%U3B0#N=VwF8!_AV3N?6~xZ7v25P9 z2f!u-E~>S1e)+!v+CqRO`y;6e$yivO8i{s!vQ?nG{I&y|vW_wjS9jDVytFFzV1iL) z|1vD+7DkKp(}GlIsLs>aB0O``DQOCr)N*0z9Fi_wTJ;zA9`x#wGoN-SP1KrIfJU(= z2NB9hSr;?*?V6#r`kQWJJK zhO_mMx*jcO5pY&AA!PrK(QXh|7;3o7Umd!a)nzA?Yn2+R>Mb{fXfIa#5+d-JprU#`*uX03NiqP9Fh? zdp3g5IDJKbCboKknKqNB;MU+0)VSET5?O*%TuaDW#6dfqA`d9vCDhaeEw47+=B&r<&hPsTCNAUUc+f+0t zL@M;*m;~{_H(xLS;VPT5vtm9@^*uQq_m(Gl`t-7GN<#A9*!EPp_B|lbdHk|dY_X7-r0${_qwaU>ZEd3eX`|`XK95k zg8RF6Zop=e#+z3owzTO5r!CIy-E z@8zI~{}uml#lu*-qb)ys*VKQ0^##2Q7Wy(6Bt-XW;CRKKR(ICPq`Tl{_tIb1GioZL zd&TKSt3=PBfq(&8U+8K%OS}!bIr>BtFI;f$fMBApVxownPI#$4-km$(q=`BtYo4 z0NE@*7o;?gxDm&hh;BlFlI|^KE=;`Bdpw6sTL2_i=!O`C9sdP9=eL#T%3P*jXn6;! z1kY(+)c?B~Ei*+N8y@0pnh9qpsO<~ijZsW>h2zZ>vqTpnY;S4dn+5_sLO2S3b3PfO zb-U$;l3n<<(>LTTT;I4zmn!E59n8cW6U2f&|Mv_IJ(ZLs3(D$B5+q7oe^?m!sjti} z10h}^!>lH>RSa`Adq9i;Cx`Yq)7tLpsgj@nAtoOEa_y6TOXiGAy0ns_L|a2YEgT!q zZRj1d6jJ=}d&FJTO`kwx;F^P-+$2;%5`O~kBv9THaAb-?~v7NS1s^kuAzqX6xPpX z>3=vCFlb5X5G8KSgg8!5Kdap`z#kW=FHXkuZL`{-VKQF>!wF<(-Xk=9lEpe6-rfyL z!)rOqZ?*W&bJ#vG^Eqpzcr-eMs7NAq8-!UwU{xEGO_Azu z3O_OY*Qr1{$nbGes;!QqnW4NqguwQNJF zhM%!Y#l`FW#wose^Hwrh5dTZ3jdDGyGeDIB+e-IWEolf~e`y~VNIU8EOaY?J_?Ss~ z1WC1kY&GeY`C}_p%zs^%4Uc-MyiPL1T?TIP8>Q7hS2a!GH*k^Ho_)U~kvU$_AxC+Q zzURo(z9yHGg}{AiN#$OYnRV1-hrKZa%6_QdX_J1=GR-np;QK4N4OU7KDso}6+lsu^ z>&9mKPnpQ*d*jFz{YE5--B-Tj0 zOZu%45Tt*FGOzVjL#9;hW38*!(1<7J;GQ8~EPDSTpn5Avy3FNh~B|HTv@pMuinAOc>+U7{22d&7GtT5eV z7uP&RZ{;J3mzjT9#$=nU}c$*S+qBkfV^bp)|kY)zb*SmPaJ!x0EfO zvwZWiWGjA}XKAzDR(`**v`pXMLtFB|)>pzLWUhDAsI1U8{lsb~8$K%kVq-8*uw|09 zYU^18p&QLHi~FzfDlTj+_qogcsMnED89r73FcL|6Wh=N|xD<*O!@RS6P`P$5+4Vl| z+7{{iyfU^v!RFBA{ibhkUSU%hA#zhY_Dp1U`of?y+T9|tY_^}N&*q-;C%)ew7upf1 zx0&5_+})2}t^z3!bK^k88C$lfniCk2L=53jM^-}+rDLmz>oYsPmQk&FO(AVC;g+{v zt$SPUCNY-`j_P>tti;TW4ZSn43Rv%_s!!b}6>XX`AA0$_KVpXYnh^IbC)iTGke-q1 zY^5&hL4Qs5I`z{@SM2F{!D&tuNo`_{D~m4Dyjh489MP;>l}k{b^I|vq^e?|(vH9!f zMlL^r#-2LIs4d4)8>H4ksC%hJD*=RcKr!Gg?VFhkhWHn`Z(GByU*Nr3A~~dQ2>!bbkUZdLDqAV)%@Y4|F=V* zuH9Fh>R!vijoP`pdKwotO^$hvv%q+^?OUAIoFyA%fEf9>#89r&p=F- zJ?gM)zv9mVCcp!2xJ=4=8ar4mDB-y3c9W;no8=Mb2F&g87U;l=Rs`yaf-w(N8@ugSUIsC zEvlZ-%FmZa#Piaf$XXy)eajc4eN${JAr{V1OS3Qsi~%oXuK)joz$@KQ`$x7+$M~l2CLmNOz>Au_qfx zvT#CcU8}=aZrTvU>*#jTuZwhQ`cHN~KGn}4oCM|65m8how}*JjsL_{}g#a|@oj)X9 zw)YOVpr2&4(~Jy?8?Yp&-O=f(`@75|=h0WC<3Ee_h+b=!wr?OSygYE;#{v??x1^+I zZiov2=&G!{=jMqZ8tf~~d1~R_qgv`9@cCJXl6b$ z?^pC`%Y{C)O@cXQ=8AODF3ig9M&Ck@bVQ2~Zrl+fysh=`7wN)P?oi&4Z3|kMsP^b; zrdud?go=1a*CEuYM5F|Okla|3|m5>gEi;zxO8@2!y6T=w)F*E zQK?im>4-G$xN^>P6mU-F_LK)yku=QJ)O1i-<<#$29O*aBr%!m~ZG0{tX`V3@CiVV- zJ#OF7Q+Z8ZM;}g5p72V#wo?Q(UE(t4k)n1ZGhpt8%JwWi%`#jc@*eb93alUn{*PCtXMAH&y{E-x&VU_ zDiK=%d0E~NmNHaY=UBw%-SmNgz*0~@HPq(xgPEhi^pH6qc6!K+l(sKP38D9X-?4e8 zn2RQWyG&}gGRr+G^FXQVc6VlFK~PiE(U7Go0PRd)g?>mBk(47vcgWJ%j1M*3D+<>R zTDI+iTf!w@fbIR_cp%iAyYn*K<-tF}>ryYX`E=Z?%`b3Jt31dZ8y60gDVyVY{Fi{R z*U&8NYL#a8a-bg9hdD}7cR_z6>(Sa=#qeuYgnEBJTho*&AN=ad1M^LJD9NkSEBF!G z4`ASsXti!C&?->A0}hAVKBzN5^W{t`-36St_A3tzf7Myi{Drg;tL1VL3swhX75-mF z2N^=<02;$Gf>ZbYdGMO8$A7cD+}RhAqbCmz{SVH%h|TffL~UOE?;8K#G|hQV8}+gK z{yK*xevd+9bbU|Kqix-)bq5A^-FYJKIR%*~R3S$H074?mo@Ix|6l_>Y${qc%R{Oe3 zVd72sAKCJ+m0rG1oJhdI`DNX4Nhv2I3{{-T_4U|m`Xy><_+d3Xhsg53HjSdOw^p`B zCG{AVI{(2%93eg(HI1nnddC7)5R^ru4un(Dw>5+ahumQyl+k0Tu6WnLtJnQsr-m~5 zkDqqanRFc~g)KMg^sb>9y+ZIy+1c(VPbpN?{c2z~wgRlnvau4AY-yx&PC%?Y4HvhP zH51;Hx3ESj5%6Yk)*MX|5%Pkl5SCY1pw3$_$k?1P#lg(Kz~>0%s%N~v!ry*}3U1Wx zRAr`D9sl*6F&i^flIQXA4x8%eOa`=lZcH!tdmk4rI+i5`zeT-79%~-{Ci4562&K(< z;8E9GgFvXHqf6d=_1Y=4y54AA_GPpeJ3nI zY{j4u591D|GbW;cET7ue%}I*zfiu}J^71Q2P~_=&nfR>Dlbw{ih;BM3IrPYP5vC#F zcq{DuB!ZBl7{d>H*_O!Hq0L$I+}zyX@|mw>bL+>)WUJLGkF6SJRiz#YaY6tnhsvLF#QXR@PbJ+>7<@6YI9X-h5%xYGp^ssI`F ztj)Ab3v3;NU%S*h2g3M)I2}DMTS!DL;!+7&5;zu`F-trL14Q)^uT29`u&wlS1@LyJ zc37R3yPi@UF6u@3F~#-cfR}n_LEl<`k6bC}!4OK&jb_jt7t$DAzDAO>eC(OG78+GL zIqUOi6JofC`~guEf`Bps>A+d3V;BiW2-zt0dos$JldFHE;`1U6l*ZZ8O#m1Ypy^b_ z_vsQk+e3B_5$J-N1bqN z8<0nvy>00Xy>@?U)|ds?(oX7URgxibHFVVxZ1o_5Vp`7Zj{7E!Ndr_`^hgSU=k=@VwH z_!l-|M0UO&R{D-SRcgn;c1kfI;JO(&SP^6*?6{VR;>>4+8)Ok}lMc}irNjwVlM&I2uBc@Thx{&xyZZ(k$@vE(LLfAV0+H}2 z%k|P8>XUo;M2Ot6l75HG!&rWQvMdjnFJJ);cYFS4o zQ@eskLNZ>Dac6@9t3f&|pQ}>^Z!W>53WKFCj9T%RU4U8&xHJZwJWxSH#C>}AJ$?PT z8=%hFFUP;$s0?^KH)*sp@otRn(cu_@z#v*RrxR1~G5M6_gEMq9`51#mS^xs_ggA;oD(Re!Qx3b^iUu zf!x&yaUAgJISNt;{u=}Ee#KJ|bg+7~GkQ~Y`h1J5dEKH#mwDa>$9{dnqt*vwRHK$a z<>f_@&QEl>mbc|(e=BTq>JRJv9C9Ch;RF{zK!C;hvSr3qlSaJ-8rpr@vdTPEvI`0f z#E!!L2v#rGklI7mkHtv^Euk7;1q6nKiq_a_Bv1z`X?1^HL5|LmL=6%K9=UuB&Q^$2wgTD+s z>1~HZX;I1r?Q0>SPsMs8Dtu$A)36na$ zJpM=I3hj@|xNJe=NQLYlaWv%omAzSxZA&%X21e(d76O?OB`Sz;-rG4({++H#{p)&! zL#PUVLWu#ANbR3OO%~Us0`PLLmN=ca4;1!VIb7T8*Wa;Em|?=(rH<1{PiU;J*Xk$a z8}wdt)esaKA6DyfRc@cCRc*FQ1CMPpn>31GWDpbSDz~<)>qa9~OkY`c8iXpSV1kme z!{ljTv#qeBtn1p2*mc=iQOk>Z{7YcPRhA#}smv7hQ?dxb958$X=Uwz?NQLy4o%$d3 z=M_%cXotox#=)impyL)Bqu9s1Q?6(?7VHz&KRGq{t6-y8nR1Eo7P(}>R|Z@U`pAUB zj?Har$7~2yn1+g2sVNOgqil9PR}P;EB4Ezi?|sO%49Ns`*Tx;!1(_k5%6=sM2FeT= znbuQ~EyHj1PyFs}bPD#;<;|f2KN|Pu7n?qpLu3ImY1m3&G5duoYZ>eg@oVn~{qc-# zlY#M8KkQRugGvJTwe}&j`OX^bB5+x z>cCI-=O|Gc_ps;EZLMf18o7vD`r1MOd4tq~nR&K($@I`i7mi|U-TnErtKL|hsj_P- zCx2IKAv zRLxDYeg2;8=gAu<+@#Sf(r1frJLgm@u^>|J2O&)W=yZlqNY5V&Sal6By6D!Obere( z<6&277!G|C;npLm7jms4d9oP~sn$ic~bFctwJ60w+YiEDaHqkqn*CLiI!Ho1C1h(5rMb8yu>Ia6skvddK9J zw{5|4y}!MpI5=lAs&uu#?4v}sOb|Wd^t~np5ttfZXgWGaxSg4i>dbu2pkMeHVxQ+O zfGd^bc7U|6gH5TgQU51N8h!(!(2UX%)z<(C%k4B%^bj<7*W za^YujmCF6p8xU&hT%yAMymc4#u_XLz;k?|ZD5mxzQ(UQ)OVI$FARV-P<3 z@dfZXJ;IHeTP`hFyM#*mYXoFxKZ4;SCdaG%`&|E(UZ+9_D`(vY?po^Un#a@%PQjB$ z9RC2}VBy8iNQjgJC5oF>Ly+LSrx)xvX8zYVAoj{6Vbz zEd-oh@OWL?(%d^$GMRoO>`~!2>)cbz>ILIDK25XJQ9f=Qd?O3`iLk$OWk8wn&U-!0 zqw`B!seRe#bXyLb+Uk>z$r`6h_^BKeX4~+(^DFfeWN(?n9Wc?!n0_q`ts#~Yf&#&D zeP*HdL=k-}pdRdT{Dvky5l8H{=lQj?_k)pr(g4-=jPBY^I{_)_pBSa2$!nR$VZDMH z*bSDVIl65^SyE0{WX?)71 z0}%u54$2%yWS62;3J4OXBIq*i?XYGQ6W=E6!nf^w{?=2fNnBz3%hvTrH!DS^p5Wod4b2}rICn6yR$+xag)5(%12cmpEX@njH1xP)TmjC$!kecO-#pVJ1~L%EY}Tws{2g z{khk^40ATYhMe&Ev+=5Oz-aLq-w~b`0hZp@qEV|?PJnkpoB+ADj zri~VB+g6hUXRgD7_t_JzfTW?3eaxPrvV-x(KX3bvnSSi&Yw^(K0kXdk999P`DDaqb zT$)<4d+T7*Bn=Z~GqRImhgGc%wH8HC4RW|ueQSVUKxZ+eZJn<{Z6j$nowH^q`qUkO zk$O1o4qoaj*&(LfprUh2%}_Hg)~yO9f|eh~ncB6aji7hZJ{^_V2Vd{Kw{3ZS=&|#j zsQM+m!EgEP^SZkKXq6@Csgyd5h_3$;(+kms0-IG=g4fhbpFk{A3}HZI`uw0jz5HXt zwAv|Z8^0wOl#aoFS}xttC+Zf1I=CXG{$jqYwj}pVElv&Ttd4GaZL4J7bPFA8aEK0& zk@!X*UU9&#k7>&hJazA`wi5QfFY!Z7xa#9I{t7ecc_j7(5$cp^LRo8>%_G zkv+>>F8}HQ6y~i#l*qo2Up|LT6&sh$=k>L|5P*Jv^%B%*4+^5UigwWtNsX)65#E z1BqwO+bFx8=H&#$GZMNR&MVEk*|IffeJ$NfzCRTO$=&>{H#2KrPt#(yLHiI$HmkDY z_Q@G$Qt7K6IB}pFhPOiBPjkm_h0L(Cx=`r{m0yoX%YTvL+5gTa~5MoAEQJm^tz>j>@%acFy&&jrb>!szbspJUHyq# z){iuD6$9mBMmWGrOL}+Y-%M3T{ejj)T)0QYT?dnf3zYpGVC`T9oH zR=D~bUlr5VI=HgvQ@yV}M>k&6q8C;_s9@%Bx6-IjL@!v0o@0C{#oF+8ele|j6TH79m)UlWmCsciaJCSGsf7+y$}&a6aK~ALIid zR=$UByPXDe|ICmLwyvNW@CSo!95=QACOF5(JOnL~TQB7tQ2eyFhQO$bKO& zX#L^Tqp1NKowY{dT86aPZeLxjLHy0|C}Z_pu$v>U9>wIy|n4surlhGNs;<%Be)dnj?RJno?$R7piRbx34A`UW}U9LUDdP2T*{POIn=~ z&(2VYPa{W$P>M&D+gbWuzLwD1`GMs8Ur|lc#v4@>p}$$X;`v)ph`Guzjb$WT@9-jF z7_Q|P48z*Cyiq>tP?-T&JHD{1h_SQPq3iMwi$m_uYfn(|ebOeEFL&iE5m!ej^TD1k zmqRbe*^*9?);vU68SX%>Ow6R0@U{FnyRwZeVw?}W$irg4HNPxWRV}qFvB19fOT7lF z{pQ(&Nlz^7gP#v%M&(mQQeVzLI=QCIFzXtR>bll~@%0J_%L07%QGSana%!}3i0_>; z6a$vL&{glS{P8#8U%VGvT3&m)$WFeltNl^rXg7mxU(3AzO1gW7kOqJq837tIE6vUw z;(-y$=QFkPZK)r(bzqa=DK-9mPv7nus|QdSWJv4E^px;*n_fu)9KwyfJ>844yN{k5 zJbAoDE=%UEOEZ~{&eisIE30jN;C+N*bakGaiXPOmMrek=gWDrPig*+Gq!FF*=s) zAIat(#ca#`Z(Ls@p#hRMEs#i|QWhx*Up105Rg`?&`VG^Iv2iR)Z{kx(#9k5W++*4N z);S#t&CGIYgDoGeQ*FpSew{lQd`Bz)FauU%KE&qR^HEkNPYP4}PI zR}Jye+~WbWJ)p3!4PT`r7ki`BDJIC8eX1`t-|>Y#%iY0|Q(R%FL>Bqu5L@>v0(NyR zZGMruqg|{I+qG??ZZl50HM4yZ3gDDixqfHa2ZH)b(|?cIq{eL71C-01Mk1t%zd3jj zK|*26pw`!1a>9i1Z!~@Lt27x7g!6BvP8^Ir6hSqKrauk|$smRhD}D2gQL~_{i%$mC z%VW%>>df@aqr_;V9PR(rbkzY(eqSFEq$NivAxL)$NGK%@V$dBTjF4`SkOt|L5GDMw zjT|swjvOE*s`7%&X!3jK{sT;O+qO$YO1Rps;oIw9*KMNdnJ<2U*R z&fl)zzOO)3INeGzm16562T#A0Ago#D{di}IbUOpXf`GwH4xl2q4Zzy<>^ZQ+d`Ns` zSC86Ccs|O>&Ko>;^wo}TvpjdQ(u}Aeu(N7G&(0y{Qt=N4egluV4{nVKS%UiZ+saU2 z)PZ=;oof38W8Fo(>-nrf`qp0x#x>Z!vvKE37WyzOoeJS_Qf#pWU@^TR-Q&E$?$Xu@ zLAILj=hkC9)~dFX%sF0Cw$8$7DrEyPt)RL^N*}_cAWrw-S zoju%!&pdx6?ke#7xf5nAP%e?8J%p`m z4iAE+rgQgtwR+;h_llncZpvFaD18lj9stZ;^F%lA!sQm_Dth0x%wzMAK{cPwqAn7x zv8L(hg_Ap~;Wx-1t72}DrlRZ2Zx{=aTC80XsRT}&!-DqCLU3w2BsY97=BaBMvSI{A zB50HytXb#6z+F|(2!aXgSFL@xSczSX$5J#ooi#+2b(NagpL3cr>-INy;U@*=o!`~T zm2#o138uVh%!fgDrggNT{XdV=u-nB#vg^jOqJb&$AyHLB-)3QD@{PPU8I4vAfvV3- zK4TxUJh$qwcvOiPfk$10`a`13`b|p8TO9)Y&XI}SMMnOR`EhG#+EKlW8DkN>Z3UQU z#0tm3iHniIQ;$r$JbJYwAszBB`I+G@Mw3E9!s|y0Z@%Ii3 zuUb2z3^S7jMCs|-IbyrNqCGg@h-#ZlrC^(3PU3B&|K)n?6`#K5rA5agndZWNwqniC z*+iXAsSkFp*?QX^IfH|Wy*r?CI*?0Da3vIZfO@!A1id990>-r;$fNA5v~Q|B>u1?z zh`=nwkG!AlsLmFm2@lqPZ6rO)rDI(ubE*ES?mr$I#kvh}&>G85Pqnk}cJ6Xf1iREH zIQ+KDj1xWLlP)D^^ZqrfiC`4FJhx3M%WP2rlm%+@%gwp%opg%CE zC9QKee7ETZ_n84`Y9HbU3_f4-YAjwu`l1yBB#rO-QVB7EBbYf(1qW?> z+mTlC-}t}a`^5V`58yA6tngC)$`HnwZbj8maB(R7wp>!7$QLN|&{tkFNA0CcYq80= z(?%?s89tKQt2!(99x|~s$GehgDot`g#tm-*EY5lDH? zHM{xYpNjseR#9Zz7U+RikCoV|{ooOO(@pig`{D%@C3Y{k3$-~{=1Ymw(hR%tXku%O zo0UUUhYSw?On8*;yi9!Yn?DO4F};M5sPHam!cLJ_Ys)iLxpezeO#E(ZhCr-WzuKLaTJ=Ph4267xk=s^6QtJ>%H>c`+{ zLncfolRK%a>J}?Hef26Es*JRWst0vr#CrX0`|72woORNADS-`F?miA9M+X4dV!`Qu z=8^HO!5Sx{x+SoFFiVHw($pcg+{R%IbgSOW4Xc=XA+>%E0ziwg&{x8*e$L5w)jYU6 z+Zw)~7l7N^+PzR7a)WruYXUw;_t7o6iy z-sJuo%A~A5=cTR@`biP2hlsfr%-ZTy+ zjn;8fB1{Pq`(5D&!n#IA&W7qUa{)%L)iZ>H;rh92uN)O$JWAhM#qprG!QV15eC^|LHw0ngNn+pG}o zwt5WPrx2Ac@z}<*qC0|95^Fw?4kU~if6M?1Ipg}wH$khEr^a)AobxSbI9h2joynA*R(oh0c%Hwg|Rh8%m(w z!93JO_i0+%E6nqV0-E}6|Ki%B$L+iKV7Hme51rdwK5Sk_I_O-$t9?k7@8j|UFd-Zm z^~wA$1t!17S}XY{ff_!QS+~(5wOC?nn}SN;i1TmghUCHA!}{en$~f4{iN9&cLV%(G zt(DKoS=L@LfD^n|6Psq(L9P%2DnFh_yDzzFtScZC?UlwK{Sx8VwQhi2`j3*S`hue6nV=1%ok ztYR2>qGaW|D(YJzvNP|NP^f_1^qHy`pDBdV>2b;3^~p*4`OUvU>#!NB+jp<6;Ozr> zuC4OBQz>mo|H#c^t9OS20+pOwLPJNICX5Mklrld;K{@8i{=y!dE zHzWMPwknpEo7oLLv&W!!qZpeJ^G&p_FD8UNkQMAo!c@mvqOp0_)XvoAY<9R_>gh7pevcWIVhss$N7r@G-KXj@RxDY$5a9Er%|gCshqdpJ%p*U zBwqmgs-Pq0+o%!t!IJ*GCoC1K%#G32X4MrYb!dwu9x)M=KnPxL}LN&{9Kvt67K>lJ3VOm;%gKpYq>7>Wo#+=XA2~%GK z3=EH1M#x@-U@!r?3X16tD(;@`)8073?{U7m`L_*y*?J0E!;LQ8lZJ^zel!-DeKCXp zlA2O&4Jgfy!$)% zb&@z1C!h3Ef1Pquefz22E!ur_S1ZYa;Y!mnIp?}M^$6B)wu3v-d|&n(znZMpPG9~A z8yO4wQ){j!OZlU8R&MX#9K_Cjb3yo|8twJR?-=*1FA=*ykD#Q@0mmGS0)5ifvlfs) z2i@*JK+xffr^o80&gJvHzeVypM0qm-`zLRAH37QCG)F{)yQ4s>*uy0`hCSl>ER(=AqGii|czGW#-u5&7*&`+!B)tCjd z>BH<~+Y1Cd4jf^GFr^kV7OOY*dtS-J7NaIBf@)PBkxq?KNqSvPOWbzm)?TK3$Kj(e zPd=+~1s~Cm_Dk!T(rhUci#-8ZaDNyV4SL6<<+)bIXrx&O-h9TBDa#o1@-EFs8<1Dn zwU|k^{BG;B92DIHjEwbmDZ5xNY2q)`BjJt|gw5xy8PYV#80pd*mo67&K~UPL4ky^(VGPPUPdE%LP|ScLWYYz)AGn>fDb#q#q`n-^a^qIk5jn}fVw1$Aw)PQ;B5E*VC5_s=L(&@V&B?f{d(o6v_rexEReL{} zOpCQjcvv#w=GI=`u({7Q0Hn`VD`m5&8WEgB!%h`ZXrEyzErDV1+R^&z#gwrpZ2jHn8oBey z%Yyfp?AKJ{i!D&{y=A21B|FSur^UQ%E({i=LtF@?(44>M_amz_)`g}8FQ>a?e+^+fmvSb_S08$>r<_x zRSkXI6X)BLN@0xVf3!q~2zghY*M+$NF537)&%-(Rn2nx0)ZVJ`^xSM659RVQ+E`$< z?qewY!ReiR(VcpadOB30TZKSXo5}YOmBp~{Ix@QpkaUNvcqKL@C@I=GYLr_|q>6#F zu{qvPE@!qEjY>zoYdt7nO&$Tf*@{MxcRaS?SpkLj$T;~D7TRS*%|_wDy&|D&gY!Nq z7V|!@?+ae7@fjwLoiPK4RZD zoG!y$`n)Vl`m)yxd_Fa|hVC@H$550X4Y}gLjUz0@d6}WNt`(b73jJn|X+Lq%0b-j_ z@*}?71IO57)X~Gh(q4Lzx93u+$Dk0|-0&be5RQ+o`PJb=e|~)HO00mQjrb?eVP;F` znngyCLO^Ue{WlhhY-l@=c1Sh1zu66Mpcw@yqhTY6u|o=aqoj7S?$!_7br|}UR@jGY zT(n)WYCd5EGG8;-VMW6+qu> z_|!9ocr3l&S1*1=V)AZXNK-T^@91_i`%uK7&@P zvi>p8{r581g=a{o0%A}zW){L;4$oN|xKc8;o2PToqWFP3z@hKni}}!jB4(I%h6|Rb z-p%9OCey(=D>Pf(=eq?D^1ev29Lt;hs4BsywV!|4fe7+Ju`9i&l$>n4b+ftBPa$CZ zU0LZ9+bs1m1a#*sep{Doi2gCAy@3CdPvE*A*g6=o=Ywl5J{0}}J*~jel8fr;2*A>6 zOsNO{F7cj3#3|6kpe6AxA%>j<^~+g$7Zb^Jum+?8%(^SPm6(xqdZ9XKBVw(X-_M(- z=+*M7T@=Bg{^KAdGYvFTViNVFrnBVbCFoQpzP|su{P!BK58>smJe+eVSN7C zxN*u>w^tqCo;0@rv=y&_@j1x9-Y(TGmC;KG-UvpNXk#85#V-ldMZoY*d?yrEFKXQX zgz%Rd@Df@^c#HU{f#sfB-S^JLqiAb<>cEQX0EBv8+1uQ1-NON&ArI4^5R zNhzr%>vs_~SVf%amT;GV*L)+mTsNj-0{meXP_@Ha=8FzhYIW5W zwO33U)mMp3=`sMOH zm~`FPQcqj$HVU?apXgeM8V`53J#f{JD1z(UOHcy0bUs=dmSF2_iXnGGg9Su8fhad z9i#a7S>(lWNMt>-;rPlx7-p_b{5dG=wOzvikI@-R;p`o=+!0b{Pq5|of>rXBaM@_0 zexr3FD-MkmopUVicS=1JPmd0~o^upcLQ^NyBQR&(-cRsldCqm7Vs&_^(%>ik+nu=L z{%#rI?kULt8IRDn_SAb?BVFahg-YLFEpP8Bn3KSXi1e%l(1r=Gw8itd-aa#m;2n=_qq7|`atU*CK2m_f@g3Av=}+6V3*&}f-qYMJ88PL;IU<1|F2cjx&l%xnexI-DHgtH>PX0h)!oJ?in5=a zX<2_dHMl9&RZZJ7LJ}GD$2+{GgTr*)AJz;PjF{YyEnzhHlV=}$Xx`%`6^eVD2zAKdyPTwiIk-l-!_&#trQg!KqJrEzW_RG*jutC- zPT%*1f3C0*d9AOY27IQTqzhQ@&3i1=V!rUf@J%n>>t&{Zy3Am=Gh9vK!_8KhvF)^< zPlb5ac&9VZ5`_}OrK_1n5})#5RW&&p!A%l^Pk9Z_IFb&15b@jFv)>;BU|~)`dm{I{ zvS_}jKNbf%3qQPYtn#+#22%3;0(+$3wi~v70h$Ql248sqYlgjg^{YpX9NV15ZhI?L zL{vnBv>!b1X+A2BcRK}UJ}xUBC_e)RX3u|UC&4>mIEzp*h=3sh9HwPJ8H`d485wy8 z4BT}zbjz5SB#$hh4rSnNQhTx)Ov@KywuQH75QIFlVd+WV?0!0_r3n7|>LU8Ln6R>q z9;}i?aG%nXkoyQ?;(MGC#EmT)yU&4a>O!2ac7r{BR5em0s?>Zf5#eXsN#WXK5aC#n zf%}5*KKm?;bgRH+yP2&f5X&@A^Q7FwjkQ!?d_VYNDYQJt>$8?Fj3yl%=%rq# z>G_zt#;{CAqUROodNCje(Wy?El72*PmOD2EMIK80e!p@(gT=)4`|=QT=0Qyzji~)@ z7#yqd;Cq5n%>B;S7IWo|*$p3$anb>%-*mDhpYK1k_|;SmMch69xr}tZ1d~ODWcoFZ!eXvV}F|t zdZUv(lMwKJ5MIr_<|e?f>K2rZFJ$45t))U+ z1)kbEFu~{E6(3X4%T7Ma^XV%yZ5*Wm$2V~&TLUj{H(XMDiNwdfh+DkTGmPHWR$u$? z#Ct_P08YQ6SZ4O4u-IRpy7BqAh754S5H;TbUPv=BPbp@MOovWX_vOytAHf!BHrFwZ zd3>R->T{{P55C5nX`|{Q^a_QWuJ_W{1(vs#gL+T-hdxGOIPZ2XNio zG^)Sv7*(b~3K5EfAKcg1j&U!OI8Hd5q9p#K+JFY!*Kc%O#XwW}7;Xj59v0lNnEyJQ zs$uk*OQ@4#^M_1lb*$B3n>NaRB$`q|Vx7Frz%eftc2ld@yQwf9bC)-N1LLE5;BhKu z)sLXYXC%_#$0NLN@D(6cHnr1UJ*|14jJ=aj+p?<}OBdQ{YijRg&;6YJ+DpC*#_vk` z;8dC3pG`0OFgtyqP^yGsIE0lsaE|U|aocMJIg268&JY!&4QN-@wQYb z7&y)N@fF}di`w1ql|7~|Y(q5R8dsgwlctoTW7^H;H%+1)GRDdR2`)(9*EihS6+1jV z%OcLZWCY#RBArc=ZtU?x*KIVIU=3F`Fvr1X_5#d>oO;THO#}*(`V(sf!?u~0O=Rk^ zc6k~lzblP{+qWeBA-&w0{Pbsu1kO+P7vJLdtomuU(b%HrFM2-KJ{KzW`(v+3b5K|N zxj+3Y&RGSur{%9&TY~N?@^!+Nl-Yl$jo@nF&ki?`o3(04{s>6R!Y9XR>iM?rl&O>5 zMJc(h-?O<%{G(W#mjz$0=G)kmy?}v@%R4jkJuj}{@DHI$e+eoMqpqW*66r)xT$@wz zfyXGLTa(6Tf%E=2`YFMqk(x5sgkIe+{0Z}$*c+G|PV zKb;8>m!vz_82zX6u!f3%p+dajdJEWXfT=Ysf)1K-Rkqq^PN1=+vDkR2RvCu~@Fme? znT(V9D6Q8RH|G99n{q!`3cG-wgmryy4fLu>zIo|>ExyD|tH`$>P>8KqLQE)CanjK= z(OZDL$mu%lg^{=>nY191>lCv75Tec6nIR7I^9hupiP%x%)0`oTp`n{Ny|C_Bsr&+9 zTTEqaRG!E*MS^9;Mk<#K9qEnY*uPFZYg!dwJz306C9`N1fhX+b86lxbWd1U<-v`#7 z>|O@lV&VV&oAg=>ieZQ_jrF=-i(PY7-5IRf5RPO4qgDx4$71q@ht-d500i@l~AlOykk#-jSB;y4YEeIM@ zL4sEsE=OUK3F zU?cV=(G$oT1grkUY`%xe=ixqUr5xqQ3b1hpQu}xTIRF6r+15ZY_`?Z1KrUWWRp$&JoJ= zG&>Y{e__Uh<4#h%lY{z+`%YO%Dd}bC1|ctAGy{BgDL2Z@elxf#A)nrYzqTCBQ}=;eW|^L$W;-U1B0IN7=%e5!fl7aJaZ@f|u|c)#%zMVRk57YDp)^ApzQFda3l z0eEHTKbdeLN?l(%YY&^XMgmcL-bE=v$;fnE6k!_P0$)0yT`x@+jURfwGaGrf!HzrS zqGJo1kOuF^f>$kFbvgrQ2OcC9pb)qphyc&G_$ST;CrL?{YRr#(&)tRkS^c=``doD; z1NUP-C0oEdNS^B6>s<`E=AAJ#_HvHv_OWg%%Q5+IZS{w<(D#Smkm_9IRy~<|B(h$Q znwip--ItA|{h#xFISuQE1%5ThvR^-a<9b8{9=AL@(Meu!n41H%zK-p(X`_HNjdPsV z3&n{IcT7BWv|AyWqVi}}LSGzg$ngOFPyOu7>$#!8BCQXTc4iIXDZf?goGp4}a7?X{ zM}xo}dP**F!Jko!$7`NW^sdFU z$gWvsi#K#tKThu5hvBvQQ$JVt186OcG-pg&NpH20iJ*Ub(hZ`ID**Q@>|0$bZI}`P3EXkcTe}k)N2p(5m?|(I66w@EZO;iJ^8lREWoLfT=jB9P?@7?8h8I-zXbbE@ zG7TKfiI&D!yA$i7d^r0r-N}pSf=D5j2SNSf5OB4u{=^)`Ois}LF8bueNue1*hG)ch z$$52%j3g={#-jjXE`@1V2(YhHL$T9OcLAN(bOuECww~&~e&Qo|#4@rS9ktht=Z-Y0 z{f3evVgT8#2QM2#i2awxaHukquGV;yP8mbVn%oRZ^q6#I>#OH`^}7puiVVd~*m+?` zPmD_J@o!WJw++?550`OY(uiRUHB6G8PKdH;XHZVmwmKRGCb$vjOBF7D>>5fUIT8-w zAF99uhH`rBYW{nK>p`+mt5+&{)GX@Or68JK@p>h z)6)Las*j{cL{%E^ob(^yTstk72>ZO|ZN#oZ#aM3q7iFkX+xq1`XH^J2EqRw%^tIpg zn?#3sX*lM7w}?&@pO4k=9U2>jhkSntnl{_#NvNW$RoA#k0QsXv%b(`u2mH~Lk`?-Irm|kh z+j?{+@{b;4c_7=CV?UoI(O@UGxr$e8I~zp3;5Fu)u}dv2@00KK47nwCT-b@>d&+Qf zB!PdL2x3D0sfuhe+0`%agyJLFFoeo&3IEETl4vm1Yjo$Xl&~>KWHBi# zuT&H8^@V@*gD2nRD3k6PSdh?BGmL;&{mo-+U$Dn5-2CeV%|8>8O%f3wriL2c9*@_A zMQS(-(V!D70z5Z5(F+{QWC4BWW}*0B$k@2*U$G(2EL{d*&JO>TT>;f~;ZuonwF%2=w< z(oV`_T6Kg%fM@d}Txh5MQt{-%S@xTo`*y1b-#wq#u_EN{S>tZAF-=~Erc2`Vfg-I` zVy}KHU(-|?RZJ`x8c;da$D6TnX?oCWos?x+$DioU($O|#A%#0Xq#JE+&CLA*B>K-> z@K`xcx{u_)>R{Hs84aod);8Eh_;mekaB-kvfYJO`>H^8XSL#sLv~_Y*&8ytRjs&S2 zi`9h9ee}2H0}hp#H{3pz?acMTLhIf#3&riOWMzELI^ptBIc@cqN{|d<0@^|%_N>Gw zPNBX7--7W&Um@u?%W@SlMJ3MYmgw7#fflvecZ97bnsWKmKR1pVwAO}Wezv%_^KkYe z@2#P>KF{~cE2rmUm4FAV`1&3^B5QlAf*1ju{1sdQPRtFcfvs_L%1zEyGBtp6I?p(E z71-vg?$8%5@EQ%iY4sC|q>HjR7if3ZFOC+y*8QV|>G5q*5NTs?jz&j${&y)7CgJ!; zV8c9YZsB}MKd;<0rdwCP%Q}ZUqj9^Y5E1L*A%I8duM1S@hDRj$%2Pb9vkMjPdW)@D zel_3f5vfpg-s63+kZL7 z!J#6+(AZll1PgJ<$$O}`(C;qg`aLHpciv2JqE2eS{L&0dS<%Y{GbQNrvN0KZWgumM z3;Bo-U{pzAZ~6Mg{i(q*rgX@`k(Wx@4UnMyxT$Hwdh@f|7T+Fe{c5d7??~egTr9qL z43C_|;$KDUk#>ug*eENJh{0M^V4D$A?ZXd<>2dgde4UdJwZjGNe3;lV0hA#*g~BaG zpS$GW>eWwGsxfpC^@qSXKucX*Q>cnZ@?vMYmAQ)}q(QF_hL(7#ml52JQ zm1SF2?YMpKVSS+h0fOQ$BCI@dgXI($+{LiUtN2xbQv9fG0MQJQi&UA9@mwYCZe3W= zD1#A_a4heK9le5iFn{IhyWQ8Gp246OZ|^7sY$!=_u7u#zG97<96LoJm+t z*q*7juXbv;2BQ0#sESo-f64zpInK2W85i`Q;zVaB`eE!M){ABv#V1G2pKadvvI5gydf;n>dQVM%|2Bc>Drtn zH~zX`e{|KaK{9cMgn&L`Og7I#A`?k`g2eXi1M7biQMQb5X8`5m8deQ`fcf5SitBvE zFD3L#>fdz0#HZAR2&&DT;D*V>Ee3I0)@~W`yUPOAd;)5JiSQ+-EAW;w=eIFB0j~Gv zO4{tNu3?9h`k8n6*H00m{me} zL&^eRgD_m=FK%&0u=Ko%yPj?*8d>5U#&{v*kXC!?XE)l6@0-RsxZ-%`XTzSK1sdyLEeVs(*9&YZ2!K5 zFIktl*dPzT-Zr@Au>2U>=wH+ShOZSb^6V;+SqVv?Ozm*Z$Z0Z&HGWjLWr?%zvE&i! z;k`@oEpmWL@)8su^zQK|($HJ@0(HEkh57g!MuIkRD~h4vr)v0MI)>&z9N|VBdZ#t2 zBod#X!DG8!c-c+wgO|SJ@Xz-L#JcfkH?M~! zXnJSu;lB~DU~LtD$ow@w%>nc&QSm>=<+LHQ>RA6LNB^{{$)5cG{$;9G8sFsSpLSdu zS%<34$FB(SxzcNFsfjDMoBQXUoR4lS-@yLwc|#Ri)4xOyzlPkj>hSyLn=6&2LM@bk zeNZ+0c24ZTM0wV0As)Pd{iB_rAI$jUXxEy`90Rcan_g)p@3_YP%@X{ +#include + +#include + + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareColour(py::module & m) { + + // py::class_(m, "Colour") + // .def_static("White", &Colour::White) + // .def_static("Black", &Colour::Black) + // .def_static("Red", &Colour::Red) + // .def_static("Green", &Colour::Green) + // .def_static("Blue", &Colour::Blue) + // .def_static("Unspecified", &Colour::Unspecified) + + // .def(py::init<>()) + // .def(py::init(), + // "red"_a, "green"_a, "blue"_a, "alpha"_a=1.0) + // // .def(py::init(), "rgba") + + // .def("Get", &Colour::Get) + // .def("WithAlpha", &Colour::WithAlpha) + // .def_static("Hsv", &Colour::Hsv, + // "hue"_a, "sat"_a=1.0, "val"_a=1.0, "alpha"_a=1.0) + // ; + + +} + +} \ No newline at end of file diff --git a/python/gl/gl.hpp b/python/gl/gl.hpp new file mode 100644 index 0000000..b91f027 --- /dev/null +++ b/python/gl/gl.hpp @@ -0,0 +1,92 @@ +#include +#include + +#include +#include "colour.hpp" + + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareGL(py::module & m) { + + declareColour(m); + + + + py::class_>(m, "GlTexture") + .def(py::init<>(), "Default constructor, represents 'no texture'") + .def(py::init(), + "width"_a, "height"_a, "internal_format"_a = GL_RGBA8, "sampling_linear"_a = true, "border"_a = 0, + "glformat"_a = GL_RGBA, "gltype"_a = GL_UNSIGNED_BYTE, "data"_a = nullptr, + "internal_format normally one of GL_RGBA8, GL_LUMINANCE8, GL_INTENSITY16") + + .def("Reinitialise", &GlTexture::Reinitialise, + "width"_a, "height"_a, "internal_format"_a = GL_RGBA8, "sampling_linear"_a = true, "border"_a = 0, + "glformat"_a = GL_RGBA, "gltype"_a = GL_UNSIGNED_BYTE, "data"_a = nullptr, + "Reinitialise teture width / height / format") // virtual function + + .def("IsValid", &GlTexture::IsValid) + .def("Delete", &GlTexture::Delete, "Delete OpenGL resources and fall back to representing 'no texture'") + .def("Bind", &GlTexture::Bind) + .def("Unbind", &GlTexture::Unbind) + + .def("Upload", (void (GlTexture::*) (const void*, GLenum, GLenum)) &GlTexture::Upload, + "image"_a, "data_format"_a = GL_LUMINANCE, "data_type"_a = GL_FLOAT, + "data_layout normally one of GL_LUMINANCE, GL_RGB, ...\n" + "data_type normally one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_FLOAT") + .def("Upload", (void (GlTexture::*) (const void*, GLsizei, GLsizei, GLsizei, GLsizei, GLenum, GLenum)) + &GlTexture::Upload, "data"_a, "tex_x_offset"_a, "tex_y_offset"_a, "data_w"_a, "data_h"_a, + "data_format"_a, "data_type"_a, "Upload data to texture, overwriting a sub-region of it.\n" + "data ptr contains packed data_w x data_h of pixel data.") + .def("Download", (void (GlTexture::*) (void*, GLenum, GLenum) const) &GlTexture::Download, + "image"_a, "data_layout"_a = GL_LUMINANCE, "data_type"_a = GL_FLOAT) + .def("Download", (void (GlTexture::*) (TypedImage&) const) &GlTexture::Download, "image"_a) + + .def("Upload", [](GlTexture &t, py::array_t img, GLenum data_format, GLenum data_type) { + auto buf = img.request(); + unsigned char* data = (unsigned char*) buf.ptr; + t.Upload(data, data_format, data_type); + }, + "image"_a, "data_format"_a = GL_RGB, "data_type"_a = GL_UNSIGNED_BYTE) + + .def("Load", &GlTexture::Load, "image"_a, "sampling_linear"_a = true) + .def("LoadFromFile", &GlTexture::LoadFromFile, "filename"_a, "sampling_linear"_a) + .def("Save", &GlTexture::Save, "filename"_a, "top_line_first"_a = true) + .def("SetLinear", &GlTexture::SetLinear) + .def("SetNearestNeighbour", &GlTexture::SetNearestNeighbour) + + .def("RenderToViewport", (void (GlTexture::*) () const) &GlTexture::RenderToViewport) + .def("RenderToViewport", (void (GlTexture::*) (Viewport, bool, bool) const) &GlTexture::RenderToViewport, + "tex_vp"_a, "flipx"_a = false, "flipy"_a = false) + .def("RenderToViewport", (void (GlTexture::*) (const bool) const) &GlTexture::RenderToViewport, "flip"_a) + + .def("RenderToViewportFlipY", &GlTexture::RenderToViewportFlipY) + .def("RenderToViewportFlipXFlipY", &GlTexture::RenderToViewportFlipXFlipY) + + .def_readwrite("internal_format", &GlTexture::internal_format) + .def_readwrite("tid", &GlTexture::tid) + .def_readwrite("width", &GlTexture::width) + .def_readwrite("height", &GlTexture::height) + + // move constructor + // move assignment + // private copy constructor + + ; + + // GlRenderBuffer + // GlFramebuffer + // GlBufferType + // GlBuffer + // GlSizeableBuffer + // + + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/gl/gldraw.hpp b/python/gl/gldraw.hpp new file mode 100644 index 0000000..cfa69df --- /dev/null +++ b/python/gl/gldraw.hpp @@ -0,0 +1,23 @@ +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareGLDraw(py::module & m) { + + m.def("glDrawColouredCube", &glDrawColouredCube, + "axis_min"_a = -0.5f, "axis_max"_a = 0.5f); + + //py::class_> cls(m, "Params"); + + //cls.def(py::init<>()); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/handler/blank.hpp b/python/handler/blank.hpp new file mode 100644 index 0000000..cbb644b --- /dev/null +++ b/python/handler/blank.hpp @@ -0,0 +1,18 @@ +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareHandlerEnums(py::module & m) { + + + +} + +} \ No newline at end of file diff --git a/python/handler/handler.hpp b/python/handler/handler.hpp new file mode 100644 index 0000000..1dcb930 --- /dev/null +++ b/python/handler/handler.hpp @@ -0,0 +1,46 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareHandler(py::module & m) { + + py::class_>(m, "Handler"); + + py::class_, Handler>(m, "HandlerScroll") + .def("Mouse", &HandlerScroll::Mouse, + "view"_a, "botton"_a, "x"_a, "y"_a, "pressed"_a, "button_state"_a) + .def("Special", &HandlerScroll::Special, + "view"_a, "inType"_a, "x"_a, "y"_a, "p1"_a, "p2"_a, "p3"_a, "p4"_a, "button_state"_a) + ; + + + py::class_, Handler>(m, "Handler3D") + .def(py::init(), + "cam_state"_a, "enforce_up"_a = AxisDirection::AxisNone, "trans_scale"_a = 0.01f, + "zoom_fraction"_a = PANGO_DFLT_HANDLER3D_ZF) + .def(py::init()) // copy constructor + + .def("Keyboard", &Handler3D::Keyboard, "view"_a, "key"_a, "x"_a, "y"_a, "pressed"_a) + .def("Mouse", &Handler3D::Mouse, + "view"_a, "botton"_a, "x"_a, "y"_a, "pressed"_a, "button_state"_a) + .def("MouseMotion", &Handler3D::MouseMotion, "view"_a, "x"_a, "y"_a, "button_state"_a) + .def("Special", &Handler3D::Special, + "view"_a, "inType"_a, "x"_a, "y"_a, "p1"_a, "p2"_a, "p3"_a, "p4"_a, "button_state"_a) + +#ifdef USE_EIGEN + .def("Selected_P_w", &Handler3D::Selected_P_w) // inline +#endif + + ; + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/handler/handler_enums.hpp b/python/handler/handler_enums.hpp new file mode 100644 index 0000000..8eed9f6 --- /dev/null +++ b/python/handler/handler_enums.hpp @@ -0,0 +1,139 @@ +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +enum PangoKey { + // Supported Key modifiers for GlobalKeyPressCallback. + // e.g. PANGO_CTRL + 'r', PANGO_SPECIAL + GLUT_KEY_RIGHT, etc. + PANGO_SPECIAL_ = PANGO_SPECIAL, + PANGO_CTRL_ = PANGO_CTRL, + PANGO_OPTN_ = PANGO_OPTN, + + // Ordinary keys + PANGO_KEY_TAB_ = PANGO_KEY_TAB, + PANGO_KEY_ESCAPE_ = PANGO_KEY_ESCAPE, + + // Special Keys (same as GLUT_ defines) + PANGO_KEY_F1_ = PANGO_KEY_F1, + PANGO_KEY_F2_ = PANGO_KEY_F2, + PANGO_KEY_F3_ = PANGO_KEY_F3, + PANGO_KEY_F4_ = PANGO_KEY_F4, + PANGO_KEY_F5_ = PANGO_KEY_F5, + PANGO_KEY_F6_ = PANGO_KEY_F6, + PANGO_KEY_F7_ = PANGO_KEY_F7, + PANGO_KEY_F8_ = PANGO_KEY_F8, + PANGO_KEY_F9_ = PANGO_KEY_F9, + PANGO_KEY_F10_ = PANGO_KEY_F10, + PANGO_KEY_F11_ = PANGO_KEY_F11, + PANGO_KEY_F12_ = PANGO_KEY_F12, + PANGO_KEY_LEFT_ = PANGO_KEY_LEFT, + PANGO_KEY_UP_ = PANGO_KEY_UP, + PANGO_KEY_RIGHT_ = PANGO_KEY_RIGHT, + PANGO_KEY_DOWN_ = PANGO_KEY_DOWN, + PANGO_KEY_PAGE_UP_ = PANGO_KEY_PAGE_UP, + PANGO_KEY_PAGE_DOWN_ = PANGO_KEY_PAGE_DOWN, + PANGO_KEY_HOME_ = PANGO_KEY_HOME, + PANGO_KEY_END_ = PANGO_KEY_END, + PANGO_KEY_INSERT_ = PANGO_KEY_INSERT, +}; + + + +void declareHandlerEnums(py::module & m) { + + py::enum_(m, "PangoKey") + .value("PANGO_SPECIAL", PangoKey::PANGO_SPECIAL_) + .value("PANGO_CTRL", PangoKey::PANGO_CTRL_) + .value("PANGO_OPTN", PangoKey::PANGO_OPTN_) + + .value("PANGO_KEY_TAB", PangoKey::PANGO_KEY_TAB_) + .value("PANGO_KEY_ESCAPE", PangoKey::PANGO_KEY_ESCAPE_) + + .value("PANGO_KEY_F1", PangoKey::PANGO_KEY_F1_) + .value("PANGO_KEY_F2", PangoKey::PANGO_KEY_F2_) + .value("PANGO_KEY_F3", PangoKey::PANGO_KEY_F3_) + .value("PANGO_KEY_F4", PangoKey::PANGO_KEY_F4_) + .value("PANGO_KEY_F5", PangoKey::PANGO_KEY_F5_) + .value("PANGO_KEY_F6", PangoKey::PANGO_KEY_F6_) + .value("PANGO_KEY_F7", PangoKey::PANGO_KEY_F7_) + .value("PANGO_KEY_F8", PangoKey::PANGO_KEY_F8_) + .value("PANGO_KEY_F9", PangoKey::PANGO_KEY_F9_) + .value("PANGO_KEY_F10", PangoKey::PANGO_KEY_F10_) + .value("PANGO_KEY_F11", PangoKey::PANGO_KEY_F11_) + .value("PANGO_KEY_F12", PangoKey::PANGO_KEY_F12_) + .value("PANGO_KEY_LEFT", PangoKey::PANGO_KEY_LEFT_) + .value("PANGO_KEY_UP", PangoKey::PANGO_KEY_UP_) + .value("PANGO_KEY_RIGHT", PangoKey::PANGO_KEY_RIGHT_) + .value("PANGO_KEY_DOWN", PangoKey::PANGO_KEY_DOWN_) + .value("PANGO_KEY_PAGE_UP", PangoKey::PANGO_KEY_PAGE_UP_) + .value("PANGO_KEY_PAGE_DOWN", PangoKey::PANGO_KEY_PAGE_DOWN_) + .value("PANGO_KEY_HOME", PangoKey::PANGO_KEY_HOME_) + .value("PANGO_KEY_END", PangoKey::PANGO_KEY_END_) + .value("PANGO_KEY_INSERT", PangoKey::PANGO_KEY_INSERT_) + .export_values(); + + + /*m.def_readonly("PANGO_SPECIAL", &PANGO_SPECIAL); // py::module no def_readonly + m.def_readonly("PANGO_CTRL", &PANGO_CTRL); + m.def_readonly("PANGO_OPTN", &PANGO_OPTN); + + m.def_readonly("PANGO_KEY_TAB", &PANGO_KEY_TAB); + m.def_readonly("PANGO_KEY_ESCAPE", &PANGO_KEY_ESCAPE); + + m.def_readonly("PANGO_KEY_F1", &PANGO_KEY_F1); + m.def_readonly("PANGO_KEY_F2", &PANGO_KEY_F2); + m.def_readonly("PANGO_KEY_F3", &PANGO_KEY_F3); + m.def_readonly("PANGO_KEY_F4", &PANGO_KEY_F4); + m.def_readonly("PANGO_KEY_F5", &PANGO_KEY_F5); + m.def_readonly("PANGO_KEY_F6", &PANGO_KEY_F6); + m.def_readonly("PANGO_KEY_F7", &PANGO_KEY_F7); + m.def_readonly("PANGO_KEY_F8", &PANGO_KEY_F8); + m.def_readonly("PANGO_KEY_F9", &PANGO_KEY_F9); + m.def_readonly("PANGO_KEY_F10", &PANGO_KEY_F10); + m.def_readonly("PANGO_KEY_F11", &PANGO_KEY_F11); + m.def_readonly("PANGO_KEY_F12", &PANGO_KEY_F12); + m.def_readonly("PANGO_KEY_LEFT", &PANGO_KEY_LEFT); + m.def_readonly("PANGO_KEY_UP", &PANGO_KEY_UP); + m.def_readonly("PANGO_KEY_RIGHT", &PANGO_KEY_RIGHT); + m.def_readonly("PANGO_KEY_DOWN", &PANGO_KEY_DOWN); + m.def_readonly("PANGO_KEY_PAGE_UP", &PANGO_KEY_PAGE_UP); + m.def_readonly("PANGO_KEY_PAGE_DOWN", &PANGO_KEY_PAGE_DOWN); + m.def_readonly("PANGO_KEY_HOME", &PANGO_KEY_HOME); + m.def_readonly("PANGO_KEY_END", &PANGO_KEY_END); + m.def_readonly("PANGO_KEY_INSERT", &PANGO_KEY_INSERT);*/ + + py::enum_(m, "MouseButton") + .value("MouseButtonLeft", MouseButton::MouseButtonLeft) + .value("MouseButtonMiddle", MouseButton::MouseButtonMiddle) + .value("MouseButtonRight", MouseButton::MouseButtonRight) + .value("MouseWheelUp", MouseButton::MouseWheelUp) + .value("MouseWheelDown", MouseButton::MouseWheelDown) + .value("MouseWheelRight", MouseButton::MouseWheelRight) + .value("MouseWheelLeft", MouseButton::MouseWheelLeft) + .export_values(); + + py::enum_(m, "KeyModifier") + .value("KeyModifierShift", KeyModifier::KeyModifierShift) + .value("KeyModifierCtrl", KeyModifier::KeyModifierCtrl) + .value("KeyModifierAlt", KeyModifier::KeyModifierAlt) + .value("KeyModifierCmd", KeyModifier::KeyModifierCmd) + .value("KeyModifierFnc", KeyModifier::KeyModifierFnc) + .export_values(); + + py::enum_(m, "InputSpecial") + .value("InputSpecialScroll", InputSpecial::InputSpecialScroll) + .value("InputSpecialZoom", InputSpecial::InputSpecialZoom) + .value("InputSpecialRotate", InputSpecial::InputSpecialRotate) + .value("InputSpecialTablet", InputSpecial::InputSpecialTablet) + .export_values(); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/handler/handler_image.hpp b/python/handler/handler_image.hpp new file mode 100644 index 0000000..ad7be80 --- /dev/null +++ b/python/handler/handler_image.hpp @@ -0,0 +1,21 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareImageViewHandler(py::module & m) { + + py::class_, Handler>(m, "ImageViewHandler") + + ; + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/pangolin.cpp b/python/pangolin.cpp new file mode 100644 index 0000000..15494de --- /dev/null +++ b/python/pangolin.cpp @@ -0,0 +1,104 @@ +#include +#include + +#include "utils/params.hpp" +#include "display/attach.hpp" +#include "display/window.hpp" +#include "display/user_app.hpp" +#include "display/viewport.hpp" +#include "display/view.hpp" +#include "display/opengl_render_state.hpp" +#include "handler/handler_enums.hpp" +#include "handler/handler.hpp" +#include "handler/handler_image.hpp" +#include "var/varvaluegeneric.hpp" +#include "var/varvaluet.hpp" +#include "var/varvalue.hpp" +#include "var/var.hpp" +#include "var/varextra.hpp" +#include "display/image_view.hpp" +#include "display/display.hpp" +#include "display/widgets/widgets.hpp" +#include "scene/scene.hpp" +#include "plot/plotter.hpp" +#include "gl/gl.hpp" +#include "gl/gldraw.hpp" +#include "contrib.hpp" + + + +namespace py = pybind11; + + +namespace pangolin { + +PYBIND11_MODULE(pangolin, m) { + + + // declaration order matters + + // utils/params + declareParams(m); + + // display/attach + declareAttach(m); + + // display/window + declareWindow(m); + + // display/user_app + declareUserApp(m); + + // display/viewport + declareViewport(m); + // display/view + declareView(m); + + // display/opengl_render_state + declareOpenGlRenderState(m); + + // handler/handler_enums + declareHandlerEnums(m); + // handler/handler + declareHandler(m); + // handler/handler_image + declareImageViewHandler(m); + + // var/varvaluegeneric + declareVarValueGeneric(m); + // var/varvaluet + declareVarValueT(m); + // var/varvalue + declareVarValue(m); + // var/var + declareVar(m); + // var/varextra + declareVarExtra(m); + + + // display/image_view + declareImageView(m); + + // display/display + declareDisplay(m); + + // display/widgets/widgets + declareWidgets(m); + + // scene + declareScene(m); + + // plot + declarePlotter(m); + + // gl/gl + declareGL(m); + // gl/gldraw + declareGLDraw(m); + + // contrib + declareContrib(m); + + } + +} \ No newline at end of file diff --git a/python/plot/datalog.hpp b/python/plot/datalog.hpp new file mode 100644 index 0000000..b80f15b --- /dev/null +++ b/python/plot/datalog.hpp @@ -0,0 +1,91 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareDataLog(py::module & m) { + + py::class_(m, "DimensionStats") + .def(py::init<>()) + .def("Reset", &DimensionStats::Reset) + .def("Add", &DimensionStats::Add, + "v"_a) // (const float) -> void + + .def_readwrite("isMonotonic", &DimensionStats::isMonotonic) // bool + .def_readwrite("sum", &DimensionStats::sum) // float + .def_readwrite("sum_sq", &DimensionStats::sum_sq) // float + .def_readwrite("min", &DimensionStats::min) // float + .def_readwrite("max", &DimensionStats::max) // float + ; + + + py::class_(m, "DataLogBlock") + .def(py::init(), + "dim"_a, "max_samples"_a, "start_id"_a) + + .def("Samples", &DataLogBlock::Samples) // -> size_t + .def("MaxSamples", &DataLogBlock::MaxSamples) // -> size_t + .def("SampleSpaceLeft", &DataLogBlock::SampleSpaceLeft) // -> size_t + .def("IsFull", &DataLogBlock::IsFull) // -> bool + .def("AddSamples", &DataLogBlock::AddSamples, + "num_samples"_a, "dimensions"_a, "data_dim_major"_a) + .def("ClearLinked", &DataLogBlock::ClearLinked) + .def("NextBlock", &DataLogBlock::NextBlock) // ->DataLogBlock* + .def("StartId", &DataLogBlock::StartId) // -> size_t + .def("DimData", &DataLogBlock::DimData, + "d"_a) // -> float* + .def("Dimensions", &DataLogBlock::Dimensions) // -> size_t + .def("Sample", &DataLogBlock::Sample, + "n"_a) // -> const float* + ; + + + py::class_(m, "DataLog") + .def(py::init(), + "block_samples_alloc"_a=10000) + + .def("SetLabels", &DataLog::SetLabels, + "labels"_a) // (const std::vector &) -> void + .def("Labels", &DataLog::Labels) // () -> const std::vector & + + .def("Log", (void (DataLog::*) (size_t, const float *, unsigned int)) &DataLog::Log, + "dimension"_a, "vals"_a, "samples"_a=1) + .def("Log", (void (DataLog::*) (float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float, float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float, float, float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (float, float, float, float, float, float, float, float, float, float)) &DataLog::Log) + .def("Log", (void (DataLog::*) (const std::vector &)) &DataLog::Log) + + .def("Clear", &DataLog::Clear) + .def("Save", &DataLog::Save, + "filename"_a) // std::string -> + + .def("FirstBlock", &DataLog::FirstBlock) // () -> const DataLogBlock* + .def("LastBlock", &DataLog::LastBlock) // () -> const DataLogBlock* + .def("Samples", &DataLog::Samples) // () -> size_t + .def("Sample", &DataLog::Sample, + "n"_a) // (int) -> const float* + .def("Stats", &DataLog::Stats, + "dim"_a) // (size_t) -> const DimensionStats& + ; + + + + + +} + +} \ No newline at end of file diff --git a/python/plot/plotter.hpp b/python/plot/plotter.hpp new file mode 100644 index 0000000..b6d1da6 --- /dev/null +++ b/python/plot/plotter.hpp @@ -0,0 +1,135 @@ +#include +#include + +#include +#include + +#include "datalog.hpp" +#include "range.hpp" + + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +struct Colour; + +void declarePlotter(py::module & m) { + + declareDataLog(m); + declareRange(m); + + + // py::class_(m, "Colour") + // .def(py::init<>()) + // ; + + py::class_(m, "Colour") + .def_static("White", &Colour::White) + .def_static("Black", &Colour::Black) + .def_static("Red", &Colour::Red) + .def_static("Green", &Colour::Green) + .def_static("Blue", &Colour::Blue) + .def_static("Unspecified", &Colour::Unspecified) + + .def(py::init<>()) + .def(py::init(), + "red"_a, "green"_a, "blue"_a, "alpha"_a=1.0) + // .def(py::init(), "rgba") + + .def("Get", &Colour::Get) + .def("WithAlpha", &Colour::WithAlpha) + .def_static("Hsv", &Colour::Hsv, + "hue"_a, "sat"_a=1.0, "val"_a=1.0, "alpha"_a=1.0) + ; + + + py::enum_(m, "DrawingMode") + .value("DrawingModePoints", DrawingMode::DrawingModePoints) + .value("DrawingModeDashed", DrawingMode::DrawingModeDashed) + .value("DrawingModeLine", DrawingMode::DrawingModeLine) + .value("DrawingModeNone", DrawingMode::DrawingModeNone) + .export_values() + ; + + + py::class_ cls_mk(m, "Marker"); + py::enum_(cls_mk, "Direction") + .value("Horizontal", Marker::Direction::Horizontal) + .value("Vertical", Marker::Direction::Vertical) + .export_values() + ; + py::enum_(cls_mk, "Equality") + .value("LessThan", Marker::Equality::LessThan) + .value("Equal", Marker::Equality::Equal) + .value("GreaterThan", Marker::Equality::GreaterThan) + .export_values() + ; + + cls_mk.def(py::init(), + "d"_a, "value"_a, "leg"_a=Marker::Equality::Equal, "c"_a=Colour()); + cls_mk.def(py::init(), + "range"_a, "c"_a=Colour()); + + cls_mk.def_readwrite("range", &Marker::range); + cls_mk.def_readwrite("colour", &Marker::colour); + + + py::class_>(m, "Plotter") + .def(py::init(), + "default_log"_a, "left"_a, "right"_a=600, "bottom"_a=-1, "top"_a=1, + "tickx"_a=30, "ticky"_a=0.5, + "linked_plotter_x"_a=nullptr, + "linked_plotter_y"_a=nullptr) + + .def("Render", &Plotter::Render) + .def("GetSelection", &Plotter::GetSelection) + .def("GetDefaultView", &Plotter::GetDefaultView) + .def("SetDefaultView", &Plotter::SetDefaultView) + + .def("GetView", &Plotter::GetView) + .def("SetView", &Plotter::SetView) + .def("SetViewSmooth", &Plotter::SetViewSmooth) + .def("ScrollView", &Plotter::ScrollView) + .def("ScrollViewSmooth", &Plotter::ScrollViewSmooth) + .def("ScaleView", &Plotter::ScaleView) + .def("ScaleViewSmooth", &Plotter::ScaleViewSmooth) + .def("ResetView", &Plotter::ResetView) + .def("SetTicks", &Plotter::SetTicks) + .def("Track", &Plotter::Track, "x"_a="$i", "y"_a="") + .def("ToggleTracking", &Plotter::ToggleTracking) + .def("Trigger", &Plotter::Trigger, "x"_a="$0", "edge"_a=-1, "value"_a=0.0) + .def("ToggleTrigger", &Plotter::ToggleTrigger) + .def("SetBackgroundColour", &Plotter::SetBackgroundColour) + .def("SetAxisColour", &Plotter::SetAxisColour) + .def("SetTickColour", &Plotter::SetTickColour) + .def("ScreenToPlot", &Plotter::ScreenToPlot) + .def("Keyboard", &Plotter::Keyboard) + .def("Mouse", &Plotter::Mouse) + .def("MouseMotion", &Plotter::MouseMotion) + .def("PassiveMouseMotion", &Plotter::PassiveMouseMotion) + .def("Special", &Plotter::Special) + .def("ClearSeries", &Plotter::ClearSeries) + .def("AddSeries", &Plotter::AddSeries, + "x_expr"_a, "y_expr"_a, "mode"_a=DrawingModeLine, "colour"_a=Colour::Unspecified(), + "title"_a="$y", "log"_a=nullptr) + .def("PlotTitleFromExpr", &Plotter::PlotTitleFromExpr) + .def("ClearMarkers", &Plotter::ClearMarkers) + .def("AddMarker", (Marker& (Plotter::*) + (Marker::Direction, float, Marker::Equality, Colour)) &Plotter::AddMarker, + "d"_a, "value"_a, "leg"_a=Marker::Equal, "c"_a=Colour()) + .def("AddMarker", (Marker& (Plotter::*) (const Marker&)) &Plotter::AddMarker) + // .def("ClearImplicitPlots", &Plotter::ClearImplicitPlots) + // .def("AddImplicitPlot", &Plotter::AddImplicitPlot) + ; + + + + + +} + +} \ No newline at end of file diff --git a/python/plot/range.hpp b/python/plot/range.hpp new file mode 100644 index 0000000..c97722c --- /dev/null +++ b/python/plot/range.hpp @@ -0,0 +1,111 @@ +#include +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +namespace { + + template + void templatedRange(py::module& m, const std::string& suffix) { + using CLS = Range; + + py::class_(m, ("Range" + suffix).c_str()) + .def(py::init<>()) + .def(py::init()) + + .def_static("Open", &CLS::Open) + .def_static("Empty", &CLS::Empty) + .def_static("Containing", &CLS::Containing) + + // .def(py::self + T()) + // .def(py::self - T()) + // .def(py::self += T()) + // .def(py::self -= T()) + // .def(py::self *= T()) + // .def(py::self /= T()) + // .def(py::self + CLS()) + // .def(py::self - CLS()) + // .def(py::self += CLS()) + // .def(py::self -= CLS()) + // .def(py::self * float()) + .def("Size", &CLS::Size) + .def("AbsSize", &CLS::AbsSize) + .def("Mid", &CLS::Mid) + .def("Scale", &CLS::Scale) + .def("Insert", (void (CLS::*) (T)) &CLS::Insert) + .def("Insert", (void (CLS::*) (const Range&)) &CLS::Insert) + .def("Clamp", (void (CLS::*) (T, T)) &CLS::Clamp) + .def("Clamp", (void (CLS::*) (const CLS&)) &CLS::Clamp) + .def("Clear", &CLS::Clear) + .def("Contains", &CLS::Contains) + .def("ContainsWeak", &CLS::ContainsWeak) + ; + + m.def("Round", (Rangei (*) (const Range&)) &Round); + } + + template + void templatedXYRange(py::module& m, const std::string& suffix) { + using CLS = XYRange; + + py::class_(m, ("XYRange" + suffix).c_str()) + .def(py::init<>()) + .def(py::init()) + .def(py::init&, const Range&>()) + + .def_static("Open", &CLS::Open) + .def_static("Empty", &CLS::Empty) + .def_static("Containing", &CLS::Containing) + + // .def(py::self + T()) + .def(py::self - CLS()) + .def(py::self * float()) + .def(py::self += CLS()) + + .def("Area", &CLS::Area) + // .def("AbsSize", &CLS::AbsSize) + // .def("Mid", &CLS::Mid) + .def("Scale", &CLS::Scale) + .def("Insert", (void (CLS::*) (T, T)) &CLS::Insert) + .def("Insert", (void (CLS::*) (CLS)) &CLS::Insert) + .def("Clamp", (void (CLS::*) (T, T, T, T)) &CLS::Clamp) + .def("Clamp", (void (CLS::*) (const CLS&)) &CLS::Clamp) + .def("Clear", &CLS::Clear) + .def("Contains", &CLS::Contains) + .def("ContainsWeak", &CLS::ContainsWeak) + + // .def_readwrite("x", CLS::x) + // .def_readwrite("y", CLS::y) + ; + + m.def("Round", (XYRangei (*) (const XYRange&)) &Round); + + } + + + +} + + + +void declareRange(py::module & m) { + + templatedRange(m, "Int"); + templatedRange(m, "Float"); + + templatedXYRange(m, "Int"); + templatedXYRange(m, "Float"); + + + +} + +} \ No newline at end of file diff --git a/python/scene/scene.hpp b/python/scene/scene.hpp new file mode 100644 index 0000000..de97d67 --- /dev/null +++ b/python/scene/scene.hpp @@ -0,0 +1,155 @@ +#include +#include + +#include +#include +#include +#include +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +void declareScene(py::module & m) { + + // abstract class + py::class_>(m, "Interactive") + .def("mouse", &Interactive::Mouse, + "button"_a, "win"_a, "obj"_a, "normal"_a, "pressed"_a, + "button_state"_a, "pickId"_a) // (int button, const GLprecision win[3], const GLprecision obj[3], const GLprecision normal[3], bool pressed, int button_state, int pickId) -> bool + .def("MouseMotion", &Interactive::MouseMotion, + "win"_a, "obj"_a, "normal"_a, + "button_state"_a, "pickId"_a) // (const GLprecision win[3], const GLprecision obj[3], const GLprecision normal[3], int button_state, int pickId) -> bool + ; + + py::class_(m, "RenderParams") + .def(py::init<>()) + .def_readwrite("render_mode", &RenderParams::render_mode) + ; + + // abstract class + py::class_>(m, "Manipulator") + .def("Render", &Manipulator::Render, + "params"_a) // (const RenderParams&) -> void + ; + + + + py::class_>(m, "Renderable") + // .def(py::init&>(), + // "parent"_a=std::weak_ptr(), + // py::keep_alive<1, 2>()) + .def(py::init([](const std::shared_ptr& parent){ + std::weak_ptr weak = parent; + return Renderable(weak); + })) + .def(py::init([](){ + std::shared_ptr parent; + std::weak_ptr weak = parent; + return Renderable(weak); + })) + + .def("UniqueGuid", &Renderable::UniqueGuid) // () -> guid_t + .def("Render", &Renderable::Render, + "params"_a=RenderParams(), + py::keep_alive<1, 2>()) // (const RenderParams&) -> void + .def("RenderChildren", &Renderable::RenderChildren, + "params"_a, + py::keep_alive<1, 2>()) // (const RenderParams&) -> void + .def("FindChild", &Renderable::FindChild, + "guid"_a) // (guid_t) -> std::shared_ptr + .def("Add", &Renderable::Add, + "child"_a, + py::keep_alive<1, 2>()) // (const std::shared_ptr&) -> Renderable& + + .def_readonly("guid", &Renderable::guid) + // .def_readwrite("parent", &Renderable::parent) + .def_readwrite("T_pc", &Renderable::T_pc) + .def_readwrite("child", &Renderable::child) + .def_readwrite("should_show", &Renderable::should_show) + .def_readwrite("children", &Renderable::children) + .def_readwrite("manipulator", &Renderable::manipulator) + ; + + + py::class_> cls_index(m, "InteractiveIndex"); + py::class_(cls_index, "Token") + .def(py::init<>()) + .def("Id", &InteractiveIndex::Token::Id) + ; + cls_index.def_static("I", &InteractiveIndex::I); + cls_index.def("Find", &InteractiveIndex::Find, + "id"_a); // (GLuint) -> Interactive* + cls_index.def("Store", &InteractiveIndex::Store, + "r"_a, + py::keep_alive<1, 2>()); // (Interactive*) -> Token + cls_index.def("Unstore", &InteractiveIndex::Unstore, + "t"_a, + py::keep_alive<1, 2>()); // (Token&) -> void + + + py::class_, Handler3D>(m, "SceneHandler") + .def(py::init(), + "scene"_a, "cam_state"_a) + + // .def("ProcessHitBuffer", &SceneHandler::ProcessHitBuffer, + // "hits"_a, "buf"_a, "hit_map"_a) // (GLint, GLuint*, std::map&) -> void + .def("ComputeHits", &SceneHandler::ComputeHits, + "view"_a, "cam_state"_a, "x"_a, "y"_a, "grad_width"_a, + "hit_objects"_a, + py::keep_alive<1, 2>()) // (pangolin::View&, const pangolin::OpenGlRenderState&, int, int, int, std::map&) -> void + + .def("Mouse", &SceneHandler::Mouse, + "view"_a, "button"_a, "x"_a, "y"_a, "pressed"_a, + "button_state"_a, + py::keep_alive<1, 2>()) // (pangolin::View&, pangolin::MouseButton, int, int, bool, int) -> void + + .def("MouseMotion", &SceneHandler::MouseMotion, + "view"_a, "x"_a, "y"_a, "button_state"_a, + py::keep_alive<1, 2>()) // (pangolin::View&, int, int, int) -> void + + .def("Special", &SceneHandler::Special, + "view"_a, "inType"_a, "x"_a, "y"_a, "p1"_a, "p2"_a, "p3"_a, "p4"_a, + "button_state"_a, + py::keep_alive<1, 2>()) // (pangolin::View&, pangolin::InputSpecial, float, float, float, float, float, float, int) -> void + + .def_readwrite("m_selected_objects", &SceneHandler::m_selected_objects) + // .def_readwrite("scene", &SceneHandler::scene) + .def_readwrite("grab_width", &SceneHandler::grab_width) + ; + + + py::class_>(m, "Axis") + .def(py::init<>()) + + .def("Render", &Axis::Render, + "params"_a, + py::keep_alive<1, 2>()) // (const RenderParams&) -> void + .def("Mouse", (bool (Axis::*) (int, const GLprecision[3], const GLprecision[3], const GLprecision[3], bool, int, int)) + &Axis::Mouse, + "button"_a, "win"_a, "obj"_a, "normal"_a, "pressed"_a, + "button_state"_a, "pickId"_a) + .def("Mouse", [](Axis& axis, int button, int button_state, int pickId){ + GLprecision win[3]; + GLprecision obj[3]; + GLprecision normal[3]; + bool pressed=false; + return axis.Mouse(button, win, obj, normal, pressed, button_state, pickId); + }, + "button"_a, "button_state"_a, "pickId"_a) + + .def_readwrite("axis_length", &Axis::axis_length) + .def_readonly("label_x", &Axis::label_x) + .def_readonly("label_y", &Axis::label_y) + .def_readonly("label_z", &Axis::label_z) + + ; + + +} + +} \ No newline at end of file diff --git a/python/utils/params.hpp b/python/utils/params.hpp new file mode 100644 index 0000000..5247018 --- /dev/null +++ b/python/utils/params.hpp @@ -0,0 +1,25 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + +namespace pangolin { + +void declareParams(py::module & m) { + + py::class_> cls(m, "Params"); + + cls.def(py::init<>()); + cls.def(py::init>>(), "l"_a); + cls.def("Contains", &Params::Contains, "key"_a); + //cls.def("Get", &Params::Get, "key"_a, "default_val"_a); + //cls.def("Set", &Params::Set, "key"_a, "default_val"_a); + cls.def_readwrite("params", &Params::params); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/var/var.hpp b/python/var/var.hpp new file mode 100644 index 0000000..0df0b4e --- /dev/null +++ b/python/var/var.hpp @@ -0,0 +1,95 @@ +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + +namespace { + + template + void templatedVar(py::module & m, std::string const & suffix) { + using Class = Var; + + py::class_> cls(m, ("Var" + suffix).c_str()); + + cls.def_static("Attach", [](const std::string& name, T& variable, bool toggle) { + return Class::Attach(name, variable, toggle); + }, "name"_a, "variable"_a, "toggle"_a = false, py::return_value_policy::reference_internal); + cls.def_static("Attach", [](const std::string& name, T& variable, double min, double max, bool logscale) { + return Class::Attach(name, variable, min, max, logscale); + }, "name"_a, "variable"_a, "min"_a, "max"_a, "logscale"_a = false, py::return_value_policy::reference_internal); + + cls.def(py::init(), "v"_a); + cls.def(py::init(), "name"_a); + cls.def(py::init(), "name"_a, "value"_a, "toggle"_a = false, py::keep_alive<0, 3>()); + cls.def(py::init(), + "name"_a, "value"_a, "min"_a, "max"_a, "logscale"_a = false); + + cls.def("Reset", &Class::Reset); + cls.def("Get", &Class::Get, py::return_value_policy::reference); + cls.def("SetVal", (void (Class::*) (const T&)) &Class::operator=, "val"_a); + cls.def("SetVal", (void (Class::*) (const Var&)) &Class::operator=, "v"_a); + cls.def("Meta", &Class::Meta, py::return_value_policy::reference_internal); + cls.def("GuiChanged", &Class::GuiChanged); + cls.def("Ref", &Class::Ref, py::return_value_policy::reference_internal); + + //m.def(("_Var" + suffix).c_str(), [](const std::string& name, T value, bool toggle) {return Class(name, value, toggle);}); + //m.def(("_Var" + suffix).c_str(), [](const std::string& name, T value, double min, double max, bool logscale) { + // return Class(name, value, min, max, logscale); + // }); + + } + + + template + void templatedVarNum(py::module & m, std::string const & suffix) { + using Class = Var; + + py::class_> cls(m, ("Var" + suffix).c_str()); + + cls.def_static("Attach", [](const std::string& name, T& variable, bool toggle) { + return Class::Attach(name, variable, toggle); + }, "name"_a, "variable"_a, "toggle"_a = false, py::return_value_policy::reference_internal); + cls.def_static("Attach", [](const std::string& name, T& variable, double min, double max, bool logscale) { + return Class::Attach(name, variable, min, max, logscale); + }, "name"_a, "variable"_a, "min"_a, "max"_a, "logscale"_a = false, py::return_value_policy::reference_internal); + + cls.def(py::init(), "v"_a); + cls.def(py::init(), "name"_a); + cls.def(py::init(), "name"_a, "value"_a, "toggle"_a = false); + cls.def(py::init(), + "name"_a, "value"_a, "min"_a, "max"_a, "logscale"_a = false); + + cls.def("Reset", &Class::Reset); + cls.def("Get", &Class::Get, py::return_value_policy::reference); + cls.def("SetVal", (void (Class::*) (const T&)) &Class::operator=, "val"_a); + cls.def("SetVal", (void (Class::*) (const Var&)) &Class::operator=, "v"_a); + cls.def("Meta", &Class::Meta, py::return_value_policy::reference_internal); + cls.def("GuiChanged", &Class::GuiChanged); + cls.def("Ref", &Class::Ref, py::return_value_policy::reference_internal); + + cls.def("__int__", [](Var& v){return (int)v.Get();}); + cls.def("__float__", [](Var& v){return (double)v.Get();}); + + + } + +} + + + +void declareVar(py::module & m) { + templatedVar(m, "Bool"); + templatedVar(m, "String"); + templatedVar>(m, "Func"); + + templatedVarNum(m, "Int"); + templatedVarNum(m, "Float"); +} + +} \ No newline at end of file diff --git a/python/var/varextra.hpp b/python/var/varextra.hpp new file mode 100644 index 0000000..9f66ede --- /dev/null +++ b/python/var/varextra.hpp @@ -0,0 +1,57 @@ +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + + +namespace { + template + void templatedSetVarFunctor(py::module & m, const std::string & suffix) { + using Class = SetVarFunctor; + py::class_>(m, ("SetVarFunctor" + suffix).c_str()) + .def(py::init(), "name"_a, "val"_a) + .def("__call__", &Class::operator()) + .def_readwrite("varName", &Class::varName) + .def_readwrite("setVal", &Class::setVal); + } +} + + + +void declareVarExtra(py::module & m) { + + m.def("ParseVarsFile", &ParseVarsFile, "filename"_a); + + m.def("LoadJsonFile", &LoadJsonFile, "filename"_a, "prefix"_a = ""); + + m.def("SaveJsonFile", &SaveJsonFile, "filename"_a, "prefix"_a = ""); + + m.def("ProcessHistoricCallbacks", &ProcessHistoricCallbacks, "callback"_a, "data"_a, "filter"_a = ""); + + m.def("RegisterNewVarCallback", &RegisterNewVarCallback, "callback"_a, "data"_a, "filter"_a = ""); + + m.def("RegisterGuiVarChangedCallback", &RegisterGuiVarChangedCallback, "callback"_a, "data"_a, "filter"_a = ""); + + templatedSetVarFunctor(m, "Bool"); + templatedSetVarFunctor(m, "Int"); + templatedSetVarFunctor(m, "Float"); + templatedSetVarFunctor(m, "String"); + templatedSetVarFunctor>(m, "Func"); + + py::class_>(m, "ToggleVarFunctor") + .def(py::init(), "name"_a) + .def("__call__", &ToggleVarFunctor::operator()) + .def_readwrite("varName", &ToggleVarFunctor::varName); + + m.def("Pushed", (bool (*) (Var&)) &Pushed, "button"_a); + m.def("Pushed", (bool (*) (bool&)) &Pushed, "button"); + +} + +} // namespace pangolin:: \ No newline at end of file diff --git a/python/var/varvalue.hpp b/python/var/varvalue.hpp new file mode 100644 index 0000000..d323a10 --- /dev/null +++ b/python/var/varvalue.hpp @@ -0,0 +1,50 @@ +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + + +namespace { + + template + void templatedVarValue(py::module & m, std::string const & suffix) { + + typedef typename std::remove_reference::type VarT; + using Class = VarValue; + + py::class_::type>>(m, ("VarValue" + suffix).c_str()) + .def(py::init<>()) + .def(py::init(), "value"_a) + .def(py::init(), "value"_a, "default_value"_a) + // destructor: delete str_ptr + + .def("TypeId", &Class::TypeId) + .def("Reset", &Class::Reset) + .def("Meta", &Class::Meta) + .def("Get", (const VarT& (Class::*) () const) &Class::Get, py::return_value_policy::reference) + .def("Get", (VarT& (Class::*) ()) &Class::Get) + .def("Set", &Class::Set); + } +} + + + + +void declareVarValue(py::module & m) { + //templateVarValue(m, "Bool"); + templatedVarValue(m, "Bool"); + templatedVarValue(m, "Int"); + templatedVarValue(m, "Float"); + templatedVarValue(m, "String"); + templatedVarValue>(m, "Func"); + +} + +} \ No newline at end of file diff --git a/python/var/varvaluegeneric.hpp b/python/var/varvaluegeneric.hpp new file mode 100644 index 0000000..5186b75 --- /dev/null +++ b/python/var/varvaluegeneric.hpp @@ -0,0 +1,51 @@ +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + + +namespace { + + class PyVarValueGeneric : public VarValueGeneric { + public: + using VarValueGeneric::VarValueGeneric; + + const char* TypeId() const override { PYBIND11_OVERLOAD_PURE(const char*, VarValueGeneric, TypeId,); } + void Reset() override { PYBIND11_OVERLOAD_PURE(void, VarValueGeneric, Reset,); } + VarMeta& Meta() override { PYBIND11_OVERLOAD_PURE( VarMeta&, VarValueGeneric, Meta,); } + }; +} + + + +void declareVarValueGeneric(py::module & m) { + + py::class_(m, "VarMeta") + .def(py::init<>()) + .def_readwrite("full_name", &VarMeta::full_name) + .def_readwrite("friendly", &VarMeta::friendly) + .def_readwrite("increment", &VarMeta::increment) + .def_readwrite("flags", &VarMeta::flags) + .def_readwrite("gui_changed", &VarMeta::gui_changed) + .def_readwrite("logscale", &VarMeta::logscale) + .def_readwrite("generic", &VarMeta::generic) + // double range[2]; + ; + + // abstract class + py::class_(m, "VarValueGeneric") + .def(py::init<>()) + .def("TypeId", &VarValueGeneric::TypeId) + .def("Reset", &VarValueGeneric::Reset) + .def("Meta", &VarValueGeneric::Meta) + ; + +} + +} \ No newline at end of file diff --git a/python/var/varvaluet.hpp b/python/var/varvaluet.hpp new file mode 100644 index 0000000..04bd274 --- /dev/null +++ b/python/var/varvaluet.hpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include + + +namespace py = pybind11; +using namespace pybind11::literals; + + +namespace pangolin { + + +namespace { + + // class PyVarValueT + + template + void templatedVarValueT(py::module & m, std::string const & suffix) { + py::class_, VarValueGeneric>(m, ("VarValueT" + suffix).c_str()); + } + +} + + +void declareVarValueT(py::module & m) { + + templatedVarValueT(m, "Bool"); + templatedVarValueT(m, "Int"); + templatedVarValueT(m, "Float"); + templatedVarValueT(m, "String"); + templatedVarValueT>(m, "Func"); + //templatedVarValueT(m, "Custom"); // T should define "operator>>(std::istream&, T&)" and + // "operator<<(std::istream&, T&)" operators +} + +} \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3a632c9 --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +from setuptools import setup +from setuptools.command.install import install +import site +import glob +import shutil + + +__library_file__ = './pangolin*.so' +__version__ = '0.0.1' + + + +class CopyLibFile(install): + """" + Directly copy library file to python's site-packages directory. + """ + + def run(self): + install_dirs = site.getsitepackages() + lib_file = glob.glob(__library_file__) + assert len(lib_file) == 1 and len(install_dirs) >= 1 + + print('copying {} -> {}'.format(lib_file[0], install_dirs[0])) + shutil.copy(lib_file[0], install_dirs[0]) + + + + +setup( + name='pangolin', + version=__version__, + description='python binding for lightweight 3D visualization library Pangolin.', + url='https://github.com/uoip/pangolin', + license='MIT', + cmdclass=dict( + install=CopyLibFile + ), + keywords='Pangolin, binding, OpenGL, 3D, visualization, Point Cloud', + long_description="""This is a Python binding for c++ library Pangolin + (https://github.com/stevenlovegrove/Pangolin). + + Pangolin is a lightweight portable rapid development library for managing + OpenGL display / interaction and abstracting video input. At its heart is + a simple OpenGl viewport manager which can help to modularise 3D visualisation + without adding to its complexity, and offers an advanced but intuitive 3D navigation + handler. Pangolin also provides a mechanism for manipulating program variables through + config files and ui integration, and has a flexible real-time plotter for visualising + graphical data.""" +) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..69f1040 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,682 @@ +####################################################### +## Library sources + +macro( append_glob list glob ) + file(GLOB files ${glob}) + set(${list} "${${list}};${files}") +endmacro() + +## Header only includes / core headers +set( INCDIR "../include/pangolin" ) +set( HEADERS + ${INCDIR}/pangolin.h + ${INCDIR}/platform.h +) +append_glob(HEADERS ${INCDIR}/utils/*.h*) +append_glob(HEADERS ${INCDIR}/image/*.h*) +append_glob(HEADERS ${INCDIR}/log/*.h*) + +### Store list of source files +append_glob(SOURCES utils/*.cpp) +append_glob(SOURCES image/*.cpp) +append_glob(SOURCES log/*.cpp) + + +### Store list of Video factory registery methods to call for init. +include(CreateMethodCallFile) +set( VIDEO_FACTORY_REG "" ) + +####################################################### +## User build options + +option(BUILD_PANGOLIN_GUI "Build support for Pangolin GUI" ON) +if(BUILD_PANGOLIN_GUI) + append_glob(HEADERS ${INCDIR}/gl/*.h*) + append_glob(HEADERS ${INCDIR}/display/*.h*) + append_glob(HEADERS ${INCDIR}/handler/*.h*) + + append_glob(SOURCES gl/*.cpp) + append_glob(SOURCES display/*.cpp) + append_glob(SOURCES handler/*.cpp) + + if(NOT HAVE_GLES OR HAVE_GLES_2) + append_glob(HEADERS ${INCDIR}/plot/*.h*) + append_glob(SOURCES plot/*.cpp) + endif() + +endif() + +option(BUILD_PANGOLIN_VARS "Build support for Pangolin Vars" ON) +if(BUILD_PANGOLIN_VARS) + append_glob(HEADERS ${INCDIR}/var/*.h*) + append_glob(SOURCES var/*.cpp) + + if(BUILD_PANGOLIN_GUI) + list(APPEND HEADERS ${INCDIR}/display/widgets/widgets.h ) + list(APPEND SOURCES display/widgets/widgets.cpp ) + endif() +endif() + +option(BUILD_PANGOLIN_VIDEO "Build support for Pangolin Video Utilities" ON) +if(BUILD_PANGOLIN_VIDEO) + # Generic video includes + append_glob(HEADERS ${INCDIR}/video/*.h*) + append_glob(SOURCES video/*.cpp) + + # Generic video drivers + list(APPEND HEADERS + ${INCDIR}/video/drivers/test.h + ${INCDIR}/video/drivers/images.h + ${INCDIR}/video/drivers/images_out.h + ${INCDIR}/video/drivers/split.h + ${INCDIR}/video/drivers/pvn.h + ${INCDIR}/video/drivers/pango.h + ${INCDIR}/video/drivers/pango_video_output.h + ${INCDIR}/video/drivers/debayer.h + ${INCDIR}/video/drivers/shift.h + ${INCDIR}/video/drivers/mirror.h + ${INCDIR}/video/drivers/unpack.h + ${INCDIR}/video/drivers/join.h + ${INCDIR}/video/drivers/merge.h + ${INCDIR}/video/drivers/thread.h + ) + list(APPEND SOURCES + video/drivers/test.cpp + video/drivers/images.cpp + video/drivers/images_out.cpp + video/drivers/split.cpp + video/drivers/pvn.cpp + video/drivers/pango.cpp + video/drivers/pango_video_output.cpp + video/drivers/debayer.cpp + video/drivers/shift.cpp + video/drivers/mirror.cpp + video/drivers/unpack.cpp + video/drivers/join.cpp + video/drivers/merge.cpp + video/drivers/json.cpp + video/drivers/thread.cpp + ) + + list(APPEND VIDEO_FACTORY_REG + RegisterTestVideoFactory + RegisterImagesVideoFactory + RegisterImagesVideoOutputFactory + RegisterSplitVideoFactory + RegisterPvnVideoFactory + RegisterPangoVideoFactory + RegisterPangoVideoOutputFactory + RegisterDebayerVideoFactory + RegisterShiftVideoFactory + RegisterMirrorVideoFactory + RegisterUnpackVideoFactory + RegisterJoinVideoFactory + RegisterMergeVideoFactory + RegisterJsonVideoFactory + RegisterThreadVideoFactory + ) + + if(LINUX) + list(APPEND HEADERS ${INCDIR}/video/drivers/shared_memory.h) + list(APPEND SOURCES video/drivers/shared_memory.cpp) + # Required for shared memory API using some versions of glibc + list(APPEND LINK_LIBS rt pthread) + endif() + +endif() + +if(BUILD_PANGOLIN_GUI AND BUILD_PANGOLIN_VARS AND BUILD_PANGOLIN_VIDEO ) + list(APPEND HEADERS ${INCDIR}/tools/video_viewer.h) + list(APPEND SOURCES tools/video_viewer.cpp) +endif() + +####################################################### +## Setup required includes / link info + +# Project headers trump everything (including any potentially installed Pangolin) +list(APPEND LIB_INC_DIR "${PROJECT_SOURCE_DIR}/include;${CMAKE_CURRENT_BINARY_DIR}/include" ) + +if(BUILD_PANGOLIN_GUI) + if( ANDROID ) + # Android specific display code + list(APPEND HEADERS ${INCDIR}/display/device/display_android.h ) + list(APPEND SOURCES display/device/display_android.cpp ) + + if(HAVE_GLES_2) + list(APPEND LINK_LIBS "-lEGL;-lGLESv2" ) + else() + list(APPEND LINK_LIBS "-lEGL;-lGLESv1_CM" ) + endif() + elseif( IOS ) + list(APPEND LINK_LIBS "-framework OpenGLES" ) + list(APPEND HEADERS "${INCDIR}/ios/PangolinAppDelegate.h" "${INCDIR}/ios/PangolinViewController.h" ) + list(APPEND SOURCES "ios/PangolinAppDelegate.mm" "ios/PangolinViewController.mm" ) + else() + find_package(OpenGL REQUIRED QUIET) + list(APPEND USER_INC "${OPENGL_INCLUDE_DIR}" ) + list(APPEND LINK_LIBS "${OPENGL_LIBRARIES}" ) + + if(NOT BUILD_EXTERN_GLEW) + find_package(GLEW REQUIRED QUIET) + endif() + if(GLEW_FOUND) + list(APPEND USER_INC "${GLEW_INCLUDE_DIR}" ) + list(APPEND LINK_LIBS "${GLEW_LIBRARY}" ) + set(HAVE_GLEW 1) + endif() + endif() + + if( HAVE_GLES_2 ) + # Add Pangolins backwards compat layer. + list(APPEND HEADERS ${INCDIR}/gl2engine.h ) + list(APPEND SOURCES gl2engine.cpp) + endif() +endif() + +####################################################### +## Find optional dependencies + +if(ANDROID) + # Fix issue with thread local storage on android. + add_definitions(-fno-data-sections) + list(APPEND LINK_LIBS android log) +elseif(IOS) + # Nothing specific in here yet. +else() + if(BUILD_PANGOLIN_GUI) + if(FORCE_GLUT) + find_package(FREEGLUT QUIET) + find_package(GLUT QUIET) + + if(_OSX_) + INCLUDE(CheckCXXSourceRuns) + set(CMAKE_REQUIRED_LIBRARIES ${GLUT_LIBRARY}) + CHECK_CXX_SOURCE_RUNS("#include \n int main () {return glutGetProcAddress(\"glutScrollFunc\") ? 0 : -1;};" HAVE_MODIFIED_OSXGLUT) + if(NOT HAVE_MODIFIED_OSXGLUT) + message(STATUS "Install modified osxglut for smooth scroll support / pinch / zoom.") + message(STATUS "(https://github.com/stevenlovegrove/osxglut)") + endif() + endif() + + # Prefer OSX_MODIFIED_GLUT > FREEGLUT > GLUT + if(FREEGLUT_FOUND AND NOT HAVE_MODIFIED_OSXGLUT) + set(HAVE_FREEGLUT 1) + list(APPEND USER_INC ${FREEGLUT_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${FREEGLUT_LIBRARY} ) + elseif(GLUT_FOUND) + list(APPEND USER_INC ${GLUT_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${GLUT_LIBRARY} ) + if(_OSX_) + set(HAVE_APPLE_OPENGL_FRAMEWORK 1) + if(NOT HAVE_MODIFIED_OSXGLUT) + message(STATUS "Using Apple Framework GLUT.") + message(STATUS "Install Freeglut or modified osxglut for scroll support.") + endif() + endif() + endif() + + if(FREEGLUT_FOUND OR GLUT_FOUND) + set(HAVE_GLUT 1) + list(APPEND HEADERS ${INCDIR}/display/device/display_glut.h ) + list(APPEND SOURCES display/device/display_glut.cpp ) + message(STATUS "Glut Found and Enabled") + endif() + elseif(_WIN_) + list(APPEND SOURCES display/device/display_win.cpp ) + elseif(_OSX_) + list(APPEND SOURCES display/device/display_osx.mm display/device/PangolinNSApplication.mm display/device/PangolinNSGLView.mm ) + list(APPEND LINK_LIBS "-framework Cocoa" ) + elseif(_LINUX_) + find_package(X11 REQUIRED) + list(APPEND USER_INC ${X11_INCLUDE_DIR}) + list(APPEND SOURCES display/device/display_x11.cpp ) + list(APPEND LINK_LIBS ${X11_LIBRARIES} ) + endif() + endif() +endif() + +if(UNIX) + append_glob(HEADERS ${INCDIR}/utils/posix/*.h*) + append_glob(SOURCES utils/posix/*.cpp) + if(_LINUX_) + # Required for shared memory API using some versions of glibc + list(APPEND LINK_LIBS rt pthread) + endif() +endif() + +option(BUILD_PANGOLIN_PYTHON "Build support for Pangolin Interactive Console" ON) +if(BUILD_PANGOLIN_PYTHON AND BUILD_PANGOLIN_GUI AND BUILD_PANGOLIN_VARS AND NOT _WIN_) + find_package(PythonLibs QUIET) + if(PYTHONLIBS_FOUND) + set(HAVE_PYTHON 1) + list(APPEND HEADERS + ${INCDIR}/console/ConsoleInterpreter.h + ${INCDIR}/console/ConsoleView.h + ${INCDIR}/python/PyInterpreter.h + ${INCDIR}/python/PyModulePangolin.h + ${INCDIR}/python/PyUniqueObj.h + ${INCDIR}/python/PyVar.h + ${INCDIR}/python/PyPangoIO.h + ) + list(APPEND SOURCES + console/ConsoleView.cpp + python/PyInterpreter.cpp + python/PyModulePangolin.cpp + ) + if(_GCC_) + set_source_files_properties(python/PyInterpreter.cpp PROPERTIES COMPILE_FLAGS "-Wno-missing-field-initializers") + set_source_files_properties(python/PyModulePangolin.cpp PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -Wno-missing-field-initializers") + endif() + list(APPEND INTERNAL_INC ${PYTHON_INCLUDE_DIR}) + list(APPEND LINK_LIBS ${PYTHON_LIBRARY}) + message(STATUS "Python Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_OCULUS "Build support for Oculus HUD" OFF) +if(BUILD_PANGOLIN_OCULUS) + find_package(Oculus QUIET) + if(Oculus_FOUND) + set(HAVE_OCULUS 1) + list(APPEND HEADERS ${INCDIR}/hud/oculus_hud.h ) + list(APPEND SOURCES hud/oculus_hud.cpp ) + list(APPEND USER_INC ${Oculus_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${Oculus_LIBRARIES} ) + message(STATUS "Oculus Rift Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_EIGEN "Build support for Eigen matrix types" ON) +if(BUILD_PANGOLIN_EIGEN) + find_package(Eigen QUIET) + if(EIGEN_FOUND) + set(HAVE_EIGEN 1) + list(APPEND USER_INC ${EIGEN_INCLUDE_DIR} ) + if(_CLANG_) + # Eigen causes many of these errors. Suppress. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-register") + endif() + message(STATUS "Eigen Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_TOON "Build support for TooN matrix types" ON) +if(BUILD_PANGOLIN_TOON) + find_package(TooN QUIET) + if(TooN_FOUND) + set(HAVE_TOON 1) + list(APPEND USER_INC ${TooN_INCLUDE_DIR} ) + message(STATUS "TooN Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_LIBDC1394 "Build support for libdc1394 video input" ON) +if(BUILD_PANGOLIN_LIBDC1394 AND BUILD_PANGOLIN_VIDEO) + find_package(DC1394 QUIET) + if(DC1394_FOUND) + set(HAVE_DC1394 1) + list(APPEND INTERNAL_INC ${DC1394_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${DC1394_LIBRARY} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/firewire.h ${INCDIR}/video/drivers/deinterlace.h ) + list(APPEND SOURCES video/drivers/firewire.cpp video/drivers/deinterlace.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterFirewireVideoFactory ) + message(STATUS "libdc1394 Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_V4L "Build support for V4L video input" ON) +if(BUILD_PANGOLIN_V4L AND BUILD_PANGOLIN_VIDEO AND _LINUX_) + set(HAVE_V4L 1) + list(APPEND HEADERS ${INCDIR}/video/drivers/v4l.h) + list(APPEND SOURCES video/drivers/v4l.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterV4lVideoFactory ) + message(STATUS "V4L Found and Enabled") +endif() + +option(BUILD_PANGOLIN_FFMPEG "Build support for ffmpeg video input" ON) +if(BUILD_PANGOLIN_FFMPEG AND BUILD_PANGOLIN_VIDEO) + find_package(FFMPEG QUIET) + if(FFMPEG_FOUND) + set(HAVE_FFMPEG 1) + list(APPEND INTERNAL_INC ${FFMPEG_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${FFMPEG_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/ffmpeg.h) + list(APPEND SOURCES video/drivers/ffmpeg.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterFfmpegVideoFactory ) + list(APPEND VIDEO_FACTORY_REG RegisterFfmpegVideoOutputFactory ) + + if(_GCC_) + # FFMPEG is a real pain for deprecating the API. + set_source_files_properties(video/drivers/ffmpeg.cpp PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations") + endif() + message(STATUS "ffmpeg Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_LIBREALSENSE "Build support for LibRealSense video input" ON) +if(BUILD_PANGOLIN_LIBREALSENSE AND BUILD_PANGOLIN_VIDEO) + find_package(LibRealSense QUIET) + if(LIBREALSENSE_FOUND) + set(HAVE_LIBREALSENSE 1) + list(APPEND INTERNAL_INC ${LIBREALSENSE_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${LIBREALSENSE_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/realsense.h ) + list(APPEND SOURCES video/drivers/realsense.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterRealSenseVideoFactory ) + message(STATUS "LibRealSense Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_OPENNI "Build support for OpenNI video input" ON) +if(BUILD_PANGOLIN_OPENNI AND BUILD_PANGOLIN_VIDEO) + find_package(OpenNI QUIET) + if(OPENNI_FOUND) + set(HAVE_OPENNI 1) + if(_LINUX_) + add_definitions(-Dlinux=1) + endif() + list(APPEND INTERNAL_INC ${OPENNI_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${OPENNI_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/openni.h ) + list(APPEND SOURCES video/drivers/openni.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterOpenNiVideoFactory ) + message(STATUS "OpenNI Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_OPENNI2 "Build support for OpenNI2 video input" ON) +if(BUILD_PANGOLIN_OPENNI2 AND BUILD_PANGOLIN_VIDEO) + find_package(OpenNI2 QUIET) + if(OPENNI2_FOUND) + set(HAVE_OPENNI2 1) + if(_LINUX_) + add_definitions(-Dlinux=1) + endif() + list(APPEND INTERNAL_INC ${OPENNI2_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${OPENNI2_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/openni2.h ) + list(APPEND SOURCES video/drivers/openni2.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterOpenNi2VideoFactory ) + message(STATUS "OpenNI2 Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_LIBUVC "Build support for libuvc video input" ON) +if(BUILD_PANGOLIN_LIBUVC AND BUILD_PANGOLIN_VIDEO) + find_package(uvc QUIET) + if(uvc_FOUND) + set(HAVE_UVC 1) + list(APPEND INTERNAL_INC ${uvc_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${uvc_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/uvc.h ) + list(APPEND SOURCES video/drivers/uvc.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterUvcVideoFactory ) + if(_WIN_) + find_package(pthread REQUIRED QUIET) + list(APPEND LINK_LIBS ${pthread_LIBRARIES} ) + + find_package(libusb1 REQUIRED QUIET) + list(APPEND LINK_LIBS ${libusb1_LIBRARIES} ) + endif() + message(STATUS "libuvc Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_UVC_MEDIAFOUNDATION "Build support for MediaFoundation UVC input" ON) +if (BUILD_PANGOLIN_UVC_MEDIAFOUNDATION AND BUILD_PANGOLIN_VIDEO) + find_package(MediaFoundation QUIET) + if (MediaFoundation_FOUND) + list(APPEND LINK_LIBS ${MediaFoundation_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/uvc_mediafoundation.h ) + list(APPEND SOURCES video/drivers/uvc_mediafoundation.cpp ) + list(APPEND VIDEO_FACTORY_REG RegisterUvcMediaFoundationVideoFactory ) + message(STATUS "MediaFoundation Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_DEPTHSENSE "Build support for DepthSense video input" ON) +if(BUILD_PANGOLIN_DEPTHSENSE AND BUILD_PANGOLIN_VIDEO) + find_package(DepthSense QUIET) + if(DepthSense_FOUND) + set(HAVE_DEPTHSENSE 1) + list(APPEND INTERNAL_INC ${DepthSense_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${DepthSense_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/depthsense.h ) + list(APPEND SOURCES video/drivers/depthsense.cpp) + list(APPEND VIDEO_FACTORY_REG RegisterDepthSenseVideoFactory ) + message(STATUS "DepthSense Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_TELICAM "Build support for TeliCam video input" ON) +if(BUILD_PANGOLIN_TELICAM AND BUILD_PANGOLIN_VIDEO) + find_package(TeliCam QUIET) + if(TeliCam_FOUND) + set(HAVE_TELICAM 1) + list(APPEND INTERNAL_INC ${TeliCam_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${TeliCam_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/teli.h ) + list(APPEND SOURCES video/drivers/teli.cpp ) + list(APPEND VIDEO_FACTORY_REG RegisterTeliVideoFactory ) + + message(STATUS "TeliCam Found and Enabled" ) + endif() +endif() + +option(BUILD_PANGOLIN_PLEORA "Build support for Pleora video input" ON) +if(BUILD_PANGOLIN_PLEORA AND BUILD_PANGOLIN_VIDEO) + find_package(Pleora QUIET) + if(Pleora_FOUND) + set(HAVE_PLEORA 1) + list(APPEND INTERNAL_INC ${Pleora_INCLUDE_DIRS} ) + list(APPEND LINK_LIBS ${Pleora_LIBRARIES} ) + list(APPEND HEADERS ${INCDIR}/video/drivers/pleora.h ) + list(APPEND SOURCES video/drivers/pleora.cpp ) + list(APPEND VIDEO_FACTORY_REG RegisterPleoraVideoFactory ) + + if(_GCC_) + # Suppress warnings generated from Pleora SDK. + set_source_files_properties(video/drivers/pleora.cpp PROPERTIES COMPILE_FLAGS -Wno-unknown-pragmas) + set_source_files_properties(video/video.cpp PROPERTIES COMPILE_FLAGS -Wno-unknown-pragmas) + endif() + message(STATUS "Pleora Found and Enabled" ) + endif() +endif() + +option(BUILD_PANGOLIN_LIBPNG "Build support for libpng image input" ON) +if(BUILD_PANGOLIN_LIBPNG) + if(NOT BUILD_EXTERN_LIBPNG) + find_package(PNG QUIET) + endif() + if(PNG_FOUND) + # (ZLIB is also found by FindPNG.cmake as its dependency) + set(HAVE_PNG 1) + list(APPEND INTERNAL_INC ${PNG_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${PNG_LIBRARY} ${ZLIB_LIBRARY} ) + message(STATUS "libpng Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_LIBJPEG "Build support for libjpeg image input" ON) +if(BUILD_PANGOLIN_LIBJPEG) + if(NOT BUILD_EXTERN_LIBJPEG) + find_package(JPEG QUIET) + endif() + if(JPEG_FOUND) + set(HAVE_JPEG 1) + list(APPEND INTERNAL_INC ${JPEG_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${JPEG_LIBRARY} ) + message(STATUS "libjpeg Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_LIBTIFF "Build support for libtiff image input" ON) +if(BUILD_PANGOLIN_LIBTIFF) + find_package(TIFF QUIET) + if(TIFF_FOUND) + set(HAVE_TIFF 1) + list(APPEND INTERNAL_INC ${TIFF_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${TIFF_LIBRARY} ) + message(STATUS "libtiff Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_LIBOPENEXR "Build support for libopenexr image input" ON) +if(BUILD_PANGOLIN_LIBOPENEXR) + find_package(OpenEXR QUIET) + if(OpenEXR_FOUND) + set(HAVE_OPENEXR 1) + list(APPEND INTERNAL_INC ${OpenEXR_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${OpenEXR_LIBRARY} ) + message(STATUS "libopenexr Found and Enabled") + endif() +endif() + +option(BUILD_PANGOLIN_ZSTD "Build support for libzstd compression" ON) +if(BUILD_PANGOLIN_ZSTD) + find_package(zstd QUIET) + if(zstd_FOUND) + set(HAVE_ZSTD 1) + list(APPEND INTERNAL_INC ${zstd_INCLUDE_DIR} ) + list(APPEND LINK_LIBS ${zstd_LIBRARY} ) + message(STATUS "libzstd Found and Enabled") + endif() +endif() + +####################################################### +## Embed resource binary files + +include(EmbedBinaryFiles) + +if(BUILD_PANGOLIN_GUI) + embed_binary_files( "_embed_/fonts/*.ttf" "${CMAKE_CURRENT_BINARY_DIR}/fonts.c" ) + list(APPEND SOURCES "${CMAKE_CURRENT_BINARY_DIR}/fonts.c" ) +endif() + +####################################################### +## Add Libraries / Include Directories / Link directories + +include_directories( ${LIB_INC_DIR} ) +include_directories( ${USER_INC} ) +include_directories( ${INTERNAL_INC} ) + +add_library(${LIBRARY_NAME} STATIC ${SOURCES} ${HEADERS}) # modified for python binding (add "STATIC") +target_link_libraries(${LIBRARY_NAME} ${LINK_LIBS}) + +## Generate symbol export helper header on MSVC +IF(MSVC) + string(TOUPPER ${LIBRARY_NAME} LIBRARY_NAME_CAPS) + include(GenerateExportHeader) + GENERATE_EXPORT_HEADER( ${LIBRARY_NAME} + BASE_NAME ${LIBRARY_NAME_CAPS} + EXPORT_MACRO_NAME ${LIBRARY_NAME_CAPS}_EXPORT + EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/include/pangolin/${LIBRARY_NAME}_export.h" # + STATIC_DEFINE ${LIBRARY_NAME_CAPS}_BUILT_AS_STATIC + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/pangolin/${LIBRARY_NAME}_export.h" # + DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${LIBRARY_NAME} + ) +ENDIF() + +## Set Special Compiler flags +if(MSVC) + set(CMAKE_CXX_FLAGS "/EHs ${CMAKE_CXX_FLAGS}") +elseif(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "-Wall -Wno-error=deprecated-declarations ${CMAKE_CXX_FLAGS}") +endif() + +####################################################### +## Create config.h file for inclusion in library + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" + #"${CMAKE_CURRENT_BINARY_DIR}/include/pangolin/config.h" # + "${PROJECT_SOURCE_DIR}/include/pangolin/config.h" +) + +####################################################### +## Create video_drivers.h file for inclusion in library +## This loads video driver factories based on cmake configuration + +CreateMethodCallFile( + "${CMAKE_CURRENT_BINARY_DIR}/include/pangolin/video_drivers.h" # + "pangolin" "LoadBuiltInVideoDrivers" "${VIDEO_FACTORY_REG}" +) + +####################################################### +## Generate Doxygen documentation target (make doc) +find_package(Doxygen) +if(DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../doc ) + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../doc + COMMENT "Generating API documentation with Doxygen" VERBATIM + ) +endif() + +####################################################### + +# This relative path allows installed files to be relocatable. +set( CMAKECONFIG_INSTALL_DIR lib/cmake/${PROJECT_NAME} ) +file( RELATIVE_PATH REL_INCLUDE_DIR + "${CMAKE_INSTALL_PREFIX}/${CMAKECONFIG_INSTALL_DIR}" + "${CMAKE_INSTALL_PREFIX}/include" +) + +# Export library for easy inclusion from other cmake projects. APPEND allows +# call to function even as subdirectory of larger project. +FILE(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") +export( TARGETS ${LIBRARY_NAME} + APPEND FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" ) + +# Version information +configure_file(${PROJECT_NAME}ConfigVersion.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" @ONLY) + +# Build tree config +set( EXPORT_LIB_INC_DIR ${LIB_INC_DIR} ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY IMMEDIATE ) + +# Install tree config +set( EXPORT_LIB_INC_DIR "\${PROJECT_CMAKE_DIR}/${REL_INCLUDE_DIR}" ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake @ONLY ) + +# Add package to CMake package registery for use from the build tree +option( EXPORT_${PROJECT_NAME} + "Should the ${PROJECT_NAME} package be exported for use by other software" ON ) + +if( EXPORT_${PROJECT_NAME} ) + export( PACKAGE ${PROJECT_NAME} ) +endif() + +####################################################### +## Install headers / targets + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/${LIBRARY_NAME}/config.h" # + DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${LIBRARY_NAME} +) +install(DIRECTORY ${INCDIR} + DESTINATION ${CMAKE_INSTALL_PREFIX}/include +) +install(TARGETS ${LIBRARY_NAME} + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib +) + +####################################################### +## Install CMake config + +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKECONFIG_INSTALL_DIR} +) +install( + EXPORT ${PROJECT_NAME}Targets DESTINATION ${CMAKECONFIG_INSTALL_DIR} +) diff --git a/src/Doxyfile.in b/src/Doxyfile.in new file mode 100644 index 0000000..8146355 --- /dev/null +++ b/src/Doxyfile.in @@ -0,0 +1,2281 @@ +# Doxyfile 1.8.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Pangolin" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Pangolin is a lightweight rapid development library for managing OpenGL display / interaction and video input" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- +# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, +# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../include +INPUT += @CMAKE_CURRENT_SOURCE_DIR@/../README.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.h *.md + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = *internal* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = android_app android_poll_source PangolinAppDelegate + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /